使用 Dispatch Source 而不使用 dispatch_async 的唯一原因就是利用聯(lián)結(jié)的優(yōu)勢(shì)。
聯(lián)結(jié)的大致流程:在任一線程上調(diào)用它的一個(gè)函數(shù) dispatch_source_merge_data 后帝牡,會(huì)在相應(yīng)quene執(zhí)行 Dispatch Source 事先定義好的句柄(可以把句柄簡(jiǎn)單理解為一個(gè) block )巍耗。
這個(gè)過(guò)程叫 Custom event 抓歼,用戶事件嚷闭。是 dispatch source 支持處理的一種事件勤众。
簡(jiǎn)單地說(shuō)掩浙,這種事件是由你調(diào)用 dispatch_source_merge_data 函數(shù)來(lái)向自己發(fā)出的信號(hào)花吟。
一、創(chuàng)建dispatch源
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
參數(shù):
參數(shù) | 意義 |
---|---|
type | dispatch源可處理的事件類型 |
handle | 可以理解為索引厨姚、id或句柄衅澈,假如要監(jiān)聽(tīng)進(jìn)程,需要傳入進(jìn)程的ID |
mask | 可以理解為描述遣蚀,提供更詳細(xì)的描述矾麻,讓它知道具體要監(jiān)聽(tīng)什么 |
queue | 自定義源需要的一個(gè)隊(duì)列纱耻,用來(lái)處理所有的響應(yīng)句柄(block) |
Dispatch Source可處理的所有事件:
名稱 | 意義 |
---|---|
DISPATCH_SOURCE_TYPE_DATA_ADD | 自定義的事件,變量增加 |
DISPATCH_SOURCE_TYPE_DATA_OR | 自定義的事件险耀,變量OR |
DISPATCH_SOURCE_TYPE_MACH_SEND | MACH端口發(fā)送 |
DISPATCH_SOURCE_TYPE_MACH_RECV | MACH端口接收 |
DISPATCH_SOURCE_TYPE_PROC | 進(jìn)程監(jiān)聽(tīng),如進(jìn)程的退出弄喘、創(chuàng)建一個(gè)或更多的子線程、進(jìn)程收到UNIX信號(hào) |
DISPATCH_SOURCE_TYPE_READ | IO操作甩牺,如對(duì)文件的操作蘑志、socket操作的讀響應(yīng) |
DISPATCH_SOURCE_TYPE_SIGNAL | 接收到UNIX信號(hào)時(shí)響應(yīng) |
DISPATCH_SOURCE_TYPE_TIMER | 定時(shí)器 |
DISPATCH_SOURCE_TYPE_VNODE | 文件狀態(tài)監(jiān)聽(tīng),文件被刪除贬派、移動(dòng)急但、重命名 |
DISPATCH_SOURCE_TYPE_WRITE | IO操作,如對(duì)文件的操作搞乏、socket操作的寫響應(yīng) |
自定義事件可以使用的只有DISPATCH_SOURCE_TYPE_DATA_ADD
和DISPATCH_SOURCE_TYPE_DATA_OR
這兩種類型波桩,我們這里也只討論這兩種類型。
二请敦、其他函數(shù):
dispatch_suspend(queue) //掛起隊(duì)列
dispatch_resume(source) //分派源創(chuàng)建時(shí)默認(rèn)處于掛起狀態(tài)镐躲,在分派源分派處理程序之前必須先恢復(fù)
dispatch_source_merge_data(source, 1) //向分派源發(fā)送事件,需要注意的是侍筛,不可以傳遞0值(事件不會(huì)被觸發(fā))萤皂,同樣也不可以傳遞負(fù)數(shù)。
dispatch_source_set_event_handler(source, block) //設(shè)置響應(yīng)分派源事件的block匣椰,在分派源指定的隊(duì)列上運(yùn)行
dispatch_source_get_data(source) //得到分派源的數(shù)據(jù)
三裆熙、代碼:
//創(chuàng)建source,以DISPATCH_SOURCE_TYPE_DATA_ADD的方式進(jìn)行累加禽笑,而DISPATCH_SOURCE_TYPE_DATA_OR是對(duì)結(jié)果進(jìn)行二進(jìn)制或運(yùn)算
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
//設(shè)置事件觸發(fā)后執(zhí)行的句柄
dispatch_source_set_event_handler(source,^{
NSLog(@"監(jiān)聽(tīng)函數(shù):%lu",dispatch_source_get_data(source));
});
//開(kāi)啟source
dispatch_resume(source);
dispatch_queue_t myqueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(myqueue, ^ {
for(int i = 1; i <= 4; i ++){
NSLog(@"~~~~~~~~~~~~~~%d", i);
//異步線程觸發(fā)事件入录,向source發(fā)送事件,這里i不能為0佳镜,否則觸發(fā)不了事件
dispatch_source_merge_data(source,i);
//當(dāng)Interval的事件越長(zhǎng)纷跛,則每次的句柄都會(huì)觸發(fā)
//[NSThread sleepForTimeInterval:1.0];
}
});
上面的這個(gè)例子,因?yàn)閒or循環(huán)運(yùn)算速度非逞樱快,系統(tǒng)會(huì)自動(dòng)把這4次事件聯(lián)結(jié)起來(lái)唬血,可以看到最終事件觸發(fā)的句柄只會(huì)執(zhí)行一次望蜡。打印出來(lái)的結(jié)果為:
~~~~~~~~~~~~~~1
~~~~~~~~~~~~~~2
~~~~~~~~~~~~~~3
~~~~~~~~~~~~~~4
監(jiān)聽(tīng)函數(shù):10
這里的10就是把每次的事件值i
相加得到的(1+2+3+4)。這里如果把DISPATCH_SOURCE_TYPE_DATA_ADD
替換為DISPATCH_SOURCE_TYPE_DATA_OR
拷恨,結(jié)果會(huì)是7脖律,也就是把每次的事件值i
或運(yùn)算得到(1|2|3|4)。
如果把[NSThread sleepForTimeInterval:1.0]
的注釋打開(kāi)腕侄,因?yàn)槭录g隔太長(zhǎng)小泉,系統(tǒng)不會(huì)聯(lián)結(jié)芦疏,此時(shí)類似于dispatch_async()
,打印結(jié)果如下:
~~~~~~~~~~~~~~1
監(jiān)聽(tīng)函數(shù):1
~~~~~~~~~~~~~~2
監(jiān)聽(tīng)函數(shù):2
~~~~~~~~~~~~~~3
監(jiān)聽(tīng)函數(shù):3
~~~~~~~~~~~~~~4
監(jiān)聽(tīng)函數(shù):4
此時(shí)不論type是DISPATCH_SOURCE_TYPE_DATA_ADD
或DISPATCH_SOURCE_TYPE_DATA_OR
微姊,結(jié)果都是這個(gè)酸茴,因?yàn)檫@兩種type只影響聯(lián)結(jié)時(shí)的value。對(duì)非聯(lián)結(jié)的情況沒(méi)有影響兢交。
四薪捍、例子:
當(dāng)我們更新進(jìn)度條時(shí),可能在多個(gè)線程上同時(shí)做很多任務(wù)配喳,每個(gè)任務(wù)完成后酪穿,刷新界面,更新一點(diǎn)進(jìn)度條的進(jìn)度晴裹,因?yàn)槊總€(gè)任務(wù)都更新一次進(jìn)度條被济,造成界面刷新次數(shù)太多,可能會(huì)導(dǎo)致界面卡頓涧团,所以此時(shí)利用Dispatch Source能很好的解決這種情況只磷,因?yàn)镈ispatch Source在刷新太頻繁的時(shí)候會(huì)自動(dòng)聯(lián)結(jié)起來(lái),下面就用代碼實(shí)現(xiàn)一下這個(gè)場(chǎng)景少欺。
//1喳瓣、指定DISPATCH_SOURCE_TYPE_DATA_ADD,做成Dispatch Source(分派源)赞别。設(shè)定Main Dispatch Queue 為追加處理的Dispatch Queue
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
__block NSUInteger totalComplete = 0;
dispatch_source_set_event_handler(source, ^{
//當(dāng)處理事件被最終執(zhí)行時(shí)畏陕,計(jì)算后的數(shù)據(jù)可以通過(guò)dispatch_source_get_data來(lái)獲取。這個(gè)數(shù)據(jù)的值在每次響應(yīng)事件執(zhí)行后會(huì)被重置仿滔,所以totalComplete的值是最終累積的值惠毁。
NSUInteger value = dispatch_source_get_data(source);
totalComplete += value;
NSLog(@"進(jìn)度:%@", @((CGFloat)totalComplete/100));
});
//分派源創(chuàng)建時(shí)默認(rèn)處于暫停狀態(tài),在分派源分派處理程序之前必須先恢復(fù)崎页。
dispatch_resume(source);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (NSUInteger index = 0; index < 100; index++) {
dispatch_async(queue, ^{
usleep(20000);//0.02秒
dispatch_source_merge_data(source, 1);
});
}
上面相等于啟動(dòng)了100個(gè)任務(wù)鞠绰,每個(gè)任務(wù)耗時(shí)0.02秒,打印結(jié)果如下:
進(jìn)度:0.25
進(jìn)度:0.32
進(jìn)度:0.37
進(jìn)度:0.41
進(jìn)度:0.55
進(jìn)度:0.61
進(jìn)度:0.63
進(jìn)度:0.64
進(jìn)度:0.76
進(jìn)度:0.89
進(jìn)度:0.96
進(jìn)度:1
附上demo飒焦。