LoginSignup
80
80

More than 5 years have passed since last update.

Swiftで非同期処理/遅延処理をキャンセルする

Last updated at Posted at 2016-02-09

GCD で dispatch した非同期処理をキャンセルしたいケースというのがよくありまして 1、Objective-C の場合はいつも使ってるスニペットがあったのですが、Swiftではどう書くのかなとググったら、こちら のコードが見つかりました。

で、命名に違和感があったり、遅延実行キューがメインキュー限定だったり、Swift的でない書き方だったりしたのを修正したものが以下になります。

CancelableBlock.swift
typealias dispatch_cancelable_block_t = (cancel:Bool) -> (Void)

private func dispatch_after_delay(delay: Double, queue: dispatch_queue_t, block: dispatch_block_t?) -> dispatch_cancelable_block_t? {
    guard let block = block else { return nil }
    var originalBlock: dispatch_block_t? = block
    var cancelableBlock: dispatch_cancelable_block_t? = nil
    let delayBlock: dispatch_cancelable_block_t = {(cancel: Bool) -> Void in
        if let originalBlock = originalBlock where !cancel {
            dispatch_async(queue, originalBlock)
        }
        cancelableBlock = nil
        originalBlock = nil
    }
    cancelableBlock = delayBlock
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), queue) { () -> Void in
        if let cancelableBlock = cancelableBlock {
            cancelableBlock(cancel: false)
        }
    }
    return cancelableBlock
}

func dispatch_after_delay(delay: Double, block: dispatch_block_t?) -> dispatch_cancelable_block_t? {
    let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    return dispatch_after_delay(delay, queue: queue, block: block)
}

func dispatch_on_main_queue_after_delay(delay: Double, block: dispatch_block_t?) -> dispatch_cancelable_block_t? {
    return dispatch_after_delay(delay, queue: dispatch_get_main_queue(), block: block)
}

func cancel_block(block: dispatch_cancelable_block_t?) {
    if let block = block {
        block(cancel: true)
    }
}

Gist に置いてあります。

使い方

プロパティを用意しておいて、

var block: dispatch_cancelable_block_t?

遅延処理実行時に次のように書きます。

block = dispatch_after_delay(delay, block: { [weak self] () -> Void in
    // Something
})

(メインキューで処理を実行したい場合)

block = dispatch_on_main_queue_after_delay(delay, block: { [weak self] () -> Void in
    // Something
})

非同期/遅延処理をキャンセルしたい場合は cancel_block を呼びます。

cancel_block(block)

blocknil であれば何もしないだけなので、if let とかする必要はありません)


  1. 例: watchOS 2 でアニメーション完了時に処理をさせたい場合とか。標準アニメーションメソッドの引数にそういうブロックを渡せないのでGCDで遅延処理をdispatchすることになるが、InterfaceObjectを追加したり消したり隠したりがiOSほど自由じゃないので、場合によってアニメーションを適切にキャンセルしたりする必要がある。 

80
80
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
80
80