GCD를 사용하여 Delay Call 취소

4212 단어
취소 작업은 OC에서 NSOperation의 특허이며, 현재 Swift의 GCD도 실행 중인 블록 작업을 취소할 수 있습니다. 코드는 다음과 같습니다.
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3, DispatchWorkItem{print("I'm here!")})
//      ,    3       
item.cancel()

Block을 DispatchWorkItem 대상에 봉한 다음 cancle을 보내서 실행 중인 Block을 취소할 수 있습니다.
여기서는 GCD의 delay call을 직접 캡슐화합니다.

오리지널 버전:

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)

만약 단번에 똑똑히 보았다면 아래의 분석을 볼 필요가 없었을 것이다.

스스로 쓰다


취소할 Block을 먼저 정의합니다.
typealias Task = (_ cancle : Bool) -> Void

func cancle(_ task: Task?) {
    task?(true)
}

핵심은task를 delayed Closure라는 클립에 봉인하는 것입니다. delayed Closure가 실행될 때result가 비어 있는지 확인하고, 비어 있지 않으면 계속 실행합니다. cancle가false이면task를 실행하고,result를 비어 있습니다. 나중에 시간이 되면 delayed Closure를 호출할 때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에서 실행 지연을 정의하는 하위 방법dispatchlater, 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에 값을 부여한 다음 delayed Closure에서 closure를 포획하고 Result와 함께 비우면 delayed Closure가 실행된 후 이 task를 즉시 방출할 수 있습니다.그렇지 않으면 Task가 반환되면서 캡처된 변수가 제거됩니다.그래서 이것은 단지 최적화일 뿐이다.그리고 if result != nil의 판단을 디스패치에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);
}

좋은 웹페이지 즐겨찾기