在 iOS SDK 里面是无法取消提交的任务的,这里实现一个可以取消的例子。
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
dispatch_after 的作用是在指定的队列,指定的时间执行任务,一般用在需要延时处理的场景。
然而它的延时并非完全的延时,延时任务在 dispatch_after
调用的时候,就已经提交给操作系统,之后操作系统在指定时间执行这个任务。所以这个任务在提交之后,执行之前,在某些时候即使修改内部的数据,也不会影响执行的结果。
例如:
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
在任务 block 中,捕获了当前 i 的瞬时值,之后对它进行修改,不会影响执行的结果。这是因为 i 作为局部变量,是分配在栈上的,在 block 还没有执行之前,i 已经出栈回收了。
如果一定想修改 block 内的 i 可以这样声明:
__block int i = 0;
这时输出的结果为 i = 2。
在变量前加上 __block 时,会将变量拷贝到堆上,而堆内存在线程之间可以共享。具体的细节可以参考 block 的实现原理
dispatch_after 在 Objective-C 中属于 C 的 API,而在 Swift 中被封装成对象,iOS8 之前是没有取消的功能的,在 iOS8 之后有加入 DispatchWorkItem 的概念,支持取消 block 的执行。
在 iOS8 之前其实我们可以自定义「取消」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)
点击这里查看 Objective-C 版本。
网友评论