AsyncDisplayKit

AsyncDisplayKit

AsyncDisplayKit 是 Facebook 開源的一個(gè)用于保持 iOS 界面流暢的庫(kù)采呐,我從中學(xué)到了很多東西蹬叭,所以下面我會(huì)花較大的篇幅來(lái)對(duì)其進(jìn)行介紹和分析服球。

ASDK 的由來(lái)

ASDK 的作者是 Scott Goodson (Linkedin)挖诸,
他曾經(jīng)在蘋果工作任柜,負(fù)責(zé) iOS 的一些內(nèi)置應(yīng)用的開發(fā),比如股票吨灭、計(jì)算器刚照、地圖、鐘表沃于、設(shè)置涩咖、Safari 等海诲,當(dāng)然他也參與了 UIKit framework 的開發(fā)繁莹。后來(lái)他加入 Facebook 后,負(fù)責(zé) Paper 的開發(fā)特幔,創(chuàng)建并開源了 AsyncDisplayKit咨演。目前他在 Pinterest 和 Instagram 負(fù)責(zé) iOS 開發(fā)和用戶體驗(yàn)的提升等工作。

image.png

ASDK 自 2014 年 6 月開源蚯斯,10 月發(fā)布 1.0 版薄风。目前 ASDK 即將要發(fā)布 2.0 版。
V2.0 增加了更多布局相關(guān)的代碼拍嵌,ComponentKit 團(tuán)隊(duì)為此貢獻(xiàn)很多遭赂。
現(xiàn)在 Github 的 master 分支上的版本是 V1.9.1,已經(jīng)包含了 V2.0 的全部?jī)?nèi)容横辆。

ASDK 的資料

想要了解 ASDK 的原理和細(xì)節(jié)撇他,最好從下面幾個(gè)視頻開始:
2014.10.15 NSLondon – Scott Goodson – Behind AsyncDisplayKit
2015.03.02 MCE 2015 – Scott Goodson – Effortless Responsiveness with AsyncDisplayKit
2015.10.25 AsyncDisplayKit 2.0: Intelligent User Interfaces – NSSpain 2015
前兩個(gè)視頻內(nèi)容大同小異,都是介紹 ASDK 的基本原理狈蚤,附帶介紹 POP 等其他項(xiàng)目困肩。
后一個(gè)視頻增加了 ASDK 2.0 的新特性的介紹。

除此之外脆侮,還可以到 Github Issues 里看一下 ASDK 相關(guān)的討論锌畸,下面是幾個(gè)比較重要的內(nèi)容:
關(guān)于 Runloop Dispatch
關(guān)于 ComponentKit 和 ASDK 的區(qū)別
為什么不支持 Storyboard 和 Autolayout
如何評(píng)測(cè)界面的流暢度

之后,還可以到 Google Groups 來(lái)查看和討論更多內(nèi)容:
https://groups.google.com/forum/#!forum/asyncdisplaykit

ASDK 的基本原理
image.png

ASDK 認(rèn)為靖避,阻塞主線程的任務(wù)潭枣,主要分為上面這三大類比默。文本和布局的計(jì)算、渲染卸耘、解碼退敦、繪制都可以通過各種方式異步執(zhí)行,但 UIKit 和 Core Animation 相關(guān)操作必需在主線程進(jìn)行蚣抗。ASDK 的目標(biāo)侈百,就是盡量把這些任務(wù)從主線程挪走,而挪不走的翰铡,就盡量?jī)?yōu)化性能钝域。

為了達(dá)成這一目標(biāo),ASDK 嘗試對(duì) UIKit 組件進(jìn)行封裝:


image.png

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


image.png

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


image.png

當(dāng)不需要響應(yīng)觸摸事件時(shí)艇搀,ASDisplayNode 可以被設(shè)置為 layer backed,即 ASDisplayNode 充當(dāng)了原來(lái) UIView 的功能求晶,節(jié)省了更多資源焰雕。

與 UIView 和 CALayer 不同,ASDisplayNode 是線程安全的芳杏,它可以在后臺(tái)線程創(chuàng)建和修改矩屁。Node 剛創(chuàng)建時(shí),并不會(huì)在內(nèi)部新建 UIView 和 CALayer蚜锨,直到第一次在主線程訪問 view 或 layer 屬性時(shí)档插,它才會(huì)在內(nèi)部生成對(duì)應(yīng)的對(duì)象。當(dāng)它的屬性(比如frame/transform)改變后亚再,它并不會(huì)立刻同步到其持有的 view 或 layer 去郭膛,而是把被改變的屬性保存到內(nèi)部的一個(gè)中間變量,稍后在需要時(shí)氛悬,再通過某個(gè)機(jī)制一次性設(shè)置到內(nèi)部的 view 或 layer则剃。

