AsyncDisplayKit 是 Facebook 開源的用于保持 iOS 界面流暢的庫。
-
ASDK 的基本原理
ASDK 認(rèn)為,阻塞主線程的任務(wù),主要分為以上三大類,文本和布局的計(jì)算、渲染、解碼、繪制都可以通過各種方式異步執(zhí)行,但 UIKit 和 CoreAnimation 相關(guān)操作必須在主線程執(zhí)行。ASDK 的主要任務(wù),就是將這些任務(wù)從主線程挪走,而挪不走的,就盡量封裝優(yōu)化。
為了達(dá)成這一目標(biāo),ASDK 嘗試對 UIKit 組件進(jìn)行封裝

這是常見的 UIView 和 CALayer 的關(guān)系:UIView 持有 Layer 用于顯示,View 中的大部分顯示屬性實(shí)際上是從 Layer 映射而來的;Layer 的 delegate 是 UIView,當(dāng)其屬性改變、動(dòng)畫產(chǎn)生時(shí),View 能夠得到通知。UIView 和 CALayer 不是線程安全的,并且只能在主線程創(chuàng)建、訪問和銷毀。

ASDK 為此創(chuàng)建了 ASDisplayNode 類,包括了常見屬性(比如 frame/bounds/alpha/transform/backgroundColor/superNode/subNodes)等,然后它用 UIView->CALayer 的方式,實(shí)現(xiàn)了 ASNode->UIView 這樣的一個(gè)關(guān)系。

