戴銘:我寫技術(shù)文章的一點心得
## 前言
非常感謝大家抽出生命中寶貴的一段時間來聽我接下來的一大段關(guān)于寫文章那些事的嘮叨。寫文章的好處看看[《覺醒年代》](https://movie.douban.com/subject/30228394/)就知道了。
這篇文章我不會寫一些常說的技巧,比如文章的內(nèi)容前后要有邏輯關(guān)系,內(nèi)容之間有關(guān)聯(lián)。所講知識前后的層次要平,不要在某個部分挖掘過深。寫作過程中牢牢抓住要表達(dá)的內(nèi)容,不要過于偏離主題。類似這樣的技巧不會說。都說一流的人討論思想、普通人討論事情、三流的人討論人,那么為了提高文章高度,除了說些事情,我還打算加些思想的內(nèi)容。
大家都習(xí)慣去閱讀他人人生體驗來體驗不一樣的人生,這樣的方式和評頭論足一樣簡單舒服,輕松爽快。而主動去對自己思想進行研究和開發(fā),通過寫作輸出自己獨特的經(jīng)歷和思考卻是困難且難受的,但這樣從0到1和從0到10的創(chuàng)造過程獲得的樂趣卻是前者的百倍甚至更多,這背后所遵循的原則是怎樣的呢?
每個心得都會基于某些原則,以至于思路不會散架,而所有的原則都無法違背物熱力學(xué)第二定律,也就是熵增定律(強烈建議先看知乎[這篇](https://zhuanlan.zhihu.com/p/72896309)介紹)。就連進化論都是遵循熵增定律。
對于寫作的心得我提煉出獨特性、真實感、故事性和新意四個點,其中的獨特性和新意都是逆著熵增的過程,其過程是非常難受煎熬的,可能做了大量付出也沒結(jié)果,因為逆熵增是非線性的,無法預(yù)測的,只有在偶然的機會才會有開掛的感覺。對于真實感和故事性屬于線性積累,和閱讀別人的文章一樣,是很容易做到的事情,都是熵增過程,有必要,容易看到結(jié)果,但會有內(nèi)耗,如果沒有更多獨特性的經(jīng)歷來逆熵,可輸出的內(nèi)容會越來越混亂,落后,漸漸無用。因此獨特性、真實感、故事性和新意這四個點之間需要平衡與演進,才能夠保持進化的活力。
接下來我就詳細(xì)展開來跟你說說獨特性、真實感、故事性和新意四個點,通過我以前寫的一些文章來詳細(xì)說明。如果你還不知道如何下筆,我還會介紹一個容易著手去做記錄和分享輸出的方法步驟,最后會從頭到尾舉個例子按照前面介紹的步驟演示如何寫完一篇技術(shù)文章。特別是那些逆熵的過程,讓你能夠多些體感。
## 四個點
先分別介紹下這四個點。
### 獨特性
獨特性也就是自己的經(jīng)歷和體驗,這個是獨一無二的,文章的內(nèi)容如果有更多的個人經(jīng)歷,作為讀者也就能夠體驗到更多的生活。
行萬里路,多去做不同事情,多嘗試不同方法,也就能夠獲取到更多的經(jīng)歷。獨特性是內(nèi)容中最重要的部分,如果這四點重要性共分十層的話,我認(rèn)為獨特性就可以占到六層。
### 真實感
記得一個美劇編劇分享過他寫編劇的經(jīng)驗,其中提到要寫的題材,他至少會花上一年以上的時間去收集和整理相關(guān)資料。他認(rèn)為只有把題材相關(guān)細(xì)節(jié)都吃透了,編排到劇里,觀眾才會感覺更真實,代入感才會強,身同感受才會有共鳴。如果觀眾感到假,感覺不到用心,那他怎么會去了解你想表達(dá)的內(nèi)容呢。
因此真實感是表達(dá)內(nèi)容的基礎(chǔ),而且是最費時的。相較于獨特的個人經(jīng)歷,真實感是需要花費大量時間去調(diào)研作者以前不了解的東西。而這個過程也是了解別人經(jīng)歷的過程,可以學(xué)習(xí)到很多以前不知道的事情。
真實感是對獨特性的擴充,是豐富和挖掘沉淀獨特性的,可以占到兩層,后面的故事性和新意各占一層。
### 故事性
我很喜歡金庸的小說,特別是射雕英雄傳、神雕俠侶和倚天屠龍記這三部,起初對金庸其它小說興趣不大,也可能拍的電視劇不是我的菜。后來抱著愛屋及烏的態(tài)度,我嘗試著打開笑傲江湖小說的開頭開始讀,一下子就被吸引進去了。不得不佩服金庸寫故事的能力,太強大了。故事一開始制造了一個令人無法解釋的案件,你會非常好奇的一直看下去,很想知道到底發(fā)生了什么。小說都進行了很長的部分令狐沖才出現(xiàn),主角出現(xiàn)前還能吸引你看下去,可見金庸講故事能力有多厲害。
自從迷上金庸的小說后,我也會試著寫些小故事,同樣我會注重把一些自身獨特的經(jīng)歷穿插到寫的小故事里,這些故事我發(fā)到了我的博客上,有[白龍班](https://ming1016.github.io/2019/06/19/white-dragon-class/)、[十中](https://ming1016.github.io/2016/04/04/tenth-middle-school/)、[白羋](https://ming1016.github.io/2018/01/04/baimi/)和[花野](https://ming1016.github.io/2018/01/04/huaye/)
故事性是一種技巧,是線性的,很容易通過大量積累掌握好,最終是好是差還是強依賴于獨特性和真實感。
### 新意
新意這個點非常關(guān)鍵和重要,也是演進的重要因素,你仔細(xì)想想看,很多深度高的文章其實底層知識都是差不多的,能夠真正有翻天覆地突進的技術(shù)演進不會很頻繁,而且這些技術(shù)往往都在硬件廠商和實驗室中產(chǎn)生出來。對于已有底層知識的輸出區(qū)別只是應(yīng)用場景和組合運用技巧上有區(qū)別,精彩的發(fā)掘和效果奇佳的收益也能夠獲得掌聲。因此技術(shù)知識和經(jīng)驗輸出的形式也非常重要和關(guān)鍵,如果沒有新意,大家勢必會對那些知識感覺到疲倦,沒人看,寫作也就沒有了動力。
關(guān)于新意可以看到淘系公眾號最近使用了視頻的方式來講他們的技術(shù),看起來就很有趣。這方面只有你想不到,沒有你做不到,打開腦殼,充分發(fā)揮想象吧。
新意之所以只占一層,因為新意獲得成功的概率較低,是非線性的,因此需要不斷去嘗試不同的方式。需要依賴天時地利人和以及前三個點都做的足夠好了,新意才會取得非常好的效果。
## 我以前的文章
前面講了四個我覺得寫技術(shù)文章最重要的點,只是說了下理論上的邏輯,體感還不夠強,下面我結(jié)合我以前寫的文章我們一起來看看這些文章背后那些獨特的經(jīng)歷吧。
### A站 的 Swift 實踐
《A站 的 Swift 實踐》[上](https://mp.weixin.qq.com/s/rUZ8RwhWf4DWAa5YHHynsQ)、[下](https://mp.weixin.qq.com/s/EIPHLdxBMb5MiRDDfxzJtA),當(dāng)時發(fā)這篇文章時,關(guān)于 Swift 實踐的文章也有很多,都是各廠自身實踐經(jīng)驗,對于獨特性這個點,開始想著把 A 站做過的事情說清楚就可以了,但是很多的經(jīng)驗和做的事情和其他廠做的差不多,這樣寫出來會沒有什么特別的,所以需要著重說下做的和別人不一樣的事情。A站比較有特色的是文章里提到的A站自研的聲明式 UI Ysera 框架,這個是別人沒有的,并且由于 Ysera 框架帶來了和 SwiftUI 類似的優(yōu)雅簡潔,提升了整體開發(fā)的效率和體驗。由于 A 站很早就進入了 Swift 開發(fā)模式,并且已有將近一半業(yè)務(wù)使用了 Swift 開發(fā),所以 A 站相較其它廠走得更快些,對于 Swift 新特性運用的也更廣,比如對于 Property Wrapper 的廣泛應(yīng)用,使得代碼復(fù)雜度驟然降低。走得更遠(yuǎn)還表現(xiàn)在 Module 化上,A 站大半 Pod 都完成了 Module 化,這方面的經(jīng)驗也很多。
有了獨特性,為了能夠讓閱讀的人更有體感,需要對一些技術(shù)點進行進一步的描述,使得文章一方面能夠讓自己得到知識的總結(jié)沉淀,還能夠?qū)λ擞杏?。這篇文章主要是在混編的內(nèi)在原理上進行了剖析,這比只描述解決混編問題過程要更加通用些,同時也能起到授人以漁的目的。但掌握原理就需要去學(xué)習(xí)和提煉相關(guān)知識,所下的功夫也更大些。另外采用 Swift 的話,還有個繞不過去的擔(dān)憂點需要面對,這就是 Swift 的動態(tài)性,Swift 這方面由于在 Swift 核心團隊工作優(yōu)先級中較低,相較于 OC 要弱和不成熟很多。所以關(guān)于動態(tài)化就要說清楚,說的全面點,最好是能夠自己進行實驗去驗證,這個過程會往往枯燥漫長,需要較大的熱情才能夠完成。
關(guān)于故事性,故事性往往是用來引入讀進去的一種辦法,A 站的 Swift 實踐這篇文章的開頭通過講述使用 Swift 的必要性、A 站為之付出的努力和收獲、Swift 語言的演進的過程的方式盡量避開具體技術(shù)描述,而是使用通俗易懂的描述讓讀的人可以被輕松帶入到文章中來。
### 深入剖析Auto Layout,分析iOS各版本新增特性
[《深入剖析Auto Layout,分析iOS各版本新增特性》](https://ming1016.github.io/2015/11/03/deeply-analyse-autolayout/)。寫這個文章也是有著一段不同尋常的經(jīng)歷。那時剛到公司,所有布局都還是使用的 frame 方式,而 Auto Layout 蘋果公司才推出不久,在另一位跟我一樣新進公司熟悉 Auto Layout 同事的慫恿下,我打算在改版需求中使用 Auto Layout 來替換原有布局方式。但在需求開發(fā)剛開始時,那位熟悉 Auto Layout 的新同事突然離職了,我感覺失去了援手,但是我認(rèn)可了這個技術(shù),還是堅持使用它。期間碰到的苦難無數(shù),布局思路帶來了很多開發(fā)方式的改變,還有動畫的結(jié)合會出現(xiàn)的各種效果不一致,其間公司老員工還不斷勸我還是走老路比較穩(wěn)妥。改版完后大部分主流程,包括首頁發(fā)單、等待頁、接單進行頁都被改造成 Auto Layout。
更困難的事情還在后面呢,測試期間發(fā)現(xiàn)在 iOS6 上會出現(xiàn)各種崩潰、頁面布局混亂、動畫效果不一致等問題,我的 Bug 始終保持在 Bug 列表前十頁。改 Bug 那些天,晚上調(diào)的眼發(fā)疼,深夜想的難入眠。線下 Bug 改完,上線后才是噩夢的開始,當(dāng)時我們 App 的 iOS6 用戶依舊很多,于是很多偶現(xiàn)崩潰被放大了,我的崩潰問題一直排在 Top1,雖然我很快找到了改好的辦法,但是對于這幾個偶現(xiàn)的問題還需要一個可靠可信服的解釋,這樣后面才能夠讓大家放心使用 Auto Layout。還記得當(dāng)時周末坐在得實大廈窗戶邊的工位上,在查完和試完所有資料后依然無果時的無力感。本想著改回以前的 frame 布局算了,后又覺不甘。下幾個周末跑到各大圖書館查看所有涉有 Auto Layout 的書,也是那個時候了解到了 VFL 語言。皇天不負(fù)有心人,WWDC 開始了,其中有個 Session 叫 Mysteries of Auto Layout,分為[上](https://developer.apple.com/videos/play/wwdc2015-218/)、[下](https://developer.apple.com/videos/play/wwdc2015-219/)兩個部分,把 Auto Layout 的原理講得非常透徹了,至此,透過原理我也找到了問題的根因,并把他們記錄在了文章中。這部分內(nèi)容我還在一個沙龍做了分享,下面是當(dāng)時分享的 Auto Layout 的原理部分的內(nèi)容:

完整幻燈片參看[這里](https://ming1016.github.io/2021/07/13/deeply-analyse-autolayout-slides/)。
這些經(jīng)驗的總結(jié)在當(dāng)時是非常新的,因為官方也是剛公布出其內(nèi)部的原理,沒有人能夠更早的知道這些信息,估計也很少有人會考究這么多。有了這些由于一直堅持下來去找根因的經(jīng)歷才使得文章有了獨特性。
當(dāng)然,深入剖析 Auto Layout 這篇文章也加了 Auto Layout 的歷史、生命周期、VFL 語言的介紹用來豐富內(nèi)容的廣度,以提升真實感,但你會發(fā)現(xiàn)獨特性在這里顯得尤為重要。
另外,在查找崩潰問題根因時,沒有放棄,一直堅持的去找答案的過程也讓我難忘。經(jīng)常會聽說到要去找自己熱愛的事情,遵循自己所想。而實際上是那件熱愛的事情是你愿意花很久甚至很多年需要克服痛苦,還能夠繼續(xù)忍耐,能忍他人所不能忍,贏過他人不是靠的熱愛和能力,而是在萬般艱難,別人都放棄而你堅持下來才贏的。巴菲特21年資產(chǎn)5000億美元,其中4997億美元是50歲之后賺到的,如果49歲那年他就不繼續(xù)做了,那么他就不會有今年這樣巨大的財富,就不會顯示巨大的復(fù)利效應(yīng)。
后來我還發(fā)現(xiàn),不斷堅持的一個竅門就是去慶祝大目標(biāo)方向上的每個小小的成功,把這個小小的成功當(dāng)成最后的成就那樣去慶祝。
### 制作一個類似蘋果VFL(Visual Format Language)的格式化語言來描述類似UIStackView那種布局思路,并解析生成頁面
[這篇文章](https://ming1016.github.io/2016/07/21/assembleview/) 誕生的原因是我寫了一個視圖布局的庫 AssembleView,通過這篇文章做了一個記錄。這篇的獨特性在于文章背后我特殊的經(jīng)歷。首先寫 AssembleView 的起因在于之前大半年我使用自動布局寫了大量的頁面和一些動畫,雖然有比系統(tǒng)更加簡化的 Masonry 庫可以使用,但是對于很早以前寫過 H5 頁面的我來說無論是從布局思路還有編寫體驗上,Masonry 依舊差的很遠(yuǎn)。蘋果為自動布局發(fā)明的簡潔 VFL 語言卻沒能用在更加先進的 UIStackView 布局思路上,于是在一次中午吃飯散步的過程中,我突然有了把 VFL 語言和 UIStackView 布局結(jié)合起來的想法,同時還想好了名字,叫做 AssembleView,也就是組裝的視圖的意思,心動不如行動,在接下來的一個需求周期中,我就著手一邊開發(fā) AssembleView 一邊開發(fā)需求。每個需求只有一周的開發(fā)時間,當(dāng)時需求只是更新評價的幾個小頁面部件,但為了將 AssembleView 運用進來,我把整個評價頁面和功能進行了重寫,包括標(biāo)簽云等復(fù)雜布局采用新庫的重寫。而這樣的工作量僅在一周內(nèi)完成了。
短時間完成 AssembleView 并應(yīng)用到產(chǎn)品中,得益于 Deadline 的限制,設(shè)置時間節(jié)點,沒有時間節(jié)點的目標(biāo)那就是夢想,有了時間節(jié)點會讓你保持一段時間專注,在限制的時間里,你沒法去把事情做到方方面面都好,因此才會激發(fā)你,讓你發(fā)揮自身的獨特性,和別的不同,其實這種獨特會讓這件事情完成的更有價值。不要試著做最好的,而是力求做與眾不同的。與眾不同意味著創(chuàng)新,畫草圖和下筆寫稿子都是創(chuàng)造的方式,這些過程不要去做雕琢、檢查、取舍、反思這樣的事情,而是釋放自己的本能,去自由的發(fā)揮自己的積累和沉淀。藝術(shù)總是來自不完美,始于雜亂。
有了這樣非同尋常的經(jīng)歷,使得這篇文章本身獨特性的意義更大了。記錄并分享,能夠獲得做著同樣事情人的共鳴。
AssembleView 本身就是全新,因此從頭到尾都是新意。
當(dāng)時寫這個庫也是為了能夠提高完成需求和維護需求的時間,有了精力才能夠做更有趣有意義的事情嘛。五年后,蘋果終于將 VFL 這種 DSL 語言運用 Swift 強大的 ResultBuilder 和不透明類型等特性進行了更好地完善,配合 Property Wrapper 和 Combine 還無縫銜接了先進的數(shù)據(jù)流架構(gòu),推出了 SwiftUI。
### 深入剖析 JavaScriptCore
[《深入剖析 JavaScriptCore》](https://ming1016.github.io/2018/04/21/deeply-analyse-javascriptcore/) 這篇文章要說獨特性,那就是對 JavaScript 語言的好奇心。我很早就開始使用 JavaScript 來開發(fā)網(wǎng)站,工作和個人網(wǎng)站的前端都是依賴于這門語言,其實知情人都知道,選擇 JavaScript 也是沒有選擇的選擇。年輕時只顧著使用技術(shù)去做東西,也做了自己覺得非常有趣的程序,滿足感十足,現(xiàn)在轉(zhuǎn)向?qū)ζ浔澈蟮臋C制技術(shù)好奇和感興趣了。還有一個迫使自己去了解 JavaScript 引擎的原因是工作中做動態(tài)頁面時需要用到對業(yè)務(wù)邏輯的解釋執(zhí)行處理。為了避免使用中出了問題會一臉懵,深入了解它顯得很有必要。
光有想法是沒有一點用的,JavaScriptCore 其實非常的龐大且復(fù)雜,當(dāng)時能找到的大部分資料都是 Bridge 和 RN 的運用,好在開源了,了解內(nèi)部的話還可以拉代碼來看。但是直接埋進去看代碼,代碼量比較大,很容易 miss 掉其精妙之處。好在發(fā)現(xiàn)了 JavaScriptCore 項目核心開發(fā)者 Filip Pizlo,通過他的[個人網(wǎng)站](http://www.filpizlo.com/)找到了大量 JavaScriptCore 的一手資料,沒日沒夜的啃內(nèi)容,同時還試著動手去實現(xiàn)一些技術(shù)細(xì)節(jié),最終了解和學(xué)習(xí)了很多解釋器、虛機相關(guān)知識。獲取一樣?xùn)|西帶來的滿足感是沒有獲取經(jīng)驗帶來的滿足感更深刻。我把學(xué)到的這些經(jīng)驗都記錄在了這篇文章中,這使得文章的獨特性更加深刻,真實感達(dá)到了滿棚。
### 深入剖析 JavaScript 編譯器/解釋器引擎 QuickJS - 多了解些 JavaScript 語言
對于 JavaScript 引擎,我先前就看了 JavaScriptCore,為啥還要再去看 QuickJS 這個輕量的 JavaScript 引擎呢。寫[這篇文章](https://ming1016.github.io/2021/02/21/deeply-analyse-quickjs/)動力主要還是對QuickJS如何使用精簡高效的代碼實現(xiàn)了那么復(fù)雜功能,還有極高的性能。QuickJS 基本是從頭看到尾,一點一點的分析,整個過程也都記錄了下來。但是我覺得記錄源碼的分析還不夠,雖然這些分析使得文章的真實感很高,前提是讀的人也會埋進代碼里。為了提高文章的獨特性和故事性,我在文章開頭加入了一些 JavaScript 的一些背景內(nèi)容,還有些當(dāng)年使用前端技術(shù)的體會和經(jīng)驗。
只看代碼不去修改和調(diào)試,往往會很枯燥,我在分析代碼前,也寫了些和 QuickJS 工程配置 makefile 相關(guān)的內(nèi)容,并以 QuickJS 本身的 makefile 的用法進行舉例說明。另外還手把手說明了怎么用 Xcode 來編譯安裝調(diào)試 QuickJS 代碼,這些都是比較獨特的內(nèi)容。QuickJS 的核心代碼基本都寫在一個文件里,閱讀分析時需要非常的專注,如果不專注,可能這篇文章也就沒法寫完。如果你花大量時間在家玩游戲、看電視劇和刷短視頻,而不工作,那么就會有危機感和負(fù)罪感。但是如果會去工作,但是期間總會找著間隙去做其他事情,刷刷微博,看看朋友圈,瞅瞅新聞什么的,那就不會有負(fù)罪感,因為你會覺得你還是在工作著呢。沒全力去工作,而在假裝工作著,可比完全不工作的危害更大。一心一意的長時間去做工作外的事情,反而能夠開眼界擴視野,從而反哺工作,工作的更好更開心。新時代就會有新機會,同時也會有新的要求,比如知識的獲取從單一感官方式變成了動態(tài)的,多感官的方式。以前只是文字和圖片的書和博客,新時代就是視頻、直播和播客,新時代你有更多方式出現(xiàn)在大家面前,出現(xiàn)的更多就代表了成功。未來會有更多感官方式,而且更加的智能。獲取知識的門檻低了,人群也就更廣了,也可以理解為新的機會更大了,保持專注不設(shè)限去感受新時代,對自己不斷做出新的要求,就會有新的機會。
### 深入剖析 iOS 編譯 Clang / LLVM
寫[這篇文章](https://ming1016.github.io/2017/03/01/deeply-analyse-llvm/)的原因主要來自在公司做的 App 安裝包體積瘦身的事情,經(jīng)過各種工具使用和分析后,總是找不到突破口。需求還在不斷疊加,也沒有好的思路。當(dāng)你遇到困難時,做不成的人才會告訴你你也做不成,如果你真的想做成,有這個理想就要自己去守護他。遇到困難離成功才會更接近。那些困難來自于有限的資源,比如沒人、沒錢等,但是正是由于這些資源的限制,才會迫使你去創(chuàng)新,你會通過自己的熱情還有毅力來尋找獨特更有效的方法,所以由于有限資源帶來的困難才會讓你去突破、思變和進取。
就在百般無奈,各種資源條件受限的情況下。我想著看能不能把需要繁瑣手動檢查的動作試著寫成程序自動完成。于是我用一個周末開發(fā)了查找無用方法工具,能夠自動查找出工程中沒有用到的方法,也兼顧了我們工程的一些運行時調(diào)用的方法檢查。這樣重復(fù)繁重的檢查工作就變得輕松了很多。
工具開發(fā)完后,我發(fā)現(xiàn)這工具的實現(xiàn)并無相關(guān)成熟理論來進行支持,以后怎么完善和優(yōu)化這個工具也沒有一點思路。為此我還苦惱了蠻久的。
經(jīng)過一位同事提醒,說大學(xué)有門編譯原理的課里面就有講怎么分析代碼的,于是我就開始針對性的翻閱相關(guān)資料。于是乎,我發(fā)現(xiàn)了一片藍(lán)海,這里面涉及到的技術(shù)不光是分析代碼,還有很多以前不了解的程序怎么跑起來的細(xì)節(jié),這里的知識就像可以無限遞歸的樹,能夠?qū)⒛闼袝r間都吞沒。這篇文章我更新添加內(nèi)容的次數(shù)不下十次,每當(dāng)get到了新的東西都忍不住記錄下來。這期間動手去實踐一些知識點,也遇到很多問題,解決這些問題的過程,對相關(guān)知識理解就更深入了。后來在17年的@Swift 大會上還做了 LLVM 相關(guān)內(nèi)容的分享,下圖是其中一張 Slide。

完整幻燈片參看[這里](https://ming1016.github.io/2017/05/27/slides-of-learn-what-interesting-things-you-can-do-with-iOS-compilation/)
### 深入剖析 WebKit
為了完成網(wǎng)頁到原生代碼的轉(zhuǎn)換,我開始學(xué)習(xí) Web 的標(biāo)準(zhǔn),而 WebKit 是蘋果公司對 Web 標(biāo)準(zhǔn)實現(xiàn),V8和 Flutter 渲染技術(shù)的源頭,WebKit 的學(xué)習(xí)能夠讓我更完整的了解網(wǎng)頁從請求到布局再到渲染的流程和使用的相關(guān)技術(shù)。[WebKit的這篇文章](https://ming1016.github.io/2017/10/11/deeply-analyse-webkit/)我羅列了大量的 Web 規(guī)范資料,由于 WebKit 非常的龐大,架構(gòu)也很復(fù)雜,文章里對架構(gòu)也進行了詳細(xì)的說明,對源碼的結(jié)構(gòu)做了詳細(xì)的說明。全文按照一個頁面從請求到最終渲染的流程順序,依次對其關(guān)鍵環(huán)節(jié)里對應(yīng)源代碼和原理進行了詳細(xì)的說明。完成這篇花費時間巨大,代碼基本讀了個遍。之后我對于前端技術(shù)有了更深的理解,特別是頁面異步加載的流程和布局原理。
感覺這篇完全靠的是對前端技術(shù)的熱情完成的。手冢治蟲說過,那些投稿的人,都是熱愛著漫畫,把畫出一部作品作為自己生命意義的人。所以他們才能獲得成功,成為馬拉松里跑到最后的人。熱情可以增加25個 IQ 值。如果一個人仿佛開悟了的高僧,失去任何欲求、愿望、不甘、煩恨與傷痛,那么即使他去畫漫畫,即使基因再好,天賦再高,也只會畫成佛教的禪畫罷了。在比爾.布萊森在《人體簡史》這本書中提到一個鏡子相關(guān)的實驗,實驗來自一名防碎眼鏡商人,在1980年創(chuàng)辦了胚種精選擇庫(Repository for Germinal Choice),這個精子庫只有諾貝爾獲獎?wù)吆推渌艹鲋R權(quán)威的鏡子。他想的是能夠提供最好的精子生出天才嬰兒,結(jié)果是在出生的200名兒童里,沒有一個杰出天才,甚至連一個眼鏡工程師都沒能造出來,可見對做的事情有熱情更加重要,而不僅僅只要基因好就行。
### 深入剖析 iOS 性能優(yōu)化
[性能這篇](https://ming1016.github.io/2017/06/20/deeply-ios-performance-optimization/)最重要的是獨特性,開始只是針對日常開發(fā)性能需要注意的一些點進行了歸納總結(jié),后來需要對啟動項進行分析,于是做了分析的工具,其間我無意多查看了下 thread_basic_info_t 這個結(jié)構(gòu)體里的字段,發(fā)現(xiàn)了 cpu_usage,覺得日后必有用,于是留了個心眼。后來負(fù)責(zé)性能的同學(xué)看了我的這篇文章,跑來找我,跟我說 App 連續(xù)幾個版本都有線上反饋耗電太大,他們自己也很容易復(fù)現(xiàn)出來。這幾個版本調(diào)整了定位頻率,排查了各種懷疑的點,電量消耗依然很大。起初我也沒有思路,instruments 也看不出問題來,于是我使用分析啟動項的方法,查看運行中方法調(diào)用次數(shù),排序來看誰調(diào)用的頻繁,后來發(fā)現(xiàn)調(diào)用頻繁的方法數(shù)量太多很難排查定位。
這時先前留意的 cpu_usage 字段起來關(guān)鍵的作用,通過定時刷新獲取線程中 CPU 使用情況,連續(xù)高使用就揪出詳細(xì)線程堆棧,后來小范圍灰度上線檢測,直接定位到了問題的堆棧,很快的解決了這個大難題。而且有了這個手段,后面也有了底氣,在遇到問題也不會慌了,而且線下也可以使用這個方法進行壓力測試,以免把問題帶到了線上。這個方案也更新記錄到了文章中,有了這個不尋常的經(jīng)歷,文章也就有了很強的獨特性。
### 啟動
關(guān)于啟動我寫了兩篇文章。第一篇是[《如何對 iOS 啟動階段耗時進行分析》](https://ming1016.github.io/2019/12/07/how-to-analyze-startup-time-cost-in-ios/),另一篇是[《App 啟動提速實踐和一些想法》](https://ming1016.github.io/2020/12/18/thinking-in-how-to-speed-up-app/)。說起這兩篇的獨特性,那絕對是獨一無二。我負(fù)責(zé)的這場血淋淋的戰(zhàn)役真可以說是畢生難忘。項目起因不用猜也可以想到,啟動速度持續(xù)劣化,導(dǎo)致用戶體驗變差,落后對手一倍,提速困難重重。臨危受命,當(dāng)時想到的只有一個字,那就是干。
開始最難的還是定方向和定策略以及決策。明確了整體的思路,所有任務(wù)就開始并行跑起來了。由于項目的重要性不言而喻,因此投入資源巨大,不光是我的人都參與了進來,還有很多其他團隊也一起加入。停下所有手上低優(yōu)事情,握緊拳頭全力打贏關(guān)鍵戰(zhàn)役。要的就是能夠速戰(zhàn)速決,一旦拖延,不光是士氣沒了,結(jié)果沒達(dá)成,還會留下一堆爛攤子難有資源去清理。
由于初期謀劃的方案全面、稠密以及有效,多個團隊通力合作配合奇佳,使得在三周內(nèi)超預(yù)期達(dá)成了目標(biāo),不光是領(lǐng)先了對手一倍,還比大部分頭部 App 都要快。這三周說長不長,說短也不短,大量的開發(fā)、調(diào)試、工具設(shè)計開發(fā)、數(shù)據(jù)分析、檢測和驗證工作集中式的進行,對體力和腦力都是極大的挑戰(zhàn),且壓力巨大。
第一篇記錄了前期的策劃內(nèi)容以及一些提效工具的開發(fā)過程。對這三周干的事情進行了沉淀,沉淀的是一次獨特的成功經(jīng)驗。第二篇是在一年后寫的,更多的是記錄了這一年我對啟動這件事情的思考,一年時間的經(jīng)歷也很多,還主負(fù)責(zé)過包體積的項目,所以內(nèi)容就顯得更加豐富了些,有記錄些對性能和調(diào)試工具的研究。
第二篇文章里我提到我發(fā)現(xiàn)了一個寶藏男孩[Michael Eisel](https://eisel.me/),發(fā)現(xiàn)了很多二手資料都是源自他的博客。另外由于這一年也發(fā)現(xiàn)了性能防劣化中,自動化分析工具和能力相關(guān)技術(shù)了解的不夠深入。于是專門去探索了下這方面的情況。對于目前為了保持雙端一直 libimobiledevice,我發(fā)現(xiàn)了 Facebook 專門針對蘋果系統(tǒng)開發(fā)的 idb,idb 做法明顯更聰明些。
這些探究的過程至少是獨特的。更獨特的地方是文中寫的那個A庫多線程問題的排查經(jīng)歷。痛苦的經(jīng)歷我已在文中清晰詳細(xì)的記錄了,歷時三天三夜,當(dāng)大家試完所有情況,士氣全無時,才柳暗花明又一村。全因蘋果的一個 bug。經(jīng)歷這么一遭,對于 GCD 的隊列排查定位問題難這點,我看國外對 iOS 并發(fā)開發(fā)方式吐槽的聲音也很大,于是我很想了解多線程問題蘋果未來會怎么處理。這就有了文中 Swift 并發(fā)提案部分的分析。當(dāng)時這份提案還未進入正式流程(現(xiàn)在已經(jīng)在 Swift 5.5正式發(fā)布了),未來并不明朗,我也擔(dān)心會遺漏關(guān)鍵信息,于是對涉及相關(guān)的提案都進行了閱讀,包括那些提案下所有的評論也都看了。
這兩篇文章跨越了整整一年時間,這一年期間我基本沒有寫其他的文章,但是卻沉淀了很多,所以第二篇實際上可寫的內(nèi)容非常多,一口氣挑著重點的說了一大篇后,還刪減了大量內(nèi)容。寫完第二篇我感覺到化繁為簡的巨大好處。自己做的記錄、素材和資料往往都是大量的,深究下去都是無窮無盡的感覺。因此需要從中提煉出自己的觀點。從那么多內(nèi)容中提煉出觀點是需要足夠的休息和放松,讓你的潛意識主動來幫助你。這些休息和放松也可以是在日常的行為中,比如洗澡、去超市買東西、騎自行車、走路、鍛煉、吃飯和睡覺等,特別是走路和睡覺持續(xù)時間長,最容易進入深度思考。不斷給自己提問題進而更大量的閱讀找答案,思考內(nèi)在邏輯和聯(lián)系。發(fā)散的找,專注的收斂提取觀點,這樣的觀點是用錢買不到的。
通過大白話講清楚,分享出去,這樣的觀點在他人接收時是自然地,意識不到其背后所花的時間和功夫,這就跟優(yōu)秀的 App 一樣,用起來是那么簡單有效,絲毫不拖泥帶水,用戶也意識不到開發(fā) App 所付出的腦動。這種化繁為簡的過程也是將無序雜亂的東西清理掉,讓你寶貴精煉的思想能夠有地方存放。
灌籃高手中流川楓打籃球行云流水,天賦異稟。背后的努力誰又能知曉。我印象最深的一段是櫻木花道為了取得晴子芳心,但始終技不如流川楓,總以為是天賦不夠。一天晚上櫻木花道很晚來到籃球館,發(fā)現(xiàn)流川楓還在苦練,才發(fā)現(xiàn)原來白天看起來懶散傲慢的流川楓原來比誰都要刻苦,簡單輕松從來都不是廉價的。
對于分享,有智慧的人都懂得給予越多收獲越大。友情比金錢價值更高,就好像有一個開電影公司的朋友比擁有一家電影公司要好。分享不是要得到他人的認(rèn)可,如果你知道這點,你擁有的能量就是無窮的,力量也是無敵的。
## 沒怎么寫過,那下一步怎么行動
看到這里,你一定會想“看你說了那么多,但我雙手放在鍵盤前,腦袋還是一片空白,無從下手”。
如果想幫其他人,讓他真的動手去做些什么事情,其實更應(yīng)該是要讓做這件事情變得容易很多倍,但方向是一樣的,這樣下次他就更好接受些?;ヂ?lián)網(wǎng)開始發(fā)布內(nèi)容門檻高,后來有了微博和朋友圈這種能夠一句話就快速發(fā)布出去的產(chǎn)品后,大家發(fā)內(nèi)容就比以前更多了。去讀資料和文章,可以懂更多的知識,自身能力還是需要通過練習(xí)才能夠有提升。想把事情做好,還是需要去做。
因此你應(yīng)該更重視動手寫,如果你不知道如何寫,可能就不知道如何思考。有叛逆和逆向思維的人常常是愛問問題的人,愛自問愛思考,對那些已經(jīng)共識正在運作的事物提出疑問,尋找和關(guān)注答案,這樣才會有打破現(xiàn)狀的意識。一些人小時候就能看到有這樣的特點,因此在別人教你怎樣怎樣做時,不要太當(dāng)回事,相信自己實踐出來的答案。多聽你喜歡人說話,多傾聽,不斷問還有沒想說的。
還要從各種類型人那學(xué)習(xí),甚至是和你觀點不同的人。因為在每個人堅持的思想里,都會有他自己獨特的經(jīng)歷和實踐總結(jié)來的結(jié)論。通過他們的結(jié)論,你也可以自己去實踐和驗證形成自己的觀點,這樣就會有復(fù)利效應(yīng)。做的結(jié)果其實并不重要,重要的是在做的過程中,你自己有沒有變得更好。
你說的話,你的觀點,你的評論都不能代表你,而是你所做的事情,花了很多時間做的事情那才是你。改變一個人的行為來改變思維,比改變一個人的思維來改變行為要容易的多很多。
因此,光看光聽不動手寫是沒用的。那行動起來的話,怎么做更好些呢?
### 四個步驟
第一步,零散的想法、工作內(nèi)容和看到的好的技術(shù)資料及時記錄,先按照時間軸的方式記錄。這一步是很容易操作的,幾乎不用費腦,只需要機械的做記錄就行,也不用考慮先前提到四個點里任何一個。
第二步,對于記錄的內(nèi)容進行分類,開始粒度可以粗一點,比如性能、架構(gòu)、構(gòu)建、編程語言、管理、成長、旅行和科技等,根據(jù)自身興趣點和期望發(fā)展方向來就好。
第三步,做完一個項目,或者想對先前做的事情進行總結(jié)時,先一口氣快速寫出想表達(dá)的內(nèi)容出來,這時寫的內(nèi)容體現(xiàn)出獨特性,搭好骨架。然后針對寫的內(nèi)容中的一些技術(shù)點,進行真實感的完善。真實感的完善是需要很多素材和資料的,這時在第一步和第二步做的工作和積累就能夠派上用場了。找到相關(guān)大分類進行細(xì)分來補充文章的血肉。
第四步,也是最后一步,可以充分發(fā)揮自己軟實力和創(chuàng)造力,通過故事性和新意來披上文章的皮膚,讓文章能夠看起來更加完整和吸引人,提高閱讀的體驗。
完整完成這四個步驟并不容易,經(jīng)常就會因為惰性半途而廢。這時就需要 push 自己一把,方法的話,我這邊的經(jīng)驗就是定目標(biāo),定時間節(jié)點。比如定好一個對外分享的時間,這樣目標(biāo)性更強,同時也有了約束和責(zé)任,自己的惰性在這一段時間內(nèi)就能夠得到很好的消減。
為了達(dá)成目的,徹底理清你想要啥,還需要清空干擾,方法很簡單,除了當(dāng)前最重要的事情,其它所有待做事情都記在備忘錄里以便追蹤防止遺漏。完成當(dāng)前事情后,再去查看備忘錄,然后定新目標(biāo)新計劃。
完成文章后可以通過下面八個問題來檢查下文章的完成度。
1. 我為什么做這件事?
2. 誰已經(jīng)做了?他們都是怎么做的?效果怎樣?
3. 我和他們做的不一樣在哪?怎么想到的?能詳細(xì)具體說出涉及相關(guān)知識點嗎?(??重點,寫好了的話,其他問題可有可無)
4. 我碰到了什么困難?
5. 我怎么解決的?
6. 做的有亮點嗎?為什么是亮點?
7. 做完后效果是怎樣的?超預(yù)期地方在哪?
8. 以后還有計劃打算嗎?為什么?
### 所用軟件
下面是我寫文章會用到的一些軟件,以及我關(guān)注和用到的一些特性:
系統(tǒng)自帶備忘錄
* 零散想法和靈感記錄
* 待做事項記錄(一個一個直接刪掉的感覺不錯)
* 聚焦想法思路,不用去考慮分類整理等
[熊掌記](https://bear.app/cn/)
* 本地文檔管理(多設(shè)備同步收費)
* 標(biāo)簽系統(tǒng)簡化分類
[Notion](https://www.notion.so/)
* 在線文檔管理
* 數(shù)據(jù)庫方式管理,分類、檢索和排序
* 字段自定義添加,比如標(biāo)簽、類別、鏈接、標(biāo)題等等都可以自定義
* 基于數(shù)據(jù)庫和自定義字段可生成看板、時間軸、日歷、列表、表格、網(wǎng)格等不同視圖樣式查看。
* 有chrome插件
[VS Code](https://code.visualstudio.com/)
* 本地文檔管理(文件夾,Git支持可多端同步)
* Markdown 插件支持(Markdown All in One、Pangu-Markdown、Markdown Preview Enhanced、Word Count CJK)
[Obsidian](https://obsidian.md/)
* 本地文檔管理(文件夾)
* Markdown原生支持
* 插件系統(tǒng),比如有大綱和看板等插件可用
* 雙向鏈接與關(guān)系圖譜
[Procreate](https://procreate.art/cn)
* 可以把在紙上的草圖配上顏色
軟件使用上,我會通過備忘錄或熊掌記快速記錄一些素材和想法,定期挪到 Notion 里,我是把 Notion 當(dāng)做一個大倉庫,寫作的第二階段整理分類我就是在 Notion 中完成的,充分利用 Notion 的自定義字段能力,對所有資料進行各種維度劃分和歸檔。開始寫文章時,初期會用 VS Code 來寫,如果文章寫長了就會打開 Obsidian 來繼續(xù)寫,主要是 Obsidian 的大綱效果比較好些。最后文章的配圖我會使用 Procreate 來畫,里面有輔助線,打開后可以很方便做參照,寫圖中文字就不容易偏了。
工具只是工具,記錄的內(nèi)容和自己的思想才是核心。我現(xiàn)在讀書還是喜歡在紙上寫筆記,特別有感觸的才會提煉一些觀點敲到備忘錄中,比如我看了網(wǎng)飛(Nexflix)的[《不拘一格》](https://book.douban.com/subject/35102294/)后提煉了一些觀點做了記錄,筆記如下:
```bash
制度都是圍繞著怎么不阻礙所要的人發(fā)揮。比如假期自由安排、無審批、決策權(quán)非自上而下,而是在認(rèn)識一致情況下松散耦合。
要和不要什么樣的人呢?
不要的人:
與人相處好,但能力平平
工作狂,缺少判斷力
天資好,行動力強,但悲觀、牢騷
有才華的混蛋:
特征
聽到贊美就自覺優(yōu)秀
對想法不明智的人,會進行嘲笑
會侮辱天賦不如自己的人
表現(xiàn)
喜歡會上慷慨陳詞,重復(fù)表達(dá)自己觀點
如沒抓住他的要點,會打斷別人的話
別人發(fā)言,不贊同時會不聽,做自己的事情
別人啰嗦,沒抓住要點,立刻打斷
總想著怎么做才能表現(xiàn)好,得獎金,缺少開放的認(rèn)知空間
為什么:管理花費精力多,討論質(zhì)量低,會排擠卓越員工。
要的人:
非凡創(chuàng)造力、工作出色(完成繁重任務(wù))、合作好
在放松狀態(tài)下,會靈光乍現(xiàn)
公司利益至上
自覺追求成功,無論是否有獎金(已給予能力匹配市場最高價)
當(dāng)某一固定思維遇到瓶頸時,他總有辦法擺脫瓶頸,或嘗試不同角度看待問題
在有才能,受愛戴的前提下,自己犯錯大聲說,成功小聲說,讓人感覺親近、真誠和體貼。
有良好的判斷力
為什么:優(yōu)秀的人激勵其他優(yōu)秀的人,出色成果感染更多人才。
只有公司里的員工都是上面提到的要的人時,公司的密度才高。這樣的公司不是家庭而是專業(yè)運動隊,運動隊追求卓越,每個位置都是最佳人選;訓(xùn)練就是為了勝利,大家都能給予和接受反饋;成績要好,不能只用努力就夠了。
書中詳細(xì)介紹了網(wǎng)飛的制度由來,大量員工的實際案例,碰到了問題如何完善了制度。非常全面進行了制度介紹,甚至包含了進行創(chuàng)新的幾個步驟的詳細(xì)說明,還有網(wǎng)飛創(chuàng)始人里德是如何做到讓大家認(rèn)識一致的。
最后是書中引用的小王子那段:
如果你想造艘船,
不要老催人去采木,
忙著分配工作
和發(fā)號施令。
而是要激起他們
對浩瀚無垠的
大海的向往。
```
## 舉個例子,怎么寫這次WWDC21的見聞文章
光說不練,這樣不好吧,那就現(xiàn)舉個例子,看看怎么按照上面的四個步驟一步一步寫一篇技術(shù)文章。那就以現(xiàn)在剛開完的 WWDC21 為主題,寫個《WWDC21我的見聞》吧。
首先我們先做第一步,從 WWDC21 開始,我就將我看到的信息、還有看感興趣 Session 中有用的點都記錄了下來,只考慮是否要記,二不考慮其它任何事情。你可以看我[WWDC21第一天的記錄](https://ming1016.github.io/2021/06/08/wwdc2021-day1-note/),我將其發(fā)到了我的博客和公眾號上。后面幾天我也不斷的收集記錄著零碎的信息。然后對這些記錄進行分類。接下來再開始內(nèi)容的撰寫。
寫 WWDC21 見聞錄,你可以先想想著你想要什么內(nèi)容,有沒人提供,有的話可以直接鏈過來,沒有的話可以自己去體會,去想,去經(jīng)歷,然后分享出來。
我會先寫個總覽,內(nèi)容如下。
### 總覽
WWDC21 官方通過一個頁面匯總了發(fā)布的新技術(shù),詳見[這里](https://developer.apple.com/documentation/new-technologies-wwdc21)。WWDC21 里的代碼范例官方都有提供和匯總,詳見[這里](https://developer.apple.com/sample-code)。WWDC21 期間蘋果也[列出了](https://developer.apple.com/wwdc21/beyond-wwdc/)蘋果公司之外圍繞 WWDC 其它組織的學(xué)習(xí)、交流和娛樂的活動。
如果沒有太多時間看 Session 視頻,也可以直接看其他人的筆記,國外有[WWDC NOTES](https://www.wwdcnotes.com/),國內(nèi)有老司機技術(shù)周刊的[WWDC21 內(nèi)參](https://xiaozhuanlan.com/wwdc21)。往屆內(nèi)容也有人做了[匯總](https://github.com/Juanpe/About-SwiftUI)
簡單筆記可以查缺補漏,Alejandro Martinez 在這篇文章[WWDC21 notes](https://alejandromp.com/blog/wwdc21-notes/)中對各種主題做了簡單的記錄,列出了關(guān)鍵字方便檢索。
### Session推薦
全部 Session,在[這里](https://developer.apple.com/videos/wwdc2021/)查看。這里有份[推薦清單](https://useyourloaf.com/blog/wwdc-2021-viewing-guide/)。我也列了下我關(guān)注的 Session。如下:
SwiftUI 相關(guān) Session:
* [What's new in SwiftUI](https://developer.apple.com/videos/play/wwdc2021/10018/):包括了所有SwiftUI這次的更新內(nèi)容介紹。
* [Add rich graphics to your SwiftUI app](https://developer.apple.com/videos/play/wwdc2021/10021/):內(nèi)容包括安全區(qū)域、材質(zhì)包、畫布API等。
* [Craft search experiences in SwiftUI](https://developer.apple.com/videos/play/wwdc2021/10176/):.searchable修飾符的使用。
* [Direct and reflect focus in SwiftUI](https://developer.apple.com/videos/play/wwdc2021/10023/):關(guān)于移動焦點的使用。
* [SwiftUI on the Mac: Build the fundamentals](https://developer.apple.com/videos/play/wwdc2021/10062/):內(nèi)容是一步一步構(gòu)建一個macOS應(yīng)用。
* [SwiftUI on the Mac: The finishing touches](https://developer.apple.com/videos/play/wwdc2021/10289/):展示如何通過設(shè)置讓人們靈活定制一個應(yīng)用程序。
* [Demystify SwiftUI](https://developer.apple.com/videos/play/wwdc2021/10022/):介紹了SwiftUI的三個核心Identity、Lifetime和Dependencies。
* [Discover concurrency in SwiftUI](https://developer.apple.com/videos/play/wwdc2021/10019/):展示并發(fā)工作流如何和SwiftUI數(shù)據(jù)流進行結(jié)合。
* [SF Symbols in SwiftUI](https://developer.apple.com/videos/play/wwdc2021/10349/):定義Symbols的大小,顯示不同變體以及Symbols著色。
Swift Concurrency 相關(guān) Session:
* [Meet async/await in Swift](https://developer.apple.com/videos/play/wwdc2021/10132/):了解 async/await 開發(fā)模式。
* [Explore structured concurrency in Swift](https://developer.apple.com/videos/play/wwdc2021/10134/):內(nèi)容包括創(chuàng)建不同類型并發(fā)任務(wù),如何創(chuàng)建任務(wù)組,如何取消正在進行的任務(wù)。
* [Protect mutable state with Swift actors](https://developer.apple.com/videos/play/wwdc2021/10133/):內(nèi)容有如何使用Swift actors組織資源競爭,actors如何工作等。
* [Swift concurrency: Behind the scenes](https://developer.apple.com/videos/play/wwdc2021/10254/):了解更多Swift并發(fā)的細(xì)節(jié),更安全的數(shù)據(jù)競爭和處理線程爆炸,和GCD的不同,線程模型怎么工作等。值得看多遍。
* [Swift concurrency: Update a sample app](https://developer.apple.com/videos/play/wwdc2021/10194/):介紹async/await、actors和continuation在現(xiàn)實工作中的經(jīng)驗
* [Meet AsyncSequence](https://developer.apple.com/videos/play/wwdc2021/10058/):流式傳輸數(shù)據(jù)。
* [Use async/await with URLSession](https://developer.apple.com/videos/play/wwdc2021/10095/):URLSession中怎么使用async/await和AsyncSequence。
DocC:
* [Meet DocC documentation in Xcode](https://developer.apple.com/videos/play/wwdc2021/10166):了解如何使用DocC,如何生成DocC檔案,并讓他們顯示在文檔瀏覽器中。
* [Host and automate your DocC documentation](https://developer.apple.com/videos/play/wwdc2021/10236):如何通過自己的服務(wù)器托管DocC,自動構(gòu)建和分發(fā)DocC檔案。
* [Elevate your DocC documentation in Xcode](https://developer.apple.com/videos/play/wwdc2021/10167):介紹寫文檔的最佳實踐。
* [Build interactive tutorials using DocC](https://developer.apple.com/videos/play/wwdc2021/10235):編寫交互教程。
其它感興趣的 Session:
* [Write a DSL in Swift using result builders](https://developer.apple.com/videos/play/wwdc2021/10253/):使用 result builders 來創(chuàng)建DSL,是代碼更容易閱讀和維護。
* [Create 3D models with Object Capture](https://developer.apple.com/videos/play/wwdc2021/10076/):捕獲現(xiàn)實對象,生成3D模型。
* [What’s new in SF Symbols](https://developer.apple.com/videos/play/wwdc2021/10097/):SF Symbols的更新。介紹如何讓自定義符號支持單色、分層、調(diào)色板和多色渲染模式。
* [Explore the SF Symbols 3 app](https://developer.apple.com/videos/play/wwdc2021/10288/):SF Symbols應(yīng)用程序的更新。
* [Create custom symbols](https://developer.apple.com/videos/play/wwdc2021/10250/):創(chuàng)建自定義symbols。
* [Ultimate application performance survival guide](https://developer.apple.com/videos/play/wwdc2021/10181/):關(guān)于性能優(yōu)化的話題,內(nèi)容包括性能相關(guān)工具、指標(biāo)和范式。會涉及到Instruments、XCTest、MetricKit等等技術(shù)和工具。iOS 15的動態(tài)鏈接器做了優(yōu)化能夠啟動提速并減少Swift二進制大小??梢詤⒖?span style="color: rgb(255, 104, 39);">[這篇文章](https://medium.com/geekculture/how-ios-15-makes-your-app-launch-faster-51cf0aa6c520)
* [Symbolication: Beyond the basics](https://developer.apple.com/videos/play/wwdc2021/10211/):介紹符號化的過程。
* [Meet the Swift Algorithms and Collections packages](https://developer.apple.com/videos/play/wwdc2021/10256/):講的通俗易懂。
### Swift 的一些更新
Paul Hudson 的這篇[What's new in Swift 5.5?](https://www.hackingwithswift.com/articles/233/whats-new-in-swift-5-5) 已經(jīng)把這些更新說的非常詳細(xì)了,每個更新點都有對應(yīng)的例子可以試。今年蘋果公司推出 AttributedString 用來替代 OC 時代的 NSAttributedString。AttributedString 是值類型,可以直接在 SwiftUI 的 Text 里使用。AttributedString 還支持簡單的 Markdown 語法,Markdown 單行沒問題,多行功能受限。
DocC 是通過 Xcode 編譯后生成的文檔,使用 Product -> Build Documentation 就會生成DocC。在函數(shù)接口代碼上使用 Shift+Cmd+A 快捷鍵就會創(chuàng)建文檔模板,有參數(shù)和返回值的話也會將其提取出來,包括參數(shù)類型等,并生成標(biāo)準(zhǔn)文檔格式,方便你進行內(nèi)容編寫。基本 Markdown 語法是支持的。詳細(xì)的介紹可以看前面列出的官方 Session,或者看這篇文章[How to document your project with DocC](https://www.hackingwithswift.com/articles/238/how-to-document-your-project-with-docc)。
### 今年重頭戲 Swift Concurrency
ABI 穩(wěn)定后,Swift 的核心團隊可以開始關(guān)注 Swift 語言一直缺失的原生并發(fā)能力了。最初是由[Chris Lattner](https://twitter.com/clattner_llvm)在17年發(fā)的[Swift并發(fā)宣言](https://gist.github.com/lattner/31ed37682ef1576b16bca1432ea9f782),從此開闊了大家的眼界。后來 Swift Evolution 社區(qū)討論了十幾個提案,幾十個方案,以及幾百頁的設(shè)計文件,做了大量的改進,社區(qū)中用戶積極的參與反饋,Chris 也一直在 Evolution 中積極的參與設(shè)計。
Swift Concurrency 的實現(xiàn)用了[LLVM的協(xié)程](https://llvm.org/docs/Coroutines.html)把 async/await 函數(shù)轉(zhuǎn)換為基于回調(diào)的代碼,這個過程發(fā)生在編譯后期,這個階段你的代碼都沒法辨識了。異步的函數(shù)被實現(xiàn)為 coroutines,在每次異步調(diào)用時,函數(shù)被分割成可調(diào)用的函數(shù)部分和后面恢復(fù)的部分。coroutine 拆分的過程發(fā)生在生成LLVM IR階段。Swift使用了哪些帶有自定義調(diào)用約定的函數(shù)保證尾部調(diào)用,并專門為Swift進行了調(diào)整。
Swift Concurrency 不是建立在 GCD 上,而是使用的一個全新的線程池。GCD 中啟動隊列工作會很快在提起線程,一個隊列阻塞了線程,就會生成一個新線程。基于這種機制 GCD 線程數(shù)很容易比 CPU 核心數(shù)量多,線程多了,線程就會有大量的調(diào)度開銷,大量的上下文切換,會使 CPU 運行效率降低。而 Swift Concurrency 的線程數(shù)量不會超過 CPU 內(nèi)核,將上下文切換放到同一個線程中去做。為了實現(xiàn)線程不被阻塞,需要通過語言特性來做。做法是,每個線程都有一個堆棧記錄函數(shù)調(diào)用情況,一個函數(shù)占一個幀。函數(shù)返回后,這個函數(shù)所占的幀就會從堆棧彈出。await 的 async 函數(shù)被作為異步幀保存在堆上等待恢復(fù),而不阻礙其它函數(shù)入棧執(zhí)行。在 await 后運行的代碼叫 continuation,continuation 會在要恢復(fù)時放回到線程的堆棧里。異步幀會根據(jù)需要放回棧上。在一個異步函數(shù)中調(diào)用同步代碼將添加幀到線程的堆棧中。這樣線程就能夠一直向前跑,而不用創(chuàng)建更多線程減少調(diào)度。
Douglas 在 Swift 論壇里發(fā)的 Swift Concurrency 下個版本的規(guī)劃貼 [Concurrency in Swift 5 and 6](https://forums.swift.org/t/concurrency-in-swift-5-and-6/49337),論壇里還有一個帖子是專門用來[征集Swift Concurrency意見](https://forums.swift.org/t/swift-concurrency-feedback-wanted/49336)的,帖子本身列出了 Swift Concurrency 相關(guān)的所有提案,也提出歡迎有新提案發(fā)出來,除了這些提案可以看外,帖子回復(fù)目前已經(jīng)過百,非常熱鬧,可以看出大家對 Swift Concurrency 的關(guān)注度相當(dāng)?shù)母摺?/p>
非常多的人參與了 Swift Concurrency 才使其看起來和用起來那么簡單。Doug Gregor 在參與 John Sundell 的播客后,發(fā)了很多條推聊 Swift Concurrency,可以看到參與的人非常多,可見背后付出的努力有多大。下面我匯總了 Doug Gregor 在推上發(fā)的一些信息,你通過這些信息也可以了解 Swift Concurrency 幕后信息,所做的事和負(fù)責(zé)的人。
[@pathofshrines](https://twitter.com/pathofshrines)是 Swift Concurrency 整體架構(gòu)師,包括低級別運行時和編譯器相關(guān)細(xì)節(jié)。[@illian](https://twitter.com/illian)是 async sequences、stream 和 Fundation 的負(fù)責(zé)人。[@optshiftk](https://twitter.com/optshiftk)對 UI 和并發(fā)交互的極好的洞察力帶來了很棒的 async 接口,[@phausler](https://twitter.com/phausler)帶來了 async sequences。Arnold Schwaighofer、[@neightchan](https://twitter.com/neightchan)、[@typesanitizer](https://twitter.com/typesanitizer)還有 Tim Northover 實現(xiàn)了 async calling convention。
[@ktosopl](https://twitter.com/ktosopl)有很深厚的 actor、分布式計算和 Swift-on-Server 經(jīng)驗,帶來了 actor 系統(tǒng)。Erik Eckstein 為 async 函數(shù)和actors建立了關(guān)鍵的優(yōu)化和功能。
SwiftUI是[@ricketson_](https://twitter.com/ricketson_)和[@luka_bernardi](https://twitter.com/luka_bernardi)完成的async接口。async I/O的接口是[@Catfish_Man](https://twitter.com/Catfish_Man)完成的。[@slava_pestov](https://twitter.com/slava_pestov)處理了 Swift 泛型問題,還指導(dǎo)其他人編譯器實現(xiàn)的細(xì)節(jié)。async 重構(gòu)工具是Ben Barham 做的。大量代碼移植到 async 是由[@AirspeedSwift](https://twitter.com/AirspeedSwift)領(lǐng)導(dǎo),由 Angela Laar,Clack Cole,Nicole Jacques 和[@mishaldshah](https://twitter.com/mishaldshah)共同完成的。
[@lorentey](https://twitter.com/lorentey)負(fù)責(zé) Swift 接口的改進。[@jckarter](https://twitter.com/jckarter)有著敏銳的語言設(shè)計洞察力,帶來了語言設(shè)計經(jīng)驗和編譯器及運行時實現(xiàn)技能。[@mikeash](https://twitter.com/mikeash) 也參與了運行時開發(fā)中。操作系統(tǒng)的集成是[@rokhinip](https://twitter.com/rokhinip)完成的,[@chimz](https://twitter.com/chimz)提供了關(guān)于 Dispatch 和 OS 很好的建議,Pavel Yaskevich 和?
[@hollyborla](https://twitter.com/hollyborla)進行了并發(fā)所需要關(guān)鍵類型檢查器的改進。[@kastiglione](https://twitter.com/kastiglione)、Adrian Prantl和[@fred_riss](https://twitter.com/fred_riss)實現(xiàn)了調(diào)試。[@etcwilde](https://twitter.com/etcwilde)和[@call1cc](https://twitter.com/call1cc)實現(xiàn)了語義模型中的重要部分。
[@evonox](https://twitter.com/evonox)負(fù)責(zé)了服務(wù)器Linux 的支持。[@compnerd](https://twitter.com/compnerd)將 Swift Concurrency 移植到了 Windows。
Swift Concurrency 模型簡單,細(xì)節(jié)都被隱藏了,比 Kotlin 和 C++的 Coroutine 接口要簡潔很多。比如 Task 接口形式就很簡潔。Swift Concurrency 大體可分為 async/await、Async Sequences、結(jié)構(gòu)化并發(fā)和? Actors。下面展開說下。
#### async/await
通過類似 throws 語法的 async 來指定函數(shù)為異步函數(shù),異步函數(shù)才能夠使用 await,使用異步函數(shù)要用 await。await 修飾在 suspension point 時當(dāng)前線程可以讓給其它任務(wù)執(zhí)行,而不用阻塞當(dāng)前線程,等 await 后面的函數(shù)執(zhí)行完成再回來繼續(xù)執(zhí)行,這里需要注意的是回來執(zhí)行不一定是在離開時的線程上。async/await 提案是[SE-0296](https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md)。如果想把現(xiàn)有的異步開發(fā)帶到 async/await 世界,請使用 withCheckedThrowingContinuation。
async/await 還有一個非常明顯的好處,就是不會再有[weak self] dance 了。
#### Async Sequences
AsyncSequence 的使用方式是 for-await-in 和 for-try-await-in,系統(tǒng)提供了一些接口,如下:
* FileHandle.standardInput.bytes.lines
* URL.lines?
* URLSession.shared.data(from: URL)
* let (localURL, _ ) = try await session.download(from: url) 下載和get請求數(shù)據(jù)區(qū)別是需要邊請求邊存儲數(shù)據(jù)以減少內(nèi)存占用
* let (responseData, response) = try await session.upload(for: request, from: data)
* URLSession.shared.bytes(from: URL)
* NotificationCenter.default.notifications
#### 結(jié)構(gòu)化并發(fā)
使用這些接口可以一邊接收數(shù)據(jù)一邊進行顯示,AsyncSequence 的提案是[SE-0298](https://github.com/apple/swift-evolution/blob/main/proposals/0298-asyncsequence.md)(Swift 5.5可用)。AsyncStream 是創(chuàng)建自己異步序列的最簡單的方法,處理迭代、取消和緩沖。AsyncStream 正在路上,提案是[SE-0314](https://github.com/apple/swift-evolution/blob/main/proposals/0314-async-stream.md)。
Task 為一組并發(fā)任務(wù)創(chuàng)建一個運行環(huán)境,async let 可以讓任務(wù)并發(fā)執(zhí)行,結(jié)構(gòu)化并發(fā)(Structured concurrency,提案在路上[SE-0304](https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md))withTaskGroup 中 group.async 可以將并發(fā)任務(wù)進行分組。
#### Actors
我們寫的程序會在進程中被拆成一個一個小指令,這些指令會在某刻會一個接一個同步的或者并發(fā)的執(zhí)行。系統(tǒng)會用多個線程執(zhí)行并行的任務(wù),執(zhí)行順序是調(diào)度器來管理的,現(xiàn)代多核可以同時處理多個線程,當(dāng)一個資源在多個線程上同時被更改時就會出問題。并發(fā)任務(wù)對數(shù)據(jù)資源操作容易造成數(shù)據(jù)競爭,以前需要手動放到串行隊列、使用鎖、調(diào)度屏障或 Atomics 的方式來避免。以前處理容易導(dǎo)致昂貴的上下文切換,過多線程容易導(dǎo)致線程爆炸,容易意外阻斷線程導(dǎo)致后面代碼沒法執(zhí)行,多任務(wù)相互的等待造成了死鎖,block 和內(nèi)存引用容易出錯等等問題。
現(xiàn)在 Swift Concurrency 可以通過 actor 來創(chuàng)建一個區(qū)域,在這個區(qū)域會自動進行數(shù)據(jù)安全保護,保證一定時間只有一個線程訪問里面數(shù)據(jù),防止數(shù)據(jù)競爭。actor 內(nèi)部對成員訪問是同步的,成員默認(rèn)是隔離的,actor 外部對 actor 內(nèi)成員的訪問只能是異步的,隱式同步以防止數(shù)據(jù)競爭。MainActor 繼承自能確保全局唯一實例的 GlobalActor,保證任務(wù)在主線程執(zhí)行,這樣你就可以拋棄掉在你的 ViewModel 里寫 DispatchQueue.main.async 了。
Actors 的概念通常被用于分布式計算,Actor 模型參看[Wikipedia](https://en.wikipedia.org/wiki/Actor_model)里的詳細(xì)解釋,Swift 中的實現(xiàn)效果也非常的理想。Actors 的提案[SE-0306](https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md)已在 Swift 5.5落實。
很多語言都支持 actors 還有 async/await,實現(xiàn)的方式也類似,actor 使用的不是鎖,而是用的 async/await 這樣能夠在一個線程中切換上下文來避免線程空閑的線程模型。actor 還利用編譯器,提前做會引起并發(fā)問題的檢查。
actor 是遵循 Sendable 協(xié)議的,只有結(jié)構(gòu)體和 final 類才能夠遵循 Sendable,繼承于 Sendable 協(xié)議的 Excutor 協(xié)議表示方法本身,SerialExecutor 表示以串行方式執(zhí)行。actor 使用 C++寫的,源碼在[這里](https://github.com/apple/swift/blob/main/stdlib/public/Concurrency/Actor.cpp),可以看到 actor 主要是通過控制各個 job 執(zhí)行的狀態(tài)的管理器。job 執(zhí)行優(yōu)先級來自 Task 對象,排隊時需要確保高優(yōu) job 先被執(zhí)行。全局 Executor 用來為 job 排隊,通知 actor 擁有或者放棄線程,實現(xiàn)在[這里](https://github.com/apple/swift/blob/main/stdlib/public/Concurrency/GlobalExecutor.cpp)。由于等待而放棄當(dāng)前線程讓其他 actor 執(zhí)行的 actor,在收到全局 Executor 創(chuàng)建一個新的 job 的通知,使其可以進入一個可能不同線程,這個過程就是并發(fā)模型中描述的 Actor Reentrancy。
#### Swift Concurrency相關(guān)提案集合
所有相關(guān)提案清單如下:
*? [SE-0296: Async/await](https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md) [【譯】SE-0296 Async/await](https://kemchenj.github.io/2021-03-06/)
*? [SE-0317: async let](https://github.com/apple/swift-evolution/blob/main/proposals/0317-async-let.md)?
*? [SE-0300: Continuations for interfacing async tasks with synchronous code](https://github.com/apple/swift-evolution/blob/main/proposals/0300-continuation.md) [【譯】SE-0300 Continuation -- 執(zhí)行同步代碼的異步任務(wù)接口](https://kemchenj.github.io/2021-03-31/)
*? [SE-0302: Sendable and @Sendable closures](https://github.com/apple/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md)?
*? [SE-0298: Async/Await: Sequences](https://github.com/apple/swift-evolution/blob/main/proposals/0298-asyncsequence.md) [【譯】SE-0298 Async/Await 序列](https://kemchenj.github.io/2021-03-10/)
*? [SE-0304: Structured concurrency](https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md)?
*? [SE-0306: Actors](https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md) [【譯】SE-0306 Actors](https://kemchenj.github.io/2021-04-25/)
*? [SE-0313: Improved control over actor isolation](https://github.com/apple/swift-evolution/blob/main/proposals/0313-actor-isolation-control.md)?
*? [SE-0297: Concurrency Interoperability with Objective-C](https://github.com/apple/swift-evolution/blob/main/proposals/0297-concurrency-objc.md) [【譯】SE-0297 Concurrency 與 Objective-C 的交互](https://kemchenj.github.io/2021-03-07/)
*? [SE-0314: AsyncStream and AsyncThrowingStream](https://github.com/apple/swift-evolution/blob/main/proposals/0314-async-stream.md)?
*? [SE-0316: Global actors](https://github.com/apple/swift-evolution/blob/main/proposals/0316-global-actors.md)?
*? [SE-0310: Effectful read-only properties](https://github.com/apple/swift-evolution/blob/main/proposals/0310-effectful-readonly-properties.md)?
*? [SE-0311: Task Local Values](https://github.com/apple/swift-evolution/blob/main/proposals/0311-task-locals.md)?
*? [Custom Executors](https://forums.swift.org/t/support-custom-executors-in-swift-concurrency/44425)?
#### 學(xué)習(xí)路徑
如果打算嘗試 Swift Concurrency 的話,按照先后順序,可以先看官方手冊介紹文章[Concurrency](https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html)。再看[Meet async/await in Swift](https://developer.apple.com/videos/play/wwdc2021/10132/)這個Session,了解背后原理看[Explore structured concurrency in Swift](https://developer.apple.com/videos/play/wwdc2021/10134)。動手照著試示例代碼,看Paul的[Swift Concurrency by Example](https://www.hackingwithswift.com/quick-start/concurrency)這個系列。接著看[Protect mutable state with Swift actors](https://developer.apple.com/videos/play/wwdc2021/10133)來了解 actors 怎么防止數(shù)據(jù)競爭。通過[Discover concurrency in SwiftUI](https://developer.apple.com/videos/play/wwdc2021/10019)看 concurrency 如何在 SwiftUI 中使用,[Use async/await with URLSession](https://developer.apple.com/videos/play/wwdc2021/10095)來看怎么在 URLSession 中使用 async/await。最后聽聽負(fù)責(zé) Swift Concurrency 的 Doug Gregor 參加的一個[播客的訪談](https://www.swiftbysundell.com/podcast/99/),了解下 Swift Concurrency 背后的故事。
#### Swift Concurrency 和 Combine
由于 Swift Concurrency 的推出和大量的 Session 發(fā)布,特別是[AsyncSequence](https://developer.apple.com/documentation/swift/asyncsequence/)的出現(xiàn),以及正在路上的[AsyncStream、AsyncThrowingStream](https://github.com/apple/swift-evolution/blob/main/proposals/0314-async-stream.md)和[continuation](https://github.com/apple/swift-evolution/blob/main/proposals/0300-continuation.md)提案(在Xcode 13.0 beta 3 AsyncStream 正式[release](https://developer.apple.com/documentation/swift/asyncstream?changes=latest_beta)),這些越來越多和 Combine 功能重疊的特性出現(xiàn)在 Swift Concurrency 藍(lán)圖里時,大家開始猜測是否 Combine 會被 Swift Concurrency 替代。關(guān)于未來是 Swift Concurrency 還是 Combine,我的感覺是,Combine 更側(cè)重在響應(yīng)式編程上,而響應(yīng)式編程并不是所有開發(fā)人員都會接受的,而 Swift Concurrency 是所有人都愿意接受的開發(fā)方式,從 Swift Concurrency 推出后開發(fā)者使用的數(shù)量和社區(qū)反應(yīng)火熱程度來看都比 Combine 要大。在蘋果對 Combine 有下一步動作之前,我還是更偏向 Swift Concurrency。
見聞寫到這里,把獨特性比作骨架,真實感比作血肉,故事性和新意比作皮膚,你會發(fā)現(xiàn)沒有寫出自己的經(jīng)歷的話,就像進擊巨人里的那些小巨人,即使有了完整的皮膚,但骨頭架子不大是不會有開頭踢破大門的只有骨架和血肉的巨型大巨人那么強大且震撼有力。
那么接下來我就描寫一些我在 WWDC21 期間獨特的一些經(jīng)歷。
### WWDC.playground直播活動
想想 WWDC21 過程中我還是有些經(jīng)歷,比如參加了蘋果官方推薦的外圍活動[WWDC.playgournd by SwiftGG](https://swift.gg/wwdc/)。


連續(xù)看了5天活動直播,還參加了一天的 Live Coding 介紹 SwiftUI 的新特性。直播 Live Coding 準(zhǔn)備的時間很少,而且以前我還沒有現(xiàn)場當(dāng)著幾千人面寫代碼的經(jīng)歷,直播前一天晚上趕著通宵達(dá)旦看完了相關(guān) Session,寫了些代碼樣例測試,當(dāng)天白天還開了一個很長的會,回家前和同事討論一個技術(shù)問題時,我發(fā)現(xiàn)我嗓子還啞了。到家坐在桌前腳還抽筋了,你可想象到我當(dāng)時內(nèi)心有多崩潰。
在直播前,我還專門的給思琦先演練了一遍,其中在介紹 AsyncImage 處理失敗、空白、成功還有默認(rèn)情況時,編譯器報錯提示無法找到原因,還提示讓我提交 bug 的錯誤信息。直播開始前一直沒有找到原因,重新敲了一遍才解決,所以心里沒底,直播開始時還一直擔(dān)心這個問題會重現(xiàn)。直播時在寫到這段時果然編譯器錯誤又出現(xiàn)了,當(dāng)時我腦袋一片空白,心中大呼救我。好在沒多一會我突然發(fā)現(xiàn)先前一段演示的 placeholder 接口沒有刪掉,原因真的就是這個,刪掉后就正常了,別提有多開心了。后面就輕松了很多。由于只有一天時間準(zhǔn)備,很多內(nèi)容準(zhǔn)備了,當(dāng)時一邊敲代碼一邊說也漏說了很多,比如 AsyncImage 使用的是 URLSession,用的是 URLCache,還不能自定義緩存。Refreshable 只能用在 List 里。SwiftUI 和數(shù)組綁定的代碼是可以兼容前一個版本的。
另外還有個 WWDC 期間很火的老系統(tǒng)UI挑戰(zhàn)賽讓我印象深刻,其中有個18歲小伙用 SwiftUI 開發(fā)了經(jīng)典 iPhone4可用版本最火爆,Github 地址[在這里](https://github.com/zzanehip/The-OldOS-Project)。
SwitUI 新特性太多了,直播沒提到的還有 task modifier、separator、macOS 上的 table、Canvas、preview in landscape、@FocusState、more button 等等。當(dāng)時直播有回放,可以在[這里看](https://www.bilibili.com/video/BV1H44y167b7)。更完整詳細(xì)介紹建議看前面提到 SwiftUI 相關(guān) Session。
WWDC.playgournd 最后一天直播有場 WWDC21 學(xué)生挑戰(zhàn)賽獲獎?wù)邚堊镶姆窒恚窒砹嗽鯓訙?zhǔn)備挑戰(zhàn)賽的過程,通過詳細(xì)的過程介紹,心得體會,還有思考,讓大家了解到了她的熱情和才華,而且分享的形式和效果非常有新意。最后一場的回放[看這里](https://www.bilibili.com/video/BV1Fq4y1L7od)??赐赀@場后,我打算在19號 SwiftGG 和快手中學(xué)合辦的 WWDC
### WWDC
可想法總是很容易,實踐起來卻又是另一種情況。我對自制幻燈片的初步設(shè)想是第一能夠前后翻頁展示內(nèi)容,第二能夠支持和 Keynote 不一樣的動畫效果和頁面美化,第三能夠直接在幻燈片上進行一些 SwiftUI 功能的交互演示。
接下來就要開始實際去做了,我先拿出上周用鉛筆在 A4 紙畫的人草圖加工來豐富展示,發(fā)現(xiàn)加工的時間來不及了,雖然現(xiàn)在加工速度比以前快了,但是時間太緊,還要寫幻燈片程序呢。SwiftUI 開發(fā)確實快,每個頁面我都寫成一個 View,標(biāo)題、大綱和示意圖的組合我做成了通用 View,通過傳入不同標(biāo)題、大綱數(shù)組和圖片數(shù)組來展示不同頁面的內(nèi)容,定義一個 ObservableObject 的類 GlobalStateInfo 作為 View Model 來存儲需要的狀態(tài)數(shù)據(jù),比如當(dāng)前在哪頁,當(dāng)前文字顏色,當(dāng)前頁背景顏色等,每個 View 里使用 @EnvironmentObject 就可以去獲取和設(shè)置 GlobalStateInfo 了。
關(guān)于為了傳遞數(shù)據(jù),是直接調(diào)用 EnvironmentObject,還是通過子視圖傳遞 ObservedObject,兩種方式哪個更好,在 WWDC21 的 Digital Lounges 里,蘋果工程師的回答是兩者用途不同。當(dāng)大部分 View 都需要用到一些通用數(shù)據(jù)時,推薦使用 EnvironmentObject,因為沒有實際使用 ObservableObject 的 View 不會被與之相關(guān)的代碼搞亂。如果模型不是基于 View 層次結(jié)構(gòu)的對象圖,使用 ObservedObject。另外還有個 Digital Lounges 的問題,是問怎么從舊的 AppDelegate/SceneDelegate 生命周期轉(zhuǎn)換到新的 SwiftUI 2 生命周期。蘋果工程師說可以使用 UIApplicationDelegateAdaptor 屬性包裝器,SwiftUI 將實例化你的 UIApplicationDelegate 的一個實例,并以正常方式調(diào)用它。更詳細(xì)的解答和其他的話題可以參看這篇[SwiftUI Lounge QAs](https://roblack.github.io/WWDC21Lounges/),內(nèi)容都是 roblack 從 Digital Lounges 里摘出來的,WWDC21 那幾天我也在 Digital Lounges(報名早)看大家和蘋果工程師的互動,后來看別人說 Digital Lounges 的 SwiftUI 那場爆滿,已經(jīng)超負(fù)荷運轉(zhuǎn)了,感覺蘋果最近變得更開放了,很多蘋果工程師都開通了 Twitter 賬號在 WWDC 期間積極和大家互動。
為了使頁面不單調(diào),我打算每頁大綱的顏色做些區(qū)分,發(fā)現(xiàn)11頁每個都配一遍看效果時間太緊,于是我選擇了一些背景色通過隨機讀取,每次看到的顏色都是不同的,由于都是一個一個手動選出來的,所以不同組合效果也不會太差。
現(xiàn)在前后翻頁展示內(nèi)容這個想法是完成了,這也是 SwiftUI 開發(fā)的優(yōu)勢,能夠快速構(gòu)建頁面架子和簡單的數(shù)據(jù)頁面同步設(shè)置。但是第二個想法,完成起來就非常費時費力且不那么順利了。
首先說下字體,系統(tǒng)默認(rèn)字體很正式,以往我都是直接用 iPad 手寫,但是這次時間緊沒法一個字一個字的寫了,所以我打算選擇其它字體,View 的 .font 修改器可以選擇其它字體,方法是 .font(Font.custom("font-name", size: 110))。如果直接在Finder里查看字體沒法得到可用的字體名,需要使用 NSFontManager.shared.availableMembers 來獲取可用字體名。
接下來是標(biāo)題,以往做幻燈,經(jīng)常講到具體內(nèi)容時,特別是細(xì)節(jié)時,容易讓看的人忘記當(dāng)前頁主題是啥。如果標(biāo)題太大,可展示內(nèi)容就少了,及時這樣,觀看的人也容易忽視主題。因此,我打算把標(biāo)題做成一個循環(huán)的動畫,這樣就可以在我展開說內(nèi)容的時候,看的人即使走神了還能夠注意到當(dāng)前頁主題。標(biāo)題的動畫主要是控制好動畫的時間,不能太快,不然會過于吸引注意。
以前 keynote 的轉(zhuǎn)場動畫我基本都試過,每次來回都是那些,很難和其他人做出差異來。只能靠圖和配色作區(qū)分。這次我利用每頁的內(nèi)容大綱進入效果來作為轉(zhuǎn)場動畫。我先將大綱列表放到 VStack 里,F(xiàn)orEach 里獲取到下標(biāo),通過下標(biāo)獲取列表數(shù)組里的 Text View。之所有要得到下標(biāo)而不是直接獲取列表數(shù)組里的 Text View,其原因是還會將這個下標(biāo)用在轉(zhuǎn)場動畫效果上,我希望大綱列表的內(nèi)容是一個接一個進來的,需要這個下標(biāo)值來做時間間隔。Animation 的效果使用的是 interpolatingSpring,我將 damping 參數(shù)設(shè)置為0.3,這樣彈性效果更佳。列表內(nèi)容進入的是 GeometryEffect 協(xié)議,用來替代 AnimatableModifier,通過 AnimatablePair 來設(shè)置移動位置新舊值。直接一個方塊滑入略顯單調(diào),使用 CGAffineTransform 里的 c 參數(shù)可以設(shè)置將矩形進行變形,會有一種被拉進來的感覺。變形過程配合滑入動畫再加上 interpolatingSpring 設(shè)置的彈性效果,會讓轉(zhuǎn)場更有動感。
并行執(zhí)行的動畫越豐富,轉(zhuǎn)場感覺就會更好,我想著每頁都做個不同的效果,使用 Shape 繪制一些圖形做背景動畫,這樣會有新鮮感。當(dāng)?shù)谝豁摵偷诙撆旰笠呀?jīng)天亮了,經(jīng)過一個上午,下午就要分享了。我還沒有困意,因為后面還有那么多頁面沒有做完區(qū)分轉(zhuǎn)場的動畫和配色,更別說 SwiftUI 功能的交互演示了。而且具體分享的內(nèi)容我還沒有整體串一遍邏輯。一天一夜完成這個項目時間還是太緊,當(dāng)時想著要再能多一天時間就好了。2點開始分享,1點我在旁邊一個小會議室把整個內(nèi)容自己在心里試著說了一遍。分享內(nèi)容包括了自制低版本兼容 AsyncImage 演示、SwiftUI 那些版本兼容問題、SwiftUI 背后關(guān)鍵技術(shù)簡介、SwiftUI 生命周期、布局、Modifier、不透明返回類型、屬性包裝、Result Builder、Geometry、Preview用的技術(shù)。
其中 SwiftUI 內(nèi)部運作的機制是每個 View 都有自己的 Identity,SwiftUI 會將給 State 和 StateObject 分配內(nèi)存空間的 Storage 和 View 的 Identity 綁定起來,共存亡。當(dāng)相同 Identity 的狀態(tài)數(shù)據(jù)發(fā)生變化了或者和 View 依賴關(guān)系改變了,就會重新建立 View 和 RenderNode 的依賴關(guān)系,他們之間的關(guān)系是圖結(jié)構(gòu),圖結(jié)構(gòu)可以降低依賴關(guān)系檢查復(fù)雜度。最后渲染出來??偟膩碚f SwiftUI 運行原理有三個點最重要,Identifier、生命周期和依賴。視圖的生命周期是 Identifier 來決定的,state 生命周期和視圖的生命周期是相同的。在生命周期中,state 有變化的時候會做diff,diff和渲染效率提升是使用圖型依賴結(jié)構(gòu),只渲染狀態(tài)依賴的視圖,如果按照 UIKit 那樣的樹形結(jié)構(gòu)做diff,效率會特別差。
現(xiàn)在很多常用開源庫都已經(jīng)對SwiftUI做了適配,蘋果公司自己的App,比如天氣、相冊、快捷指令、地圖和相冊都有用到SwiftUI。以下是SwiftUI用到的語法特性:
* ResultBuilder
* ViewBuilder
* Trailing Closure
* Opaque Type
* Inline
* PropertyWrapper
* KeyPath
* DynamicMemberLookup
如果你使用這些特性也能夠再造一個兼容低版本的類似 SwiftUI 的框架。SwiftUI 最顯現(xiàn)的 DSL 技術(shù)使用的就是 ResultBuilder 語法特性,Result Builder的提案[SE-0289](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md) 里有詳細(xì)的描述,通過 Result Builder 下面的方法可以自定義出一個簡潔的 DSL 出來,提高特定業(yè)務(wù)開發(fā)效率。
* buildBlock:構(gòu)建基本語句的block組合結(jié)果。
* buildExpression:可選,給表達(dá)式提供上下文類型信息。
* buildOptional:對沒有else的if語句支持。
* buildEither:構(gòu)建選擇語句不同結(jié)果。通過條件結(jié)果折疊成一個結(jié)果,實現(xiàn)對if-else和switch語句的支持。
* buildArray:將所有迭代結(jié)果合并成一個結(jié)果的方式實現(xiàn)對for...in語句的支持。
* buildFinalResult:可選,可以調(diào)用頂層函數(shù)體的結(jié)果進行處理,產(chǎn)生最終的返回結(jié)果。
* buildLimitedAvailability:會在if #available的block部分結(jié)果上調(diào)用,使result builder可以擦除類型信息。
這次的 WWDC 還專門有個 Session 講解了怎么用 Result Builder 來做 DSL,這個 Session 是 [Write a DSL in Swift using result builders](https://developer.apple.com/videos/play/wwdc2021/10253/)。
Swift 視圖返回的類型是不固定的,因此使用了 Swift 的不透明類型語法特性來進行支持,支持其返回帶有大量泛型參數(shù)的龐大類型,這個類型中還包括了 Result Builder 中的 if 條件類型值,支持多分支類型。Opaque Types的提案在這里[SE-0244](https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md)。
跟著視圖后面的點語法是 modifier,每個 modifier 都會在視圖樹中新建一個層,因此 modifier 的寫的先后順序不同,效果是不一樣的。
對于數(shù)據(jù)的監(jiān)聽和響應(yīng)使用的是 swift 里的屬性包裝語法特性,屬性包裝的提案是[SE-0258](https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md)。包裝后數(shù)據(jù)的使用就方便了很多,對于不同屬性包裝類別的選擇可以按照數(shù)據(jù)類型和應(yīng)用場景來,對于值類型,如果是只讀的數(shù)據(jù)可以什么都不加,如果數(shù)據(jù)是可讀寫的,使用@State,如果數(shù)據(jù)是需要在其他視圖進行讀寫并自己也同步響應(yīng)的,使用@Binding進行聲明。對于對象類型的數(shù)據(jù),指向?qū)ο蟮囊媚馨l(fā)生變化要用@ObservedObject來聲明,引用不可改變,那么就用@StateObject,使用環(huán)境傳遞對象用@EnvironmentObject。
完整的 WWDC


下面是當(dāng)時現(xiàn)場演示的部分幻燈片,動畫的效果可以看上面的視頻回放:











至此,這篇WWDC21見聞就寫完了,詳細(xì)描寫自己WWDC21期間的一些獨特經(jīng)歷和其中涉及相關(guān)技術(shù),這樣會讓文章的獨特性和真實感有很大的提升。
## 對獨特性和新意的思考
通篇看下來,你是不是感覺到故事性和真實性其實是非常容易做到的??梢岳斫鉃橹灰π瑫r間再長些,這兩點就能夠完成,且能線性得到提高。只埋頭做事情比較容易和舒適,但一直這么干,熵就會越來越多,不可逆的無用能量無法排除。而獨特性意味著你會去體驗適應(yīng)新的環(huán)境,去獲取實踐新的認(rèn)知,去結(jié)識新的朋友碰撞新的思路,使得自己體驗到不同以往的經(jīng)驗。新意成功幾率很低,非線性的,類似于基因突變產(chǎn)生的進化,這和努力無關(guān)。新意和獨特性一樣屬于逆熵過程,不能忽視,大跨步的進步需要對傳統(tǒng)的顛覆。新意會帶來新的獨特經(jīng)歷形成一個新的循環(huán),不去嘗試就不會有新的機會。
如果把本文當(dāng)成一篇筆記,其間又融入了寫作心得;如果把本文當(dāng)做一篇寫作心得,其間又穿插了大量筆記內(nèi)容。你說這是不是也是一種新意呢。
對于新意,我印象最深的還是權(quán)力的游戲的血色婚禮,神來之筆,當(dāng)Joffrey正最可氣,少狼主正得勢時,劇情完全打破傳統(tǒng),效果非常震撼。凡人皆有一死,凡事皆有可能,于是乎對后面劇情的推進更加期待了。而這個新意是建立在整個劇對真實感上的毫不含糊,包括了扎實的世界觀構(gòu)建,服飾道具高度的還原,完全把觀眾帶入了故事中。另外作者對古歷史的專研和記者經(jīng)歷的結(jié)合產(chǎn)生出的鮮明的人物刻畫和獨特的劇情設(shè)計也是本劇的骨架支撐,獨特性的體現(xiàn)。
## Finally
今天我說的這些心得可以作為下筆“記錄和分享”技術(shù)的一個契機,但是對于自己技術(shù)的成長,寫文章并不是最終的目的,寫作是你對自己思想的研究和開發(fā)。文章的上限是你的技術(shù)能力,文章只是讓人了解你技術(shù)一種手段。因此更重要的是你做的技術(shù)是否有突破有演進,獲得應(yīng)用,并在產(chǎn)品中取得了好的效果。還有那些孤獨著研究技術(shù)的時光,經(jīng)歷著一直努力著奮斗著卻一直不被看見,得不到認(rèn)同,也沒有結(jié)果的歲月,還能夠一直被自己的熱情感動而不放棄去取得一點點進步帶來的滿足感。
