方法及關(guān)鍵函數(shù)解釋
static dispatch_queue_t YChartAsyncLayerGetDispalyQueue() {
#define MAX_QUEUE_COUNT 16
static int queueCount;
static dispatch_queue_t queues[MAX_QUEUE_COUNT];
static int32_t counter = 0;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queueCount = (int)[NSProcessInfo processInfo].activeProcessorCount;
queueCount = queueCount < 1 ? 1 : queueCount;
if ( [[UIDevice currentDevice].systemVersion floatValue] >= 8.0 ) {
for (NSUInteger i = 0 ; i < queueCount; i++) {
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0);
queues[i] = dispatch_queue_create("com.yxw.chart.render", attr);
}
}
else{
for (NSUInteger i= 0; i < queueCount; i++) {
queues[i] = dispatch_queue_create("com.yxw.chart.render", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(queues[i], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
}
}
});
int32_t cur = OSAtomicIncrement32(&counter);
if (cur < 0) {
cur = -cur;
}
return queues[cur%queueCount];
}
static dispatch_queue_t YChartAsyncLayerGetReleaseQueue() {
return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
}
iOS8之前和之后設(shè)置線(xiàn)程隊(duì)列優(yōu)先級(jí)及層次體系骑歹,讓不同隊(duì)列中的任務(wù)同步的執(zhí)行時(shí)
iOS8 之前需要?jiǎng)?chuàng)建一個(gè)串行隊(duì)列并設(shè)置和全局隊(duì)列優(yōu)先級(jí)相同: 兩個(gè)線(xiàn)程隊(duì)列分別按照添加順序來(lái)執(zhí)行
queues[i] = dispatch_queue_create("com.yxw.chart.render", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(queues[i], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
iOS8 之后: QOS_CLASS_USER_INITIATED : 和全局隊(duì)列中的線(xiàn)程優(yōu)先級(jí)相同
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0);
queues[i] = dispatch_queue_create("com.yxw.chart.render", attr);
[NSProcessInfo processInfo].activeProcessorCount 獲取當(dāng)前設(shè)備是幾核,創(chuàng)建隊(duì)列數(shù)等于核數(shù)
cell 賦值異步渲染原理
- (void)setDataArray:(NSArray *)dataArray{
_dataArray = dataArray;
if (_displaysAsynchronously) {
[self p_clearContents];
}
[self p_setLayoutNeedRedraw];
}
** [self p_clearContents] 為什么會(huì)清空內(nèi)容 ? 清空會(huì)有什么影響 峦树?**
1宪哩、異步渲染當(dāng)前 cell 顯示內(nèi)容清空褂策, 否則會(huì)有錯(cuò)位的現(xiàn)象峭判, 本次異步渲染還未完成時(shí),會(huì)顯示上次的內(nèi)容棍好;
2仗岸、同步渲染是實(shí)時(shí)刷新不會(huì)存在這個(gè)問(wèn)題;
3借笙、因?yàn)槭钱惒戒秩景遣溃援?dāng)快速滾動(dòng)時(shí), 會(huì)看到空白的頁(yè)面业稼;
** [self p_setLayoutNeedRedraw] 異步渲染的原理都在這里**
1盗痒、調(diào)用 YChartAsyncLayer 的 setNeedsDisplay 方法,改變當(dāng)前 cell 保存的隨機(jī)數(shù)低散;
2俯邓、當(dāng)前 cell 新開(kāi)啟一個(gè)異步線(xiàn)程加入到串行隊(duì)列中等待執(zhí)行,每個(gè)線(xiàn)程和一個(gè)隨機(jī)數(shù)對(duì)應(yīng) 谦纱;
3看成、當(dāng)串行隊(duì)列中線(xiàn)程的開(kāi)始渲染 該 cell 時(shí)君编, 先比較該線(xiàn)程對(duì)應(yīng)的 隨機(jī)數(shù)和 cell 當(dāng)前標(biāo)記的 隨機(jī)數(shù)是否相同跨嘉, 如果相同則渲染cell, 如果不相同吃嘿,說(shuō)明 cell 已經(jīng)被復(fù)用祠乃,放棄渲染線(xiàn)程退出梦重;
4、cell 渲染 有三個(gè)步驟 :
(1) willDisplay : 開(kāi)始渲染前亮瓷,移除 layer 持有的動(dòng)畫(huà)琴拧, 在主線(xiàn)程執(zhí)行;
(2) display : 真正開(kāi)始渲染嘱支,在異步線(xiàn)程蚓胸, 渲染過(guò)程中 實(shí)時(shí)判斷是否被取消渲染;
(3) didDisplay : 渲染完成除师,生成一張 image 沛膳,回到主線(xiàn)程,通過(guò)動(dòng)畫(huà)顯示在layer上汛聚;
為什么是 串行線(xiàn)程隊(duì)列 而不是 并行線(xiàn)程隊(duì)列 锹安?
1、當(dāng)快速滾動(dòng)時(shí)創(chuàng)建太多的 線(xiàn)程 卡死現(xiàn)象
**2倚舀、防治數(shù)據(jù)渲染錯(cuò)誤 **:
比如 :
當(dāng)前 cell row=10叹哭, 渲染的線(xiàn)程是 t1 cell數(shù)據(jù) d1
下一次復(fù)用時(shí):
cell row=15, 渲染的線(xiàn)程是 t2 cell數(shù)據(jù) d2
同步線(xiàn)程觸發(fā)時(shí)間及結(jié)束時(shí)間不確定 :
場(chǎng)景1 :
當(dāng) t1 先執(zhí)行后結(jié)束 痕貌,t2 后執(zhí)行先結(jié)束 风罩, 此時(shí) 顯示數(shù)據(jù)為 d1 ;
場(chǎng)景2 :
當(dāng) t2 先執(zhí)行先結(jié)束 ,t1 后執(zhí)行后結(jié)束時(shí)舵稠, 此時(shí) 顯示數(shù)據(jù)為 d1 ;
當(dāng)一次復(fù)用時(shí)有兩個(gè) 線(xiàn)程 4種情況泊交,,當(dāng)有 n 次復(fù)用 最多 2 的 n此方 種情況柱查,顯示錯(cuò)誤概率極高廓俭;
同步的時(shí)候取消時(shí)的邏輯: 最后一次 cell 賦值時(shí)改變了隨機(jī)值,所有復(fù)用該 cell 的同步隊(duì)列中的線(xiàn)程都被取消唉工,所以不會(huì)造成數(shù)據(jù)錯(cuò)亂的現(xiàn)象
最后再回到Y(jié)YLabel
1研乒、YYLabel layer 層
+ (Class)layerClass {
return [YYTextAsyncLayer class];
}
2、渲染觸發(fā)
- (void)_displayAsync:(BOOL)async {
3淋硝、渲染雹熬,回調(diào)到 leyer.delegate = YYLabel;
- (YYTextAsyncLayerDisplayTask *)newAsyncDisplayTask {
}
4、YYLabel 添加點(diǎn)擊事件: touch 方法去匹配數(shù)據(jù)谣膳,然后重新渲染
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
........
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
}
參考鏈接
demo;
ibireme/YYText;
iOS 保持界面流暢的技巧;