在 iOS SDK 里面是無法取消提交的任務(wù)的争群,這里實(shí)現(xiàn)一個可以取消的例子。
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
dispatch_after 的作用是在指定的隊(duì)列,指定的時間執(zhí)行任務(wù)完域,一般用在需要延時處理的場景赘来。
然而它的延時并非完全的延時,延時任務(wù)在 dispatch_after
調(diào)用的時候呛梆,就已經(jīng)提交給操作系統(tǒng)锐涯,之后操作系統(tǒng)在指定時間執(zhí)行這個任務(wù)。所以這個任務(wù)在提交之后填物,執(zhí)行之前纹腌,在某些時候即使修改內(nèi)部的數(shù)據(jù),也不會影響執(zhí)行的結(jié)果滞磺。
例如:
int i = 0;
NSTimeInterval time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{
printf("i = %d", i);
});
i = 2;
// 輸出: i = 0
在任務(wù) block 中升薯,捕獲了當(dāng)前 i 的瞬時值,之后對它進(jìn)行修改击困,不會影響執(zhí)行的結(jié)果涎劈。這是因?yàn)?i 作為局部變量,是分配在棧上的阅茶,在 block 還沒有執(zhí)行之前蛛枚,i 已經(jīng)出棧回收了脸哀。
如果一定想修改 block 內(nèi)的 i 可以這樣聲明:
__block int i = 0;
這時輸出的結(jié)果為 i = 2蹦浦。
在變量前加上 __block 時,會將變量拷貝到堆上撞蜂,而堆內(nèi)存在線程之間可以共享盲镶。具體的細(xì)節(jié)可以參考 block 的實(shí)現(xiàn)原理
dispatch_after 在 Objective-C 中屬于 C 的 API,而在 Swift 中被封裝成對象谅摄,iOS8 之前是沒有取消的功能的徒河,在 iOS8 之后有加入 DispatchWorkItem 的概念,支持取消 block 的執(zhí)行送漠。
在 iOS8 之前其實(shí)我們可以自定義「取消」block 顽照。
extension DispatchQueue {
typealias Task = (_ cancel: Bool) -> Void
func delay(_ time: TimeInterval, task: @escaping ()->()) -> Task? {
func dispatch_later(_ block: @escaping ()->()) {
let t = DispatchTime.now() + time
self.asyncAfter(deadline: t, execute: block)
}
var closure: (()->Void)? = task
var result: Task?
let delayedClosure: Task = { cancel in
if let internalClosure = closure {
if cancel == false {
self.async(execute: internalClosure)
}
}
closure = nil
result = nil
}
result = delayedClosure
dispatch_later {
if let delayedClosure = result {
delayedClosure(false)
}
}
return result
}
func cancel(_ task: Task?) {
task?(true)
}
}
使用:
let task = DispatchQueue.main.delay(3) {
print("延遲三秒輸出")
}
DispatchQueue.main.cancel(task)
點(diǎn)擊這里查看 Objective-C 版本。