Dispatch Source的自定義事件

使用 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操作的寫(xiě)響應(yīng)

自定義事件可以使用的只有DISPATCH_SOURCE_TYPE_DATA_ADDDISPATCH_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_ADDDISPATCH_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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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