更多內(nèi)容請(qǐng)挪步我的博客
GCD 的 Timer 相對(duì)于 NSTimer 更加靈活哈恰、高效(無(wú)需在主線程和后臺(tái)線程之間進(jìn)行切換)妥衣,而且 NSTimer 需要在合適的地方調(diào)用 invalid 以避免內(nèi)存泄漏幔亥,所以平時(shí)比較常用歉备。但是發(fā)現(xiàn) GCD Timer 隨便調(diào)用 API 的話也會(huì)造成內(nèi)存泄漏谈竿。
GCD Timer 通過(guò) Dispatch Source 實(shí)現(xiàn),調(diào)用方法如:
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW,
2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^() {
// do something
});
dispatch_resume(timer);
如果需要暫停定時(shí)器調(diào)用
dispatch_suspend(timer);
dispatch_suspend 和 dispatch_resume 應(yīng)該是成對(duì)出現(xiàn)的沮尿。兩者分別會(huì)減少和增加 dispatch 對(duì)象的掛起計(jì)數(shù),但是沒(méi)有 API 獲取當(dāng)前是掛起還是執(zhí)行狀態(tài)较解,所以需要自己記錄畜疾。
dispatch_suspend 暫停隊(duì)列并不意味著當(dāng)前執(zhí)行的 block 暫停
當(dāng)暫停派發(fā)隊(duì)列時(shí)需要注意,調(diào)用 dispatch_suspend 暫停一個(gè)隊(duì)列印衔,并不意味著暫停當(dāng)前正在執(zhí)行的 block啡捶,而是 block 可以執(zhí)行完,但是接下來(lái)的 block 會(huì)被暫停奸焙,直到 dispatch_resume 被調(diào)用瞎暑。
如果添加 dispatch_suspend 的調(diào)用后 timer 是無(wú)法被釋放的。一般情況下會(huì)發(fā)生崩潰并報(bào)“EXC_BAD_INSTRUCTION”錯(cuò)誤与帆,看下 GCD 源碼 dispatch source release 的時(shí)候判斷了當(dāng)前是否是在暫停狀態(tài)了赌。
dispatch_suspend 狀態(tài)下無(wú)法釋放
但是還有不一般的情況,如果暫停的代碼加到 dispatch_source_set_event_handler 的 block 中玄糟,并不會(huì)發(fā)生崩潰勿她,但是這個(gè)時(shí)候頁(yè)面會(huì)無(wú)法釋放造成內(nèi)存泄漏!內(nèi)存泄漏阵翎!內(nèi)存泄漏逢并!。
dispatch_source_set_event_handler(timer, ^() {
// do something
dispatch_suspend(timer);
});
Demo 代碼點(diǎn)擊這里郭卫,Demo 中進(jìn)入“GCD Timer” 頁(yè)面再退出砍聊,通過(guò) Allocations 工具可以發(fā)現(xiàn) GCDTimerViewController 沒(méi)有釋放,dealloc 無(wú)法執(zhí)行贰军。
怎么破玻蝌?如果有需求按照上面這種方式寫的話,添加個(gè)變量記錄下 dispatch_suspend 是否被調(diào)用的狀態(tài)谓形,在 dealloc 時(shí)判斷下灶伊,調(diào)用過(guò) dispatch_suspend 則再調(diào)用下 dispatch_resume『或者暫停后不需要重新運(yùn)行 timer 的話聘萨,取消 timer,一旦取消則不能再重新運(yùn)行 timer童太,只能重建米辐。
dispatch_source_cancel(timer);