AsyncDisplayKit 是 Facebook 開源的用于保持 iOS 界面流暢的庫。
-
ASDK 的基本原理
ASDK 認為缕粹,阻塞主線程的任務,主要分為以上三大類纸淮,文本和布局的計算平斩、渲染、解碼咽块、繪制都可以通過各種方式異步執(zhí)行绘面,但 UIKit 和 CoreAnimation 相關操作必須在主線程執(zhí)行。ASDK 的主要任務侈沪,就是將這些任務從主線程挪走揭璃,而挪不走的,就盡量封裝優(yōu)化亭罪。
為了達成這一目標瘦馍,ASDK 嘗試對 UIKit 組件進行封裝
這是常見的 UIView 和 CALayer 的關系:UIView 持有 Layer 用于顯示,View 中的大部分顯示屬性實際上是從 Layer 映射而來的应役;Layer 的 delegate 是 UIView情组,當其屬性改變、動畫產生時箩祥,View 能夠得到通知院崇。UIView 和 CALayer 不是線程安全的,并且只能在主線程創(chuàng)建袍祖、訪問和銷毀底瓣。
![](http://upload-images.jianshu.io/upload_images/4653622-ace8647c111ee2d1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/500)
ASDK 為此創(chuàng)建了 ASDisplayNode 類,包括了常見屬性(比如 frame/bounds/alpha/transform/backgroundColor/superNode/subNodes)等蕉陋,然后它用 UIView->CALayer 的方式捐凭,實現了 ASNode->UIView 這樣的一個關系。
當不需要響應觸摸事件時寺滚,ASDisplayNode 可以被設置為 layer backed,即 ASDisplayNode 充當了原來的 View 的功能柑营,節(jié)省了更多資源。
與 UIView 和 CALayer 不同村视,ASDisplayNode 是線程安全的官套,它可以在后臺線程創(chuàng)建和修改。Node 剛創(chuàng)建的時候蚁孔,并不會在內部新建 UIView 和 CALayer奶赔,直到第一次在主線程訪問 UIView 和 CALayer 屬性時,它才會在內部生成相應對象杠氢。當它的屬性(frame/transform)改變后站刑,它并不會立即同步到它持有的 View 或者 Layer 上,而是把改變的屬性保存到內部的一個中間變量鼻百,稍后需要的時候再通過某個機制一次性設置到內部的 View 和 Layer绞旅。
ASDK 的圖層預合成
有時候一個 Layer 會包含許多 sub-Layer摆尝,而這些 sub-Layer 并不需要響應觸摸事件,也不需要進行動畫和位置調整因悲。ASDK 為此實現了一個叫做 pre-composing 的技術堕汞,可以把這些 sub-layer 合成渲染為一張圖片。開發(fā)時晃琳,ASNode 已經替代了 UIView 和 CALayer讯检;直接使用各種 Node 并設置為 layer backed 后,ASNode 甚至可以使用預合成來避免創(chuàng)建內部的 UIView 和 CALayer卫旱。
通過這種方式人灼,把一個大的層級,通過一個大的繪制方法繪制到一張圖上顾翼,性能會獲得很大提升投放。CPU 也避免了創(chuàng)建 UIKit 對象的資源消耗,GPU 避免了多張 Texture 合成和渲染的消耗适贸,更少的 bitmap 也意味著更少的內存占用跪呈。ASDK 異步并行開發(fā)
自4S 開始,蘋果移動設備都已經是雙核 CPU 以上 取逾,充分利用多核的優(yōu)勢耗绿、并發(fā)執(zhí)行任務對保持界面流暢有很大作用。ASDK 把布局計算砾隅、文本排版误阻、圖片/文本/圖形渲染等操作都封裝成較小的任務,并利用 GCD 異步并發(fā)執(zhí)行晴埂。如果開發(fā)者使用了 ASNode 相關的控件究反,那么這些并發(fā)操作會自動在后臺進行,無需進行過多配置儒洛。-
Runloop 任務分發(fā)
Runloop Work Distribution 是 ASDK 一個比較核心的技術精耐。ASDK 的介紹視頻和文檔中都沒有詳細介紹,但是網上關于 Runloop 的博客很多琅锻,在這里無需贅述卦停。
iOS 的顯示系統是由 VSync 信號驅動的,VSync 由硬件時鐘生成恼蓬,每秒發(fā)出60次(這個值取決于設備惊完,iPhone 上通常是 59.97 次)。iOS 圖形服務收到 VSync 信號后处硬,會通過 IPC 通知的 App 內小槐,App 的 Runloop 在啟動后會注冊對應的 CFRunloopSource 通過 math_port 傳過來的時鐘信號通知,隨后 Source 的回調會驅動整個 App 的動畫與現實荷辕。
Core Animation 在 Runloop 中注冊了一個 Observer凿跳,監(jiān)聽了 BeforeWaiting 和 Exit 事件件豌,這個 Observer 的優(yōu)先級是 200 0000,低于其他常見的 Observer控嗜。當一個觸發(fā)事件到來時苟径,Runloop 被喚醒,App 中的代碼會執(zhí)行一些操作躬审,比如創(chuàng)建和調整視圖層級
設置 UIView 的 frame、修改 CALayer 的透明度蟆盐、為視圖添加一些動畫承边;這些操作最終會被 CALayer 捕獲,并通過 CATransaction 提交到一個中間狀態(tài)去(CATransaction 的文檔中有提到這寫內容石挂,但并不完整)博助。當上面的所有操作結束后,Runloop 即將進入休眠或退出時痹愚,關注該事件的 Observer 都會得到通知富岳,這時 CA 注冊的那個 Observer 就會在回調中把所有的中間狀態(tài)合并提交到 GPU 去顯示;如果此處有動畫拯腮,CA 會通過 DisplayLink 等機制多次觸發(fā)相關流程窖式。
ASDK 在此處模擬了 CoreAnimation 的這個機制,所有針對 ASNode 的修改和提交动壤,總有些任務必須放到主線程中去執(zhí)行的萝喘。當出現這種任務的時候,ASNode 會把任務用 ASASyncTransaction(Group)封裝并提交到一個全新容器中去琼懊。ASDK 也在 Runloop 中注冊了一個Observer阁簸,監(jiān)聽的事件和CA一樣,但是優(yōu)先級比 CA 要低哼丈。在 Runloop 進入休眠前启妹,CA 處理完事件后,ASDK 就會執(zhí)行該 loop 內提交的所有任務醉旦。具體代碼見ASAsyncTransactionGroup饶米。
通過這種機制,ASDK 可以在合適的機會把同步车胡、異步的操作同步到主線程中去咙崎,并且能獲得不錯的性能。
- 其他
ASDK 中還封裝了許多高級的功能吨拍,比如滑動列表的預加載褪猛、v2.0 添加新的布局模式等。
ASDK 是一個很龐大的庫羹饰,它本身并不推薦你將整個 App 全部改為 ASDK 驅動伊滋,把最需要提升交互性能的地方用 ASDK 進行優(yōu)化就足夠了碳却。