當(dāng)不需要響應(yīng)觸摸事件時(shí),ASDisplayNode 可以被設(shè)置為 layer backed,即 ASDisplayNode 充當(dāng)了原來的 View 的功能,節(jié)省了更多資源。
與 UIView 和 CALayer 不同,ASDisplayNode 是線程安全的,它可以在后臺(tái)線程創(chuàng)建和修改。Node 剛創(chuàng)建的時(shí)候,并不會(huì)在內(nèi)部新建 UIView 和 CALayer,直到第一次在主線程訪問 UIView 和 CALayer 屬性時(shí),它才會(huì)在內(nèi)部生成相應(yīng)對象。當(dāng)它的屬性(frame/transform)改變后,它并不會(huì)立即同步到它持有的 View 或者 Layer 上,而是把改變的屬性保存到內(nèi)部的一個(gè)中間變量,稍后需要的時(shí)候再通過某個(gè)機(jī)制一次性設(shè)置到內(nèi)部的 View 和 Layer。
ASDK 的圖層預(yù)合成
有時(shí)候一個(gè) Layer 會(huì)包含許多 sub-Layer,而這些 sub-Layer 并不需要響應(yīng)觸摸事件,也不需要進(jìn)行動(dòng)畫和位置調(diào)整。ASDK 為此實(shí)現(xiàn)了一個(gè)叫做 pre-composing 的技術(shù),可以把這些 sub-layer 合成渲染為一張圖片。開發(fā)時(shí),ASNode 已經(jīng)替代了 UIView 和 CALayer;直接使用各種 Node 并設(shè)置為 layer backed 后,ASNode 甚至可以使用預(yù)合成來避免創(chuàng)建內(nèi)部的 UIView 和 CALayer。
通過這種方式,把一個(gè)大的層級,通過一個(gè)大的繪制方法繪制到一張圖上,性能會(huì)獲得很大提升。CPU 也避免了創(chuàng)建 UIKit 對象的資源消耗,GPU 避免了多張 Texture 合成和渲染的消耗,更少的 bitmap 也意味著更少的內(nèi)存占用。ASDK 異步并行開發(fā)
自4S 開始,蘋果移動(dòng)設(shè)備都已經(jīng)是雙核 CPU 以上 ,充分利用多核的優(yōu)勢、并發(fā)執(zhí)行任務(wù)對保持界面流暢有很大作用。ASDK 把布局計(jì)算、文本排版、圖片/文本/圖形渲染等操作都封裝成較小的任務(wù),并利用 GCD 異步并發(fā)執(zhí)行。如果開發(fā)者使用了 ASNode 相關(guān)的控件,那么這些并發(fā)操作會(huì)自動(dòng)在后臺(tái)進(jìn)行,無需進(jìn)行過多配置。-
Runloop 任務(wù)分發(fā)
Runloop Work Distribution 是 ASDK 一個(gè)比較核心的技術(shù)。ASDK 的介紹視頻和文檔中都沒有詳細(xì)介紹,但是網(wǎng)上關(guān)于 Runloop 的博客很多,在這里無需贅述。
iOS 的顯示系統(tǒng)是由 VSync 信號(hào)驅(qū)動(dòng)的,VSync 由硬件時(shí)鐘生成,每秒發(fā)出60次(這個(gè)值取決于設(shè)備,iPhone 上通常是 59.97 次)。iOS 圖形服務(wù)收到 VSync 信號(hào)后,會(huì)通過 IPC 通知的 App 內(nèi),App 的 Runloop 在啟動(dòng)后會(huì)注冊對應(yīng)的 CFRunloopSource 通過 math_port 傳過來的時(shí)鐘信號(hào)通知,隨后 Source 的回調(diào)會(huì)驅(qū)動(dòng)整個(gè) App 的動(dòng)畫與現(xiàn)實(shí)。
Core Animation 在 Runloop 中注冊了一個(gè) Observer,監(jiān)聽了 BeforeWaiting 和 Exit 事件,這個(gè) Observer 的優(yōu)先級是 200 0000,低于其他常見的 Observer。當(dāng)一個(gè)觸發(fā)事件到來時(shí),Runloop 被喚醒,App 中的代碼會(huì)執(zhí)行一些操作,比如創(chuàng)建和調(diào)整視圖層級
設(shè)置 UIView 的 frame、修改 CALayer 的透明度、為視圖添加一些動(dòng)畫;這些操作最終會(huì)被 CALayer 捕獲,并通過 CATransaction 提交到一個(gè)中間狀態(tài)去(CATransaction 的文檔中有提到這寫內(nèi)容,但并不完整)。當(dāng)上面的所有操作結(jié)束后,Runloop 即將進(jìn)入休眠或退出時(shí),關(guān)注該事件的 Observer 都會(huì)得到通知,這時(shí) CA 注冊的那個(gè) Observer 就會(huì)在回調(diào)中把所有的中間狀態(tài)合并提交到 GPU 去顯示;如果此處有動(dòng)畫,CA 會(huì)通過 DisplayLink 等機(jī)制多次觸發(fā)相關(guān)流程。
ASDK 在此處模擬了 CoreAnimation 的這個(gè)機(jī)制,所有針對 ASNode 的修改和提交,總有些任務(wù)必須放到主線程中去執(zhí)行的。當(dāng)出現(xiàn)這種任務(wù)的時(shí)候,ASNode 會(huì)把任務(wù)用 ASASyncTransaction(Group)封裝并提交到一個(gè)全新容器中去。ASDK 也在 Runloop 中注冊了一個(gè)Observer,監(jiān)聽的事件和CA一樣,但是優(yōu)先級比 CA 要低。在 Runloop 進(jìn)入休眠前,CA 處理完事件后,ASDK 就會(huì)執(zhí)行該 loop 內(nèi)提交的所有任務(wù)。具體代碼見ASAsyncTransactionGroup。
通過這種機(jī)制,ASDK 可以在合適的機(jī)會(huì)把同步、異步的操作同步到主線程中去,并且能獲得不錯(cuò)的性能。
- 其他
ASDK 中還封裝了許多高級的功能,比如滑動(dòng)列表的預(yù)加載、v2.0 添加新的布局模式等。
ASDK 是一個(gè)很龐大的庫,它本身并不推薦你將整個(gè) App 全部改為 ASDK 驅(qū)動(dòng),把最需要提升交互性能的地方用 ASDK 進(jìn)行優(yōu)化就足夠了。

