取消任务在OC当中是NSOperation的专利,如今Swift的GCD也支持取消正在等待执行的Block操做了,代码以下所示:javascript
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3, DispatchWorkItem{print("I'm here!")})
//执行下面一行,则可取消3秒后的延迟操做
item.cancel()复制代码
咱们能够经过将一个Block封装到DispatchWorkItem对象中,而后对其发送cancle,来取消一个正在等待执行的block。java
在这里咱们本身封装实现一个GCD的delay call:api
import Foundation
typealias Task = (_ cancle : Bool) -> Void
func 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)复制代码
要是一下就看明白了,就不用看下面的分析了。async
先定义用来取消的block:优化
typealias Task = (_ cancle : Bool) -> Void
func cancle(_ task: Task?) {
task?(true)
}复制代码
核心就是把task封装到delayedClosure这个闭包中,delayedClosure执行时会检查result是否为空,不为空就继续执行而且cancle为false就执行task,把result置空,后面当时间到了要调用delayedClosure的时候由于result为空了,因此就不会执行task了:spa
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能调用的闭包:code
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了。对象
原版的下面这句:ip
var closure : (() -> Void)? = task复制代码
将task赋值给可变变量closure,而后在delayedClosure中捕获closure,与result一块儿置为空,则能够在delayedClosure执行完后当即释放这个task。
若是不这样作的话,随着返回的Task被销毁,它所捕获的变量也会销毁。所以这只是一个优化。
而后在将if result != nil
的判断写在dispatch_later调用的闭包中,代码就和原版同样了。
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);
}复制代码