通過模擬和封裝 UIView/CALayer耘柱,開發(fā)者可以把代碼中的 UIView 替換為 ASNode,很大的降低了開發(fā)和學(xué)習(xí)成本棍现,同時(shí)能獲得 ASDK 底層大量的性能優(yōu)化调煎。為了方便使用, ASDK 把大量常用控件都封裝成了 ASNode 的子類己肮,比如 Button士袄、Control、Cell谎僻、Image娄柳、ImageView、Text艘绍、TableView赤拒、CollectionView 等。利用這些控件诱鞠,開發(fā)者可以盡量避免直接使用 UIKit 相關(guān)控件挎挖,以獲得更完整的性能提升。

ASDK 的圖層預(yù)合成
image.png

有時(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ù)合成來(lái)避免創(chuàng)建內(nèi)部的 UIView 和 CALayer帝雇。

通過這種方式涮俄,把一個(gè)大的層級(jí),通過一個(gè)大的繪制方法繪制到一張圖上尸闸,性能會(huì)獲得很大提升彻亲。CPU 避免了創(chuàng)建 UIKit 對(duì)象的資源消耗,GPU 避免了多張 texture 合成和渲染的消耗吮廉,更少的 bitmap 也意味著更少的內(nèi)存占用苞尝。

ASDK 異步并發(fā)操作
image.png

自 iPhone 4S 起,iDevice 已經(jīng)都是雙核 CPU 了宦芦,現(xiàn)在的 iPad 甚至已經(jīng)更新到 3 核了宙址。充分利用多核的優(yōu)勢(shì)、并發(fā)執(zhí)行任務(wù)對(duì)保持界面流暢有很大作用调卑。ASDK 把布局計(jì)算抡砂、文本排版大咱、圖片/文本/圖形渲染等操作都封裝成較小的任務(wù),并利用 GCD 異步并發(fā)執(zhí)行注益。如果開發(fā)者使用了 ASNode 相關(guān)的控件碴巾,那么這些并發(fā)操作會(huì)自動(dòng)在后臺(tái)進(jìn)行,無(wú)需進(jìn)行過多配置丑搔。

Runloop 任務(wù)分發(fā)

Runloop work distribution 是 ASDK 比較核心的一個(gè)技術(shù)厦瓢,ASDK 的介紹視頻和文檔中都沒有詳細(xì)展開介紹,所以這里我會(huì)多做一些分析啤月。如果你對(duì) Runloop 還不太了解旷痕,可以看一下我之前的文章深入理解RunLoop,里面對(duì) ASDK 也有所提及顽冶。

iOS 的顯示系統(tǒng)是由 VSync 信號(hào)驅(qū)動(dòng)的欺抗,VSync 信號(hào)由硬件時(shí)鐘生成,每秒鐘發(fā)出 60 次(這個(gè)值取決設(shè)備硬件强重,比如 iPhone 真機(jī)上通常是 59.97)绞呈。iOS 圖形服務(wù)接收到 VSync 信號(hào)后,會(huì)通過 IPC 通知到 App 內(nèi)间景。App 的 Runloop 在啟動(dòng)后會(huì)注冊(cè)對(duì)應(yīng)的 CFRunLoopSource 通過 mach_port 接收傳過來(lái)的時(shí)鐘信號(hào)通知佃声,隨后 Source 的回調(diào)會(huì)驅(qū)動(dòng)整個(gè) App 的動(dòng)畫與顯示。

Core Animation 在 RunLoop 中注冊(cè)了一個(gè) Observer倘要,監(jiān)聽了 BeforeWaiting 和 Exit 事件圾亏。這個(gè) Observer 的優(yōu)先級(jí)是 2000000,低于常見的其他 Observer封拧。當(dāng)一個(gè)觸摸事件到來(lái)時(shí)志鹃,RunLoop 被喚醒,App 中的代碼會(huì)執(zhí)行一些操作泽西,比如創(chuàng)建和調(diào)整視圖層級(jí)曹铃、設(shè)置 UIView 的 frame、修改 CALayer 的透明度捧杉、為視圖添加一個(gè)動(dòng)畫陕见;這些操作最終都會(huì)被 CALayer 捕獲,并通過 CATransaction 提交到一個(gè)中間狀態(tài)去(CATransaction 的文檔略有提到這些內(nèi)容味抖,但并不完整)评甜。當(dāng)上面所有操作結(jié)束后,RunLoop 即將進(jìn)入休眠(或者退出)時(shí)仔涩,關(guān)注該事件的 Observer 都會(huì)得到通知忍坷。這時(shí) CA 注冊(cè)的那個(gè) Observer 就會(huì)在回調(diào)中,把所有的中間狀態(tài)合并提交到 GPU 去顯示;如果此處有動(dòng)畫承匣,CA 會(huì)通過 DisplayLink 等機(jī)制多次觸發(fā)相關(guān)流程蓖乘。

