[Swift 3] Swift 3時代のGCDの基本的な使い方

logo_swift_400x400

はじめに

こんにちは!モバイルアプリサービス部の加藤潤です。
今回はSwift3で大きく変わったGCDのAPIについて、その基本的な使い方を見ていきたいと思います。

サブスレッドで何か重い処理をして終わったらメインスレッドに戻す

iOSアプリでは負荷の高い処理やいつ終わるかわからない通信等の処理は基本的にメインスレッドでは行いません。 メインスレッドで行うとアプリが固まりユーザーの操作を妨げてしまうためです。 そこでよくあるパターンは、サブスレッドで何か重い処理をして終わったらメインスレッドに戻すというパターンです。

自分でキューを生成

以下は自分でキューを生成して任意の処理をサブスレッドで実行、終わったらメインスレッドで処理を実行する例です。

// キューを生成してサブスレッドで実行
DispatchQueue(label: "jp.classmethod.app.queue").async {
    self.doSomething()

    // メインスレッドで実行
    DispatchQueue.main.async {
        self.doSomething()
    }
}

上記で自分でキューを生成と書いた理由は以下のようにシステムで用意されたキューを使うことも可能だからです。

システムで用意されたキューを使う

// システムで用意されたキューを使う
DispatchQueue.global(qos: .default).async {
    self.doSomething()
}

引数で指定しているqosQuality of Serviceの略で、処理の優先度を決定するものです。ここではdefaultを指定していますが、優先度の高いものから順に以下の選択肢が用意されています。

  • userInteractive
  • userInitiated
  • default
  • utility
  • background
  • unspecified

指定した秒数後に処理を実行する

以下は5秒後に任意の処理を実行する例です。 DispatchTime.now()で取得した現在時間に対し、.seconds(5)で5秒足しています。secondsDispatchTimeIntervalというenumであり、他にmilliseconds(ミリ秒)、microseconds(マイクロ秒)、nanoseconds(ナノ秒)を指定することができます。

// 5秒後に実行
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(5)) {
    self.doSomething()
}

全てのキューの処理が終わったら任意の処理を行う

以下はキューが複数ある場合に、全てのキューの処理が終わったら任意の処理を行う例です。 最初にグループとキューを生成し、キューに処理を登録する際にグループを指定することでキューとグループを紐付けています。 全てのキューの処理が終わったらメインスレッド上で処理が行われます。

// グループを生成
let group = DispatchGroup()
let queue1 = DispatchQueue(label: "jp.classmethod.app.queue1")
let queue2 = DispatchQueue(label: "jp.classmethod.app.queue2")
let queue3 = DispatchQueue(label: "jp.classmethod.app.queue3")

// キューとグループを紐付ける
queue1.async(group: group) {
    print("queue1 done.")
}
queue2.async(group: group) {
    print("queue2 done.")
}
queue3.async(group: group) {
    print("queue3 done.")
}

// タスクが全て完了したらメインスレッド上で処理を実行する
group.notify(queue: DispatchQueue.main) {
    print("all task done.")
}

DispatchWorkItemを使って処理を行う

DispatchWorkItemはタスクをカプセル化したクラスです。このクラスを使うと以下のようにキューの指定が必須ではなくなります。 この場合は現在のコンテキスト(スレッド)上で処理が行われるようです。

// キューを指定しなくても処理が行える
let workItem = DispatchWorkItem {
    self.doSomething()
}
workItem.perform()

もちろん、以下のようにキューを指定することも可能です。

queue1.async(execute: workItem)

このDispatchWorkItemを使うと、処理の実行perform()の他にも待機wait()やキャンセルcancel()など、タスクに関しての制御を細かく行うことができます。 また、DispatchWorkItemFlags というオプションも用意されていて、QoSの細かい挙動等を調整することができます。

おわりに

今回はSwift3におけるGCDの基本的な使い方について書いてみました。GCDは非常に奥が深い世界なので一朝一夕に全てを理解することは難しいかもしれませんが、基本的なことから学んでいきたいと思います。

参考