簡介
AsyncDisplayKit(簡稱 ASDK)是由 Facebook 開源的一個 iOS 框架,能夠幫助最復雜的 UI 界面保持流暢和快速響應。
在 ASDK 中最基本的單位就是 ASDisplayNode
,每一個 node
都是對 UIView
以及CALayer
的抽象。但是與 UIView
不同的是,ASDisplayNode
是線程安全的,它可以在后臺線程中完成初始化以及配置工作。
如果按照 60 FPS 的刷新頻率來計算,每一幀的渲染時間只有 16ms,在 16ms 的時間內要完成對 UIView
的創建、布局、繪制以及渲染,CPU 和 GPU 面臨著巨大的壓力。
但是從 A5 處理器之后,多核的設備成為了主流,原有的將所有操作放入主線程的實踐已經不能適應復雜的 UI 界面,所以 ASDK 將耗時的 CPU 操作以及 GPU 渲染紋理(Texture)的過程全部放入后臺進程,使主線程能夠快速響應用戶操作。
ASDK 通過獨特的渲染技巧、代替 AutoLayout 的布局系統、智能的預加載方式等模塊來實現對 App 性能的優化。
初步使用
節點
nodes
的使用大致與view
相同,少數像(.clipsToBounds vs .masksToBounds)這樣的方法上的不同node
也會自動使用UIView
的命名,唯一的例外是node使用position
而不是center
。
使用node
的時候也可以隨時在主線程拿到node.view
或者node.layer
。
AsyncDisplayKit提供了各種各樣的node
以代替UIKit
中的大部分組件。
節點容器
當一個app轉成使用AsyncDisplayKit時,一個常見的錯誤就是直接把nodes
添加到現有的視圖層級中,這樣做會造成你的node渲染時屏閃。
正確的做法是將nodes作為subnodes
添加到node container classes
中,這些容器的作用是告訴容器內的nodes它們現在的狀態,這樣才能保證高效地進行數據的加載和nodes的渲染。
布局引擎
AsyncDisplayKit
的Layout Engine
也是它最強大和最獨特的特性之一。基于CSS FlexBox model
它提供了一種公開的方式來區分用戶自定義的node
的subnodes
的size和layout。當所有的node全部以默認的同步方式渲染完畢,尺寸設置和布局計算將會以異步的方式進行。
想要參與這個過程的主要方式就是通過在子類中實現layoutSpecThatFits:方法,在這里你聲明和建立布局規則對象,返回最重的包含所有信息的對象。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStack.direction = ASStackLayoutDirectionVertical;
verticalStack.spacing = 4.0;
[verticalStack setChildren:_commentNodes];
return verticalStack;
}
核心理念
智能預加載
node
非常強大,它有能力同步和異常進行渲染和計算,另一個至關重要的層面則是它智能預加載的理念。
同之前所說,node在node containers
以外的地方使用并沒有什么性能改善作用。那是因為所有的node都有interfaceState
的概念。
這個interfaceState
特性是被所有的containers
創建和維持的一個ASRangeController
進行持續更新的。
如果一個node
在container
之外使用,它的狀態就不會被任何range controller
更新,這就會導致偶爾會出現屏閃(nodes
在沒有發出任何警告的情況下已經顯示在屏幕上,但它的渲染和布局還沒有完成)
當nodes被添加到滾動或者頁面視圖的時候,它們通常會進入下圖的區域范圍內。這就意味著如果這個視圖滾動到它這里了,它的界面狀態就會被更新。

一個node將會有三種狀態:
Fetch Data
Display
Visible
上面的這三種狀態都是由 ASDK 來管理的,而每一個 ASCellNode
的狀態都是由 SRangeController
控制,所有的狀態都對應一個 ASInterfaceState
:
ASInterfaceStatePreload
當前元素貌似要顯示到屏幕上,需要從磁盤或者網絡請求數據;ASInterfaceStateDisplay
當前元素非常可能要變成可見的,需要進行異步繪制;ASInterfaceStateVisible
當前元素最少在屏幕上顯示了 1px
當用戶滾動當前視圖時,ASRangeController
就會修改不同區域內元素的狀態:
上圖是用戶在向下滑動時,ASCellNode
是如何被標記的,假設當前視圖可見的范圍高度為 1,那么在默認情況下,五個區域會按照上圖的形式進行劃分:
在滾動方向(Leading)上 Fetch Data 區域會是非滾動方向(Trailing)的兩倍,ASDK 會根據滾動方向的變化實時改變緩沖區的位置;在向下滾動時,下面的 Fetch Data 區域就是上面的兩倍,向上滾動時,上面的 Fetch Data 區域就是下面的兩倍。
這里的兩倍并不是一個確定的數值,ASDK 會根據當前設備的不同狀態,改變不同區域的大小,但是滾動方向的區域總會比非滾動方向大一些。
智能預加載能夠根據當前的滾動方向,自動改變當前的工作區域,選擇合適的區域提前觸發請求資源、渲染視圖以及異步布局等操作,讓視圖的滾動達到真正的流暢。