ASDK 在此處模擬了 Core Animation 的這個(gè)機(jī)制:所有針對(duì) ASNode 的修改和提交,總有些任務(wù)是必需放入主線程執(zhí)行的韧骗。當(dāng)出現(xiàn)這種任務(wù)時(shí)嘉抒,ASNode 會(huì)把任務(wù)用 ASAsyncTransaction(Group) 封裝并提交到一個(gè)全局的容器去。ASDK 也在 RunLoop 中注冊(cè)了一個(gè) Observer袍暴,監(jiān)視的事件和 CA 一樣些侍,但優(yōu)先級(jí)比 CA 要低。當(dāng) RunLoop 進(jìn)入休眠前政模、CA 處理完事件后岗宣,ASDK 就會(huì)執(zhí)行該 loop 內(nèi)提交的所有任務(wù)。具體代碼見這個(gè)文件:ASAsyncTransactionGroup淋样。

通過這種機(jī)制耗式,ASDK 可以在合適的機(jī)會(huì)把異步、并發(fā)的操作同步到主線程去趁猴,并且能獲得不錯(cuò)的性能刊咳。

其他

ASDK 中還有封裝很多高級(jí)的功能,比如滑動(dòng)列表的預(yù)加載儡司、V2.0添加的新的布局模式等娱挨。ASDK 是一個(gè)很龐大的庫(kù),它本身并不推薦你把整個(gè) App 全部都改為 ASDK 驅(qū)動(dòng)捕犬,把最需要提升交互性能的地方用 ASDK 進(jìn)行優(yōu)化就足夠了跷坝。

參考文章:
使用 ASDK 性能調(diào)優(yōu) - 提升 iOS 界面的渲染性能

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市碉碉,隨后出現(xiàn)的幾起案子柴钻,更是在濱河造成了極大的恐慌,老刑警劉巖誉裆,帶你破解...
    沈念sama閱讀 194,390評(píng)論 5 459
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件顿颅,死亡現(xiàn)場(chǎng)離奇詭異缸濒,居然都是意外死亡足丢,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 81,821評(píng)論 2 371
  • 文/潘曉璐 我一進(jìn)店門庇配,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)斩跌,“玉大人,你說(shuō)我怎么就攤上這事捞慌∫唬” “怎么了?”我有些...
    開封第一講書人閱讀 141,632評(píng)論 0 319
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)袖订。 經(jīng)常有香客問我氮帐,道長(zhǎng),這世上最難降的妖魔是什么洛姑? 我笑而不...
    開封第一講書人閱讀 52,170評(píng)論 1 263
  • 正文 為了忘掉前任上沐,我火速辦了婚禮,結(jié)果婚禮上楞艾,老公的妹妹穿的比我還像新娘参咙。我一直安慰自己,他們只是感情好硫眯,可當(dāng)我...
    茶點(diǎn)故事閱讀 61,033評(píng)論 4 355
  • 文/花漫 我一把揭開白布蕴侧。 她就那樣靜靜地躺著,像睡著了一般两入。 火紅的嫁衣襯著肌膚如雪净宵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 46,098評(píng)論 1 272
  • 那天裹纳,我揣著相機(jī)與錄音塘娶,去河邊找鬼。 笑死痊夭,一個(gè)胖子當(dāng)著我的面吹牛刁岸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播她我,決...
    沈念sama閱讀 36,511評(píng)論 3 381
  • 文/蒼蘭香墨 我猛地睜開眼虹曙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了番舆?” 一聲冷哼從身側(cè)響起酝碳,我...
    開封第一講書人閱讀 35,204評(píng)論 0 253
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恨狈,沒想到半個(gè)月后疏哗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 39,479評(píng)論 1 290
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡禾怠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 34,572評(píng)論 2 309
  • 正文 我和宋清朗相戀三年返奉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吗氏。...
    茶點(diǎn)故事閱讀 36,341評(píng)論 1 326
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡芽偏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出弦讽,到底是詐尸還是另有隱情污尉,我是刑警寧澤,帶...
    沈念sama閱讀 32,213評(píng)論 3 312
  • 正文 年R本政府宣布,位于F島的核電站被碗,受9級(jí)特大地震影響某宪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锐朴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 37,576評(píng)論 3 298
  • 文/蒙蒙 一缩抡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧包颁,春花似錦瞻想、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 28,893評(píng)論 0 17
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至岳悟,卻和暖如春佃迄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贵少。 一陣腳步聲響...
    開封第一講書人閱讀 30,171評(píng)論 1 250
  • 我被黑心中介騙來(lái)泰國(guó)打工呵俏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人滔灶。 一個(gè)月前我還...
    沈念sama閱讀 41,486評(píng)論 2 341
  • 正文 我出身青樓普碎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親录平。 傳聞我的和親對(duì)象是個(gè)殘疾皇子麻车,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 40,676評(píng)論 2 335

推薦閱讀更多精彩內(nèi)容