前言
Dispatch Source Timer 是一種與 Dispatch Queue 結(jié)合使用的定時(shí)器。當(dāng)需要在后臺(tái) queue 中定期執(zhí)行任務(wù)的時(shí)候,使用 Dispatch Source Timer 要比使用 NSTimer 更加自然,也更加高效(無(wú)需在 main queue 和后臺(tái) queue 之前切換)。下面將詳細(xì)給大家介紹關(guān)于Dispatch Source Timer的使用和一些注意事項(xiàng),話(huà)不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧。
創(chuàng)建 Timer
Dispatch Source Timer 首先其實(shí)是 Dispatch Source 的一種,關(guān)于 Dispatch Source 的內(nèi)容在這里就不再贅述了。下面是蘋(píng)果官方文檔里給出的創(chuàng)建 Dispatch Timer 的代碼:
dispatch_source_t CreateDispatchTimer(uint64_t interval,
uint64_t leeway,
dispatch_queue_t queue,
dispatch_block_t block)
{
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
0, 0, queue);
if (timer)
{
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);
dispatch_source_set_event_handler(timer, block);
dispatch_resume(timer);
}
return timer;
}
有幾個(gè)地方需要注意:
Dispatch Source Timer 是間隔定時(shí)器,也就是說(shuō)每隔一段時(shí)間間隔定時(shí)器就會(huì)觸發(fā)。在 NSTimer 中要做到同樣的效果需要手動(dòng)把 repeats 設(shè)置為 YES。
dispatch_source_set_timer 中第二個(gè)參數(shù),當(dāng)我們使用dispatch_time 或者 DISPATCH_TIME_NOW 時(shí),系統(tǒng)會(huì)使用默認(rèn)時(shí)鐘來(lái)進(jìn)行計(jì)時(shí)。然而當(dāng)系統(tǒng)休眠的時(shí)候,默認(rèn)時(shí)鐘是不走的,也就會(huì)導(dǎo)致計(jì)時(shí)器停止。使用 dispatch_walltime 可以讓計(jì)時(shí)器按照真實(shí)時(shí)間間隔進(jìn)行計(jì)時(shí)。
dispatch_source_set_timer 的第四個(gè)參數(shù) leeway 指的是一個(gè)期望的容忍時(shí)間,將它設(shè)置為 1 秒,意味著系統(tǒng)有可能在定時(shí)器時(shí)間到達(dá)的前 1 秒或者后 1 秒才真正觸發(fā)定時(shí)器。在調(diào)用時(shí)推薦設(shè)置一個(gè)合理的 leeway 值。需要注意,就算指定 leeway 值為 0,系統(tǒng)也無(wú)法保證完全精確的觸發(fā)時(shí)間,只是會(huì)盡可能滿(mǎn)足這個(gè)需求。
event handler block 中的代碼會(huì)在指定的 queue 中執(zhí)行。當(dāng) queue 是后臺(tái)線程的時(shí)候,dispatch timer 相比 NSTimer 就好操作一些了。因?yàn)?NSTimer 是需要 Runloop 支持的,如果要在后臺(tái) dispatch queue 中使用,則需要手動(dòng)添加 Runloop。使用 dispatch timer 就簡(jiǎn)單很多了。
dispatch_source_set_event_handler 這個(gè)函數(shù)在執(zhí)行完之后,block 會(huì)立馬執(zhí)行一遍,后面隔一定時(shí)間間隔再執(zhí)行一次。而 NSTimer 第一次執(zhí)行是到計(jì)時(shí)器觸發(fā)之后。這也是和 NSTimer 之間的一個(gè)顯著區(qū)別。
停止 Timer
停止 Dispatch Timer 有兩種方法,一種是使用 dispatch_suspend,另外一種是使用 dispatch_source_cancel。
dispatch_suspend 嚴(yán)格上只是把 Timer 暫時(shí)掛起,它和 dispatch_resume 是一個(gè)平衡調(diào)用,兩者分別會(huì)減少和增加 dispatch 對(duì)象的掛起計(jì)數(shù)。當(dāng)這個(gè)計(jì)數(shù)大于 0 的時(shí)候,Timer 就會(huì)執(zhí)行。在掛起期間,產(chǎn)生的事件會(huì)積累起來(lái),等到 resume 的時(shí)候會(huì)融合為一個(gè)事件發(fā)送。
需要注意的是:dispatch source 并沒(méi)有提供用于檢測(cè) source 本身的掛起計(jì)數(shù)的 API,也就是說(shuō)外部不能得知一個(gè) source 當(dāng)前是不是掛起狀態(tài),在設(shè)計(jì)代碼邏輯時(shí)需要考慮到這一點(diǎn)。
dispatch_source_cancel 則是真正意義上的取消 Timer。被取消之后如果想再次執(zhí)行 Timer,只能重新創(chuàng)建新的 Timer。這個(gè)過(guò)程類(lèi)似于對(duì) NSTimer 執(zhí)行 invalidate。
關(guān)于取消 Timer,另外一個(gè)很重要的注意事項(xiàng):dispatch_suspend 之后的 Timer,是不能被釋放的!下面的代碼會(huì)引起崩潰:
- (void)stopTimer
{
dispatch_suspend(_timer);
_timer = nil; // EXC_BAD_INSTRUCTION 崩潰
}
因此使用 dispatch_suspend 時(shí),Timer 本身的實(shí)例需要一直保持。使用 dispatch_source_cancel 則沒(méi)有這個(gè)限制:
- (void)stopTimer
{
dispatch_source_cancel(_timer);
_timer = nil; // OK
}