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)
(block
が nil
であれば何もしないだけなので、if let
とかする必要はありません)
-
例: watchOS 2 でアニメーション完了時に処理をさせたい場合とか。標準アニメーションメソッドの引数にそういうブロックを渡せないのでGCDで遅延処理をdispatchすることになるが、InterfaceObjectを追加したり消したり隠したりがiOSほど自由じゃないので、場合によってアニメーションを適切にキャンセルしたりする必要がある。 ↩