LoginSignup
12
11

More than 5 years have passed since last update.

非同期のためのGCD処理を簡略化する

Last updated at Posted at 2016-06-04

役に立つかどうかよくわからないTipsシリーズ

とある重い処理を行う時
メインスレッドとは別のスレッドでやりたいときは多いですね

そんなときに使うベタな手法がGCD(Grand Central Dispatch)なわけですが
そのまま書くとこんな感じになるかなと思います

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    // 何か重たい処理
    dispatch_async(dispatch_get_main_queue()) {
        // UIの処理
    }
}

このdispatch_async()に渡す引数は、小難しい話になるので
GCDについて情報整理する
↑こちらの方の記事にお任せするとして

複雑なスレッド処理をしないかぎりは、
この書き方で大抵のものは成立します

しかし、書くことが長いし、
もしiOS初心者には初見で何やってるのかよくわからないかも…なので
まずはラッピング関数を用意してしまいましょう

/// メインスレッドで処理を行う
/// - parameter block: メインスレッドで行う処理
func onMainThread(block: ()->()) {
    dispatch_async(dispatch_get_main_queue(), block)
}

/// メインスレッド以外の新しいスレッドで処理を行う
/// - parameter block: 新しいスレッドで行う処理
func onNewThread(block: ()->()) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block)
}

こうすると、先ほどの実装は

onNewThread {
    // 何か重たい処理
    onMainThread {
        // UIの処理
    }
}

いかがでしょう?
「あー、新しいスレッドで何かやって、メインスレッドで何かやってるんですねー」って
何をしているか分かりやすくなったと思います

で、
あともう少し工夫を

別スレッドに回す時って

メインスレッドに居て
重い処理をやるために別スレッドに処理を書いて
終わったらメインスレッドに戻す

って流れになることが多いので
こういう関数を作ってみました

/// 非同期処理を行う
/// - parameter async: 非同期でやりたい処理(別スレッドで実行)
/// - parameter completed: 完了時に行う処理(メインスレッドで実行)
func async(process asynchronousProcess: ()->(), completed completionHandler: ()->()) {
    onNewThread {
        asynchronousProcess()
        onMainThread {
            completionHandler()
        }
    }
}

使うときは、このような実装になります

async(
    process: {
        // 何か重たい処理
    },
    completed: {
        // UIの処理
    }
)

好みかもしれませんが

UIViewでのアニメーションを書く時の
この書き方に近しいものになるかと思います

UIView.animateWithDuration(
    0.1, // 0.1秒間で
    animations: {
        // こんなアニメーションを行って
    },
    completion: { _ in
        // 終わったらこれをやるよ
    }
)

なので、すっきりして個人的に満足です

これらを使って、こういう関数も作成できます

/// 秒を指定して非同期でスリープを行う
/// - parameter seconds: スリープ秒数
/// - parameter completed: スリープ完了時に行う処理
func asyncSleep(seconds sec: UInt32, completed: ()->()) {
    async(process: { sleep(sec) }, completed: completed)
}

/// ミリ秒を指定して非同期でスリープを行う
/// - parameter milliseconds: スリープのミリ秒数
/// - parameter completed: スリープ完了時に行う処理
func asyncSleep(milliseconds msec: useconds_t, completed: ()->()) {
    async(process: { usleep(msec) }, completed: completed)
}

/// スリープ間隔を指定して非同期でスリープを行う
/// - parameter duration: スリープ間隔
/// - parameter completed: スリープ完了時に行う処理
func asyncSleep(duration: NSTimeInterval, completed: ()->()) {
    async(process: { usleep(useconds_t(duration * 1000000)) }, completed: completed)
}

使用例

showIndicator()     // インジケータを表示して
asyncSleep(0.8) {   // 0.8秒待ったら
    hideIndicator() // インジケータを隠す
}

普通にスリープ関数使うとできないことが、簡単にできるようになると思います

12
11
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
12
11