GCDについて情報整理する


久々にGCDを使ったコードを書くたびに、「あれっ？dispatch_get_global_queue()の第2引数って0だった？NULLだった？」とか「シリアルディスパッチキューってどうやってつくるんだったっけ？」とかになることがよくあるので、歴史的なことも含めてGCDに関する情報を整理しておく。


# GCDの歴史
- Mac OS X 10.6(Snow Leopard)以降、iOS 4.0以降で利用可能
- OS X 10.8(Mountain Lion)、iOS 6.0以降では、GCDオブジェクト(Dispatch Queue）もARCの管轄下に入るようになった(実際にはGCDだけでなくXPCも)
- OS X 10.10(Yosemite)、iOS8.0 からQoS(Quality of Service)の概念が導入された


# ディスパッチキューの利用
## ディスパッチキューの利用には大きく分けて2つの方法がある
1. dispatch_queue_create関数を使ってディスパッチキューを自分で生成する
2. システムが標準で提供しているディスパッチキューを取得する


### 1. dispatch_queue_create関数を使ってディスパッチキューを自分で生成する
- 開発者はシリアルキューとコンカレントキューを生成できる
- コンカレントキューを自分で生成するケースは稀
- OS X v10.7、iOS 4.3以降から第2引数にNULL以外を指定できるようになった

#### 1.1. シリアルキューを生成する場合

```objectivec:シリアルキューの生成方法
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.MySerialDispatchQueue", DISPATCH_QUEUE_SERIAL);
// または、下記の通り、第2引数にNULLを指定してもよい。
// ただし、個人的には明示的に定数指定している上記の書き方の方がいいと思う。
//dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.MySerialDispatchQueue", NULL);
```

#### 1.2. コンカレントキューを生成する場合

```objectivec:コンカレントキューの生成方法
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.example.MyConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT);
```

---

### 2. システムが標準で提供しているディスパッチキューを取得する
- システムが標準で提供しているディスパッチキューは2種類ある(Main Dispatch QueueとGlobal Dispatch Queue)
- アプリのどこからでも利用できる

#### 2.1. Main Dispatch Queue
- メインスレッドで実行されるシリアルキュー(メインスレッドは1つしかないため)
- 実際にはメインスレッドで実行されるデフォルトのRunLoopで処理される
- Main queue is access queue for UI

```objectivec:MainDispatchQueueの取得方法
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
```


#### 2.2. Global Dispatch Queue
- コンカレントキュー
- 実行優先度別に4種類(従来方式)または5種類(QoS方式)ある
- 2015年5月現在においてバックワードコンパチを考慮(例えばiOS7.xをサポート)する場合は必然的に従来方式を採用することになる

```objectivec:従来方式
// OS X 10.10(Yosemite)、iOS8.0以前でも以降でも利用できる
// 第2引数は0固定

dispatch_queue_t globalDispatchQueuePriorityHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

dispatch_queue_t globalDispatchQueuePriorityDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_queue_t globalDispatchQueuePriorityLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

dispatch_queue_t globalDispatchQueuePriorityBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
```

```objectivec:QoS方式
// OS X 10.10(Yosemite)、iOS8.0以降でしか利用できない
// 第2引数は0固定

// 基本的にはQoSに関する明確な意思を決めて
// QOS_CLASS_DEFAULT以外を選択するのが良さげ。
// また、QOS_CLASS_DEFAULTについては下記の通り。
// Ordered between UI and non-UI QoS
// Not intented as a work classification

dispatch_queue_t globalDispatchQueueQosInteractive = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0);

dispatch_queue_t globalDispatchQueueQosInitiated = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0);

dispatch_queue_t globalDispatchQueueQosDefault = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);

dispatch_queue_t globalDispatchQueueQosUtility = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);

dispatch_queue_t globalDispatchQueueQosBackground = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
```

# コード例
## ワーカスレッドがシリアルの場合

```objectivec:ワーカスレッドがシリアルの場合
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.MySerialDispatchQueue", DISPATCH_QUEUE_SERIAL);
    
dispatch_async(mySerialDispatchQueue, ^{
    // ワーカスレッド(シリアル)
    // 重い処理をここに書く
    
    dispatch_async(dispatch_get_main_queue(), ^{
        // メインスレッド(シリアル)
        // UI処理をここに書く
    });
    
});
```

## ワーカスレッドがコンカレントの場合

```objectivec:ワーカスレッドがコンカレント
//dispatch_queue_t globalDispatchQueuePriorityDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // ワーカスレッド(コンカレント)
    // 「コンカレント実行されても問題ない」重い処理をここに書く
    
    dispatch_async(dispatch_get_main_queue(), ^{
        // メインスレッド(シリアル)
        // UI処理をここに書く
    });
    
});
```