核心思路
YYAsyncLayer 是 CALayer 的子類(lèi),當(dāng)它需要顯示內(nèi)容(比如調(diào)用了 [layer setNeedDisplay])時(shí)贷掖,它會(huì)向 delegate刑赶,也就是 UIView 請(qǐng)求一個(gè)異步繪制的任務(wù)锯厢。在異步繪制時(shí),Layer 會(huì)傳遞一個(gè) BOOL(^isCancelled)() 這樣的 block叭爱,繪制代碼可以隨時(shí)調(diào)用該 block 判斷繪制任務(wù)是否已經(jīng)被取消撮躁。
當(dāng) TableView 快速滑動(dòng)時(shí),會(huì)有大量異步繪制任務(wù)提交到后臺(tái)線程去執(zhí)行买雾。但是有時(shí)滑動(dòng)速度過(guò)快時(shí)把曼,繪制任務(wù)還沒(méi)有完成就可能已經(jīng)被取消了。如果這時(shí)仍然繼續(xù)繪制漓穿,就會(huì)造成大量的 CPU 資源浪費(fèi)嗤军,甚至阻塞線程并造成后續(xù)的繪制任務(wù)遲遲無(wú)法完成。YYAsyncLayer的做法是盡量快速晃危、提前判斷當(dāng)前繪制任務(wù)是否已經(jīng)被取消叙赚;在繪制每一行文本前,都會(huì)調(diào)用 isCancelled() 來(lái)進(jìn)行判斷僚饭,保證被取消的任務(wù)能及時(shí)退出震叮,不至于影響后續(xù)操作。
實(shí)現(xiàn)原理
首先介紹里面幾個(gè)類(lèi):
YYAsyncLayer:繼承自CALayer鳍鸵,繪制苇瓣、創(chuàng)建繪制線程的部分都在這個(gè)類(lèi)。
YYTransaction:仿照 QuartzCore/UIKit 框架的模式偿乖,實(shí)現(xiàn)了一套類(lèi)似的界面更新的機(jī)制:即在主線程的 RunLoop 中添加一個(gè) Observer击罪,監(jiān)聽(tīng)了 kCFRunLoopBeforeWaiting 和 kCFRunLoopExit 事件,在收到回調(diào)時(shí)汹想,遍歷所有之前放入隊(duì)列的待處理的任務(wù)外邓,然后一一執(zhí)行撤蚊。
YYSentinel:提供獲取當(dāng)前值的value(只讀)屬性古掏,以及- (int32_t)increase自增加的方法返回一個(gè)新的value值,用于判斷異步繪制任務(wù)是否被取消的工具侦啸。
并發(fā)控制
App 中使用了大量 concurrent queue 來(lái)執(zhí)行較多任務(wù)時(shí)槽唾,App 在同一時(shí)刻就會(huì)存在幾十個(gè)線程同時(shí)運(yùn)行丧枪、創(chuàng)建、銷(xiāo)毀庞萍。CPU 是用時(shí)間片輪轉(zhuǎn)來(lái)實(shí)現(xiàn)線程并發(fā)的拧烦,盡管 concurrent queue 能控制線程的優(yōu)先級(jí),但當(dāng)大量線程同時(shí)創(chuàng)建運(yùn)行銷(xiāo)毀時(shí)钝计,這些操作仍然會(huì)擠占掉主線程的 CPU 資源恋博。
YYAsyncLayer使用YYDispatchQueuePool為不同優(yōu)先級(jí)創(chuàng)建和 CPU 數(shù)量相同的 serial queue,每次從 pool 中獲取 queue 時(shí)私恬,會(huì)輪詢(xún)返回其中一個(gè) queue债沮。我把 App 內(nèi)所有異步操作,包括圖像解碼本鸣、對(duì)象釋放疫衩、異步繪制等,都按優(yōu)先級(jí)不同放入了全局的 serial queue 中執(zhí)行荣德,這樣盡量避免了過(guò)多線程導(dǎo)致的性能問(wèn)題闷煤。
參考地址
https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/
http://ios.jobbole.com/86878/