取消任务在OC当中是NSOperation的专利,现在Swift的GCD也支持取消正在等待执行的Block操作了,代码如下所示:
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3, DispatchWorkItem{print("I'm here!")})//执行下面一行,则可取消3秒后的延迟操作item.cancel()复制代码
我们可以通过将一个Block封装到DispatchWorkItem对象中,然后对其发送cancle,来取消一个正在等待执行的block。
在这里我们自己封装实现一个GCD的delay call:
原版:
import Foundationtypealias Task = (_ cancle : Bool) -> Voidfunc delay(_ time: TimeInterval, task: @escaping() -> ()) -> Task? { func dispatch_later(block: @escaping()->()) { let t = DispatchTime.now() + time DispatchQueue.main.asyncAfter(deadline: t, execute: block) } var closure : (() -> Void)? = task var result : Task? let delayedClosure : Task = { cancle in if let internalClosure = closure { if cancle == false { DispatchQueue.main.async(execute: internalClosure) } } closure = nil result = nil } result = delayedClosure dispatch_later { if let delayedClosure = result { delayedClosure(false) } } return result}func cancle(_ task: Task?) { task?(true)}复制代码
最后的使用代码如下:
let task = delay(3){print("I will be cancle")}cancle(task)复制代码
要是一下就看明白了,就不用看下面的分析了。
自己写
先定义用来取消的block:
typealias Task = (_ cancle : Bool) -> Voidfunc cancle(_ task: Task?) { task?(true)}复制代码
核心就是把task封装到delayedClosure这个闭包中,delayedClosure执行时会检查result是否为空,不为空就继续执行并且cancle为false就执行task,把result置空,后面当时间到了要调用delayedClosure的时候因为result为空了,所以就不会执行task了:
func delay(_ time: TimeInterval, task: @escaping()->()) -> Task? { var result : Task? let delayedClosure: Task = {cancle in if result != nil { if cancle == false { DispatchQueue.main.async { task() } } result = nil } } result = delayedClosure return result}复制代码
在delay中定义延迟执行的子方法dispatch_later,将delayedClosure封装成dispatch_later能调用的闭包:
func delay(_ time: TimeInterval, task: @escaping()->()) -> Task? { var result : Task? func dispatch_later(block: @escaping()->()) { let t = DispatchTime.now() + time DispatchQueue.main.asyncAfter(deadline: t, execute: block) } let delayedClosure: Task = {cancle in if result != nil { if cancle == false { DispatchQueue.main.async { task() } } result = nil } } result = delayedClosure dispatch_later { delayedClosure(false) } return result}复制代码
到此为止就已经能实现延迟执行并取消task了。
原版的下面这句:
var closure : (() -> Void)? = task复制代码
将task赋值给可变变量closure,然后在delayedClosure中捕获closure,与result一起置为空,则可以在delayedClosure执行完后立即释放这个task。
如果不这样做的话,随着返回的Task被销毁,它所捕获的变量也会销毁。因此这只是一个优化。然后在将if result != nil
的判断写在dispatch_later调用的闭包中,代码就和原版一样了。 OC版本的:
Task delay(NSTimeInterval time, Blk task) { __block Task result; __block Blk closure = task; Task delayClosure = ^(BOOL cancle) { if (closure) { Blk internalClosure = closure; if (!cancle) { dispatch_async(dispatch_get_main_queue(), ^{ internalClosure(); }); } closure = nil; } result = nil; }; result = delayClosure; dispatch_delay(time, ^{ if (result) { result(false); } }); return result;}void dispatch_delay(NSTimeInterval time, Blk block) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), block);}复制代码