使用GCD實現Delay Call的取消操做

取消任務在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調用的閉包中,代碼就和原版同樣了。

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);
}複製代碼
相關文章
相關標籤/搜索