簡(jiǎn)介
AsyncDisplayKit(簡(jiǎn)稱 ASDK)是由 Facebook 開源的一個(gè) iOS 框架,能夠幫助最復(fù)雜的 UI 界面保持流暢和快速響應(yīng)。
在 ASDK 中最基本的單位就是 ASDisplayNode
改化,每一個(gè) node
都是對(duì) UIView
以及CALayer
的抽象。但是與 UIView
不同的是枉昏,ASDisplayNode
是線程安全的陈肛,它可以在后臺(tái)線程中完成初始化以及配置工作。
如果按照 60 FPS 的刷新頻率來計(jì)算兄裂,每一幀的渲染時(shí)間只有 16ms句旱,在 16ms 的時(shí)間內(nèi)要完成對(duì) UIView
的創(chuàng)建、布局晰奖、繪制以及渲染谈撒,CPU 和 GPU 面臨著巨大的壓力。
但是從 A5 處理器之后匾南,多核的設(shè)備成為了主流啃匿,原有的將所有操作放入主線程的實(shí)踐已經(jīng)不能適應(yīng)復(fù)雜的 UI 界面,所以 ASDK 將耗時(shí)的 CPU 操作以及 GPU 渲染紋理(Texture)的過程全部放入后臺(tái)進(jìn)程蛆楞,使主線程能夠快速響應(yīng)用戶操作溯乒。
ASDK 通過獨(dú)特的渲染技巧、代替 AutoLayout 的布局系統(tǒng)豹爹、智能的預(yù)加載方式等模塊來實(shí)現(xiàn)對(duì) App 性能的優(yōu)化裆悄。
初步使用
節(jié)點(diǎn)
nodes
的使用大致與view
相同,少數(shù)像(.clipsToBounds vs .masksToBounds)這樣的方法上的不同node
也會(huì)自動(dòng)使用UIView
的命名,唯一的例外是node使用position
而不是center
臂聋。
使用node
的時(shí)候也可以隨時(shí)在主線程拿到node.view
或者node.layer
光稼。
AsyncDisplayKit提供了各種各樣的node
以代替UIKit
中的大部分組件。
節(jié)點(diǎn)容器
當(dāng)一個(gè)app轉(zhuǎn)成使用AsyncDisplayKit時(shí)孩等,一個(gè)常見的錯(cuò)誤就是直接把nodes
添加到現(xiàn)有的視圖層級(jí)中艾君,這樣做會(huì)造成你的node渲染時(shí)屏閃。
正確的做法是將nodes作為subnodes
添加到node container classes
中瞎访,這些容器的作用是告訴容器內(nèi)的nodes它們現(xiàn)在的狀態(tài)腻贰,這樣才能保證高效地進(jìn)行數(shù)據(jù)的加載和nodes的渲染。
布局引擎
AsyncDisplayKit
的Layout Engine
也是它最強(qiáng)大和最獨(dú)特的特性之一扒秸〔パ荩基于CSS FlexBox model
它提供了一種公開的方式來區(qū)分用戶自定義的node
的subnodes
的size和layout。當(dāng)所有的node全部以默認(rèn)的同步方式渲染完畢伴奥,尺寸設(shè)置和布局計(jì)算將會(huì)以異步的方式進(jìn)行写烤。
想要參與這個(gè)過程的主要方式就是通過在子類中實(shí)現(xiàn)layoutSpecThatFits:方法,在這里你聲明和建立布局規(guī)則對(duì)象拾徙,返回最重的包含所有信息的對(duì)象洲炊。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStack.direction = ASStackLayoutDirectionVertical;
verticalStack.spacing = 4.0;
[verticalStack setChildren:_commentNodes];
return verticalStack;
}
核心理念
智能預(yù)加載
node
非常強(qiáng)大,它有能力同步和異常進(jìn)行渲染和計(jì)算,另一個(gè)至關(guān)重要的層面則是它智能預(yù)加載的理念暂衡。
同之前所說询微,node在node containers
以外的地方使用并沒有什么性能改善作用。那是因?yàn)樗械膎ode都有interfaceState
的概念狂巢。
這個(gè)interfaceState
特性是被所有的containers
創(chuàng)建和維持的一個(gè)ASRangeController
進(jìn)行持續(xù)更新的撑毛。
如果一個(gè)node
在container
之外使用,它的狀態(tài)就不會(huì)被任何range controller
更新唧领,這就會(huì)導(dǎo)致偶爾會(huì)出現(xiàn)屏閃(nodes
在沒有發(fā)出任何警告的情況下已經(jīng)顯示在屏幕上藻雌,但它的渲染和布局還沒有完成)
當(dāng)nodes被添加到滾動(dòng)或者頁面視圖的時(shí)候,它們通常會(huì)進(jìn)入下圖的區(qū)域范圍內(nèi)斩个。這就意味著如果這個(gè)視圖滾動(dòng)到它這里了胯杭,它的界面狀態(tài)就會(huì)被更新。
![預(yù)加載示意圖](http://img.draveness.me/2016-11-04-intelligent-preloading-ranges-with-names.png)
一個(gè)node將會(huì)有三種狀態(tài):
Fetch Data
Display
Visible
上面的這三種狀態(tài)都是由 ASDK 來管理的受啥,而每一個(gè) ASCellNode
的狀態(tài)都是由 SRangeController
控制做个,所有的狀態(tài)都對(duì)應(yīng)一個(gè) ASInterfaceState
:
ASInterfaceStatePreload
當(dāng)前元素貌似要顯示到屏幕上,需要從磁盤或者網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)腔呜;ASInterfaceStateDisplay
當(dāng)前元素非橙拢可能要變成可見的,需要進(jìn)行異步繪制核畴;ASInterfaceStateVisible
當(dāng)前元素最少在屏幕上顯示了 1px
當(dāng)用戶滾動(dòng)當(dāng)前視圖時(shí)膝但,ASRangeController
就會(huì)修改不同區(qū)域內(nèi)元素的狀態(tài):
上圖是用戶在向下滑動(dòng)時(shí),ASCellNode
是如何被標(biāo)記的谤草,假設(shè)當(dāng)前視圖可見的范圍高度為 1跟束,那么在默認(rèn)情況下,五個(gè)區(qū)域會(huì)按照上圖的形式進(jìn)行劃分:
在滾動(dòng)方向(Leading)上 Fetch Data 區(qū)域會(huì)是非滾動(dòng)方向(Trailing)的兩倍丑孩,ASDK 會(huì)根據(jù)滾動(dòng)方向的變化實(shí)時(shí)改變緩沖區(qū)的位置冀宴;在向下滾動(dòng)時(shí),下面的 Fetch Data 區(qū)域就是上面的兩倍温学,向上滾動(dòng)時(shí)略贮,上面的 Fetch Data 區(qū)域就是下面的兩倍。
這里的兩倍并不是一個(gè)確定的數(shù)值仗岖,ASDK 會(huì)根據(jù)當(dāng)前設(shè)備的不同狀態(tài)逃延,改變不同區(qū)域的大小,但是滾動(dòng)方向的區(qū)域總會(huì)比非滾動(dòng)方向大一些轧拄。
智能預(yù)加載能夠根據(jù)當(dāng)前的滾動(dòng)方向揽祥,自動(dòng)改變當(dāng)前的工作區(qū)域,選擇合適的區(qū)域提前觸發(fā)請(qǐng)求資源檩电、渲染視圖以及異步布局等操作拄丰,讓視圖的滾動(dòng)達(dá)到真正的流暢府树。