使用 Dispatch Source 而不使用 dispatch_async 的唯一原因就是利用聯(lián)結(jié)的優(yōu)勢(shì)坦报。
聯(lián)結(jié)的大致流程:在任一線程上調(diào)用它的一個(gè)函數(shù) dispatch_source_merge_data 后岳遥,會(huì)執(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_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue)
參數(shù):
參數(shù) | 意義 |
---|---|
type | dispatch源可處理的事件 |
handle | 可以理解為句柄扶歪、索引或id,假如要監(jiān)聽進(jìn)程摄闸,需要傳入進(jìn)程的ID |
mask | 可以理解為描述善镰,提供更詳細(xì)的描述,讓它知道具體要監(jiān)聽什么 |
queue | 自定義源需要的一個(gè)隊(duì)列年枕,用來(lái)處理所有的響應(yīng)句柄(block) |
Dispatch Source可處理的所有事件
名稱 | 內(nèi)容 |
---|---|
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)聽,如進(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)聽桥状,文件被刪除、移動(dòng)硝清、重命名 |
DISPATCH_SOURCE_TYPE_WRITE | IO操作辅斟,如對(duì)文件的操作、socket操作的寫響應(yīng) |
注意:
DISPATCH_SOURCE_TYPE_DATA_ADD
當(dāng)同一時(shí)間芦拿,一個(gè)事件的的觸發(fā)頻率很高砾肺,那么Dispatch Source會(huì)將這些響應(yīng)以ADD的方式進(jìn)行累積,然后等系統(tǒng)空閑時(shí)最終處理防嗡,如果觸發(fā)頻率比較零散变汪,那么Dispatch Source會(huì)將這些事件分別響應(yīng)。DISPATCH_SOURCE_TYPE_DATA_OR 和上面的一樣蚁趁,是自定義的事件裙盾,但是它是以O(shè)R的方式進(jìn)行累積
二、一些函數(shù)
dispatch_suspend(queue) //掛起隊(duì)列
dispatch_resume(source) //分派源創(chuàng)建時(shí)默認(rèn)處于暫停狀態(tài)他嫡,在分派源分派處理程序之前必須先恢復(fù)
dispatch_source_merge_data //向分派源發(fā)送事件番官,需要注意的是,不可以傳遞0值(事件不會(huì)被觸發(fā))钢属,同樣也不可以傳遞負(fù)數(shù)徘熔。
dispatch_source_set_event_handler //設(shè)置響應(yīng)分派源事件的block,在分派源指定的隊(duì)列上運(yùn)行
dispatch_source_get_data //得到分派源的數(shù)據(jù)
uintptr_t dispatch_source_get_handle(dispatch_source_t source); //得到dispatch源創(chuàng)建淆党,即調(diào)用dispatch_source_create的第二個(gè)參數(shù)
unsigned long dispatch_source_get_mask(dispatch_source_t source); //得到dispatch源創(chuàng)建酷师,即調(diào)用dispatch_source_create的第三個(gè)參數(shù)
void dispatch_source_cancel(dispatch_source_t source); //取消dispatch源的事件處理--即不再調(diào)用block讶凉。如果調(diào)用dispatch_suspend只是暫停dispatch源。
long dispatch_source_testcancel(dispatch_source_t source); //檢測(cè)是否dispatch源被取消山孔,如果返回非0值則表明dispatch源已經(jīng)被取消
void dispatch_source_set_cancel_handler(dispatch_source_t source, dispatch_block_t cancel_handler); //dispatch源取消時(shí)調(diào)用的block懂讯,一般用于關(guān)閉文件或socket等,釋放相關(guān)資源
void dispatch_source_set_registration_handler(dispatch_source_t source, dispatch_block_t registration_handler); //可用于設(shè)置dispatch源啟動(dòng)時(shí)調(diào)用block台颠,調(diào)用完成后即釋放這個(gè)block褐望。也可在dispatch源運(yùn)行當(dāng)中隨時(shí)調(diào)用這個(gè)函數(shù)。
三串前、代碼
實(shí)例一:dispatch_source的基本用法
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_global_queue(0, 0));
dispatch_source_set_event_handler(source, ^{
dispatch_sync(dispatch_get_main_queue(), ^{
//更新UI
});
});
dispatch_resume(source);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//網(wǎng)絡(luò)請(qǐng)求
dispatch_source_merge_data(source, 1); //通知隊(duì)列
});
實(shí)例二:dispatch_source實(shí)例
上面的例子創(chuàng)建一個(gè)source瘫里,source的type為ADD的方式,然后將事件觸發(fā)后要執(zhí)行的句柄添加到main隊(duì)列里荡碾,在source創(chuàng)建后默認(rèn)是掛起的减宣,需要用dispatch_resume函數(shù)來(lái)恢復(fù)監(jiān)聽,后面為了測(cè)試監(jiān)聽玩荠,加入了一個(gè)for循環(huán)漆腌,用dispatch_source_merge_data來(lái)觸發(fā)事件,但是在觸發(fā)事件的響應(yīng)句柄里我們只打印了一次阶冈,結(jié)果是每次相加的和闷尿,也就是10,而不是打印了4次女坑。
原因:DISPATCH_SOURCE_TYPE_DATA_ADD是將所有觸發(fā)結(jié)果相加填具,最后統(tǒng)一執(zhí)行響應(yīng),但是加入sleepForTimeInterval后匆骗,如果interval的時(shí)間越長(zhǎng)劳景,則每次觸發(fā)都會(huì)響應(yīng),但是如果interval的時(shí)間很短碉就,則會(huì)將觸發(fā)后的結(jié)果相加后統(tǒng)一觸發(fā)盟广。
這在更新UI時(shí)很有用,比如更新進(jìn)度條時(shí)瓮钥,沒(méi)必要每次觸發(fā)都響應(yīng)筋量,因?yàn)楦聲r(shí)還有其他的用戶操作(用戶輸入,觸碰等)碉熄,所以可以統(tǒng)一觸發(fā)
//創(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());
//事件觸發(fā)后執(zhí)行的句柄
dispatch_source_set_event_handler(source,^{
NSLog(@"監(jiān)聽函數(shù):%lu",dispatch_source_get_data(source));
});
//開啟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:0.0001];
}
});
3. 使用timer定時(shí)器
dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway)
參數(shù):
- source 分派源
- start 數(shù)控制計(jì)時(shí)器第一次觸發(fā)的時(shí)刻性誉。參數(shù)類型是 dispatch_time_t窿吩,這是一個(gè)opaque類型,我們不能直接操作它艾栋。我們得需要 dispatch_time 和 dispatch_walltime 函數(shù)來(lái)創(chuàng)建它們。另外蛉顽,常量 DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER 通常很有用蝗砾。
- interval 間隔時(shí)間
- leeway 計(jì)時(shí)器觸發(fā)的精準(zhǔn)程度
下面代碼可用作開屏倒計(jì)時(shí)
//倒計(jì)時(shí)時(shí)間
__block int timeout = 3;
//創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//創(chuàng)建timer
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//設(shè)置1s觸發(fā)一次,0s的誤差
dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒執(zhí)行
//觸發(fā)的事件
dispatch_source_set_event_handler(_timer, ^{
if(timeout<=0){ //倒計(jì)時(shí)結(jié)束携冤,關(guān)閉
//取消dispatch源
dispatch_source_cancel(_timer);
}
else{
timeout--;
dispatch_async(dispatch_get_main_queue(), ^{
//更新主界面的操作
NSLog(@"~~~~~~~~~~~~~~~~%d", timeout);
});
}
});
//開始執(zhí)行dispatch源
dispatch_resume(_timer);
實(shí)例四:dispatch_suspend掛起隊(duì)列
//創(chuàng)建DISPATCH_QUEUE_SERIAL隊(duì)列
dispatch_queue_t queue1 = dispatch_queue_create("com.iOSChengXuYuan.queue1", 0);
dispatch_queue_t queue2 = dispatch_queue_create("com.iOSChengXuYuan.queue2", 0);
//創(chuàng)建group
dispatch_group_t group = dispatch_group_create();
//異步執(zhí)行任務(wù)
dispatch_async(queue1, ^{
NSLog(@"任務(wù) 1 : queue 1...");
sleep(1);
NSLog(@":white_check_mark:完成任務(wù) 1");
});
dispatch_async(queue2, ^{
NSLog(@"任務(wù) 1 : queue 2...");
sleep(1);
NSLog(@":white_check_mark:完成任務(wù) 2");
});
//將隊(duì)列加入到group
dispatch_group_async(group, queue1, ^{
NSLog(@":no_entry_sign:正在暫停 1");
dispatch_suspend(queue1);
});
dispatch_group_async(group, queue2, ^{
NSLog(@":no_entry_sign:正在暫停 2");
dispatch_suspend(queue2);
});
//等待兩個(gè)queue執(zhí)行完畢后再執(zhí)行
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"=======等待兩個(gè)queue完成, 再往下進(jìn)行...");
//異步執(zhí)行任務(wù)
dispatch_async(queue1, ^{
NSLog(@"任務(wù) 2 : queue 1");
});
dispatch_async(queue2, ^{
NSLog(@"任務(wù) 2 : queue 2");
});
//在這里將這兩個(gè)隊(duì)列重新恢復(fù)
dispatch_resume(queue1);
dispatch_resume(queue2);
//當(dāng)將dispatch_group_wait(group, DISPATCH_TIME_FOREVER);注釋后悼粮,會(huì)產(chǎn)生崩潰,因?yàn)樗械娜蝿?wù)都是異步執(zhí)行的曾棕,在執(zhí)行恢復(fù)queue1和queue2隊(duì)列的時(shí)候扣猫,可能這個(gè)時(shí)候還沒(méi)有執(zhí)行queue1和queue2的掛起隊(duì)列
實(shí)例五:進(jìn)度條例子
//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));
NSLog(@":large_blue_circle:線程號(hào):%@", [NSThread currentThread]);
});
//分派源創(chuàng)建時(shí)默認(rèn)處于暫停狀態(tài)橙喘,在分派源分派處理程序之前必須先恢復(fù)时鸵。
dispatch_resume(source);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//2、恢復(fù)源后厅瞎,就可以通過(guò)dispatch_source_merge_data向Dispatch Source(分派源)發(fā)送事件:
for (NSUInteger index = 0; index < 100; index++) {
dispatch_async(queue, ^{
dispatch_source_merge_data(source, 1);
NSLog(@":recycle:線程號(hào):%@~~~~~~~~~~~~i = %ld", [NSThread currentThread], index);
usleep(20000);//0.02秒
});
}
//3饰潜、比較上面的for循環(huán)代碼,將dispatch_async放在外面for循環(huán)的外面和簸,打印結(jié)果不一樣
//dispatch_async(queue, ^{
//
// for (NSUInteger index = 0; index < 100; index++) {
//
// dispatch_source_merge_data(source, 1);
//
// NSLog(@":recycle:線程號(hào):%@~~~~~~~~~~~~i = %ld", [NSThread currentThread], index);
//
// usleep(20000);//0.02秒
// }
//});
//2是將100個(gè)任務(wù)添加到queue里面彭雾,而3是在queue里面添加一個(gè)任務(wù),而這一個(gè)任務(wù)做了100次循環(huán)
參考: