Swift3のGCD周りのまとめ

Swift3でGrand Central Dispatchも大幅に変わっているので、簡単にまとめました。

# dispatch_queue

concurrent queue（実行スレッドは複数で同時に複数タスク）やserial queue（<del>実行スレッドは１つで</del>タスクごとに違うスレッドで実行される可能性はあるが、同時に1タスク）の生成は、以下のように行います。

```swift
// concurrent queue
let concurrentQueue = DispatchQueue(label: "com.example.concurrent-queue", attributes: .concurrent)
// serial queue
let serialQueue = DispatchQueue(label: "com.example.serial-queue")
```

## dispatch_get_global_queue

global queueは`DispatchQoS.QoSClass`の優先度をもとに、下記のように取得することができます。

```swift
// 上から順に優先度が高いもの
enum QoSClass {
    case userInteractive
    case userInitiated
    case default
    case utility
    case background
    case unspecified
}
```

```swift
let globalQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
```

## dispatch_get_main_queue

main queueは下記のように取得することができます。

```swift
let mainQueue = DispatchQueue.main
```

## dispatch_async

非同期で処理を実行する場合は、下記のようになります。

```swift
let queue = DispatchQueue.global()
queue.async {
    print("dispatch_async")
}
```

## dispatch_sync

同期で処理を実行する場合は、下記のようになります。

```swift
let queue = DispatchQueue.global()
queue.sync {
    print("dispatch_sync")
}
```

## dispatch_after

Swift3ではインターバルを生成するために下記のenumが用意されています。

```swift
enum DispatchTimeInterval {
    case seconds(Int)
    case milliseconds(Int)
    case microseconds(Int)
    case nanoseconds(Int)
}
```

処理を指定した時間が経過してから実行する場合は、下記のようになります。

```swift
let dispatchTime: DispatchTime = DispatchTime.now() + DispatchTimeInterval.seconds(1)
DispatchQueue.global().asyncAfter(deadline: dispatchTime) {
    print("dispatchTime = \(dispatchTime)")
}
```

## dispatch_barrier_async

下記のコードのように0から99までループをさせて、10で割り切れる数字のみ文字列を置き換えて、それ以外の場合はprintするという処理を行います。

```swift
let queue = DispatchQueue(label: "com.sample.barrier", attributes: .concurrent)
var string = ""
for i in 0..<99 {
    guard i % 10 == 0 else {
        queue.async {
            print("\(i) : string = " + string)
        }
        continue
    }
    queue.async {
        let range = string.startIndex..<string.index(string.startIndex, offsetBy: string.characters.count)
        string.removeSubrange(range)
        string += "\(i)"
    }
}
```

並列に非同期で処理しているので、removeSubrange完了時にprintが呼ばれている可能性があり、空文字列の状態がprintされてしまっているときがあります。

```
// 実行結果
4 : string =
7 : string = 0
3 : string =
1 : string =
6 : string =
2 : string =
5 : string = 0
8 : string = 0
9 : string = 0
```

下記のように`async(flags: .barrier)`を使うことで、文字列操作が実行される際のタスクが1つになります。

```swift
let queue = DispatchQueue(label: "com.sample.barrier", attributes: .concurrent)
var string = ""
for i in 0..<99 {
    guard i % 10 == 0 else {
        queue.async {
            print("\(i) : string = " + string)
        }
        continue
    }
    queue.async(flags: .barrier) {
        let range = string.startIndex..<string.index(string.startIndex, offsetBy: string.characters.count)
        string.removeSubrange(range)
        string += "\(i)"
    }
}
```

```
// 実行結果
6 : string = 0
7 : string = 0
8 : string = 0
9 : string = 0
11 : string = 10
13 : string = 10
12 : string = 10
15 : string = 10
14 : string = 10
```

# dispatch_group

`async`を使ったり、`wait`を使う場合は、下記のようになります。

```swift
let group = DispatchGroup()
for i in 0..<100 {
    DispatchQueue.global().async(group: group) {
        print(i)
    }
}

_ = group.wait(timeout: .distantFuture)

print("after wait")
```

```
// 実行結果
1
2
// 中略
98
99
after wait
```

`enter`や`leave`を使ったり、`nofity`を使う場合は、下記のようになります。

```swift
let group = DispatchGroup()
for i in 0..<100 {
    group.enter()
    DispatchQueue.global().async {
        print(i)
        group.leave()
    }
}

group.notify(queue: .global()) {
    print("notify closure called")
}
        
print("after notify")
```

```
// 実行結果
1
2
// 中略
98
99
after notify
notify closure called
```

# dispatch_once

Swift3では`dispatch_once`に相当するものがなくなりました。
シングルトンのインスタンスを一度だけ初期化するなどの処理は

```swift
class Hoge {
    static let shared = Hoge()
}
```

のように`static`なPropertyを`let`で定義することで実現できます。

一方、一度だけ実行したい処理がある場合は

```swift
class ViewController: UIViewController {
    
    lazy var __once: Void = {
        self.printSomething()
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        _ = __once // Void型のPropertyの初期化が一度だけ実行されるので、`self.printSomething()`が呼ばれる
        _ = __once // 既にPropertyの初期化が完了しているので、`self.printSomething()`が呼ばれない
    }
    
    func printSomething() {
        print("this method might be executed once.")
    }
}
```

のように、`Void`型のPropertyをlazyで初期化を行い、その中で処理を実行することで実現できます。

上記の方法は

> The free function `dispatch_once` is no longer available in Swift. In Swift, you can use lazily initialized globals or static properties and get the same thread-safety and called-once guarantees as `dispatch_once` provided. Example:
>> ```
let myGlobal = { … global contains initialization in a call to a closure … }()
_ = myGlobal  // using myGlobal will invoke the initialization code only 
```

のように[Migrating to Swift 2.3 or Swift 3 from Swift 2.2](https://swift.org/migration-guide/)に記載されています。