読者です 読者をやめる 読者になる 読者になる

忘れないでGCD(復習しよう)

iOS Engineering

このエントリーは、iOS Advent Calendar 2014 の 2日目です。 2日目なので、Swiftとかではない送りバンドな記事で行きます。

非同期処理が多く求められるモバイルアプリ開発の現場では、ReactiveCocoaやRxJava等のFrameworkが注目を浴びている。 しかし、意外と基本となるGCD(Grand Central Dispatch)のことを忘れがち。

FacebookTwitterからタイムラインを取得しいい具合に表示する案件をやっていた時、非同期処理とNSNotificationを多様した難解な実装となっていた。 これもdispatch_groupやdispatch_barrier_asyncを使えば解決できるんだよね。 いい機会だし、復習してみよう。

GCDとは

Dispatch queueにBlocksとして実行したいタスクを渡し実行できる。このqueueには2種類ある。

  • Serial dispatch queue:タスクを逐次的に実行
  • Concurrent dispatch queue:他のタスクを待たずに実行

実際に使うときは、描画を行うメインスレッドであるMain dispatch queue(Serial dispatch queue)と、iOSがいい具合に判断しスレッドを作り実行してくれるGlobal dispatch queueのどちらかを選択して使う事になる。Global dispatch queueでは、優先度が選べるがこれはあくまで目安なので注意。

Serial dispatch queue

    dispatch_queue_t queue = dispatch_queue_create("com.jsk.test", NULL);
    for (int i = 0; i < 5; i++) {
        dispatch_async(queue, ^{NSLog(@"%d", i); });
    }

Output

0
1
2
3
4

Concurrent dispatch queue

    dispatch_queue_t queue = dispatch_queue_create("com.jsk.test", NULL);
    for (int i = 0; i < 5; i++) {
        dispatch_async(queue, ^{NSLog(@"%d", i); });
    }

Output(順番は実行時によって異なる)

4
0
2
1
3

Dispatch Groupでちょっと待つ

Dispatch Groupを使うと、queueに追加する処理をグループ化し、全ての処理が完了した事を受け取る事ができる。 これを使えば、非同期通信が複数走っている場合等の処理をまとめることができて便利。 使い方はとっても簡単。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    
    for (int i = 0; i < 3; i++) {
        dispatch_group_async(group, queue, ^{
            NSLog(@"%d", i);
        });
    }
    dispatch_group_notify(group, queue, ^{
        NSLog(@"Done");
    });

Output

2
0
1
Done

また、dispatch_group_waitを用いる事で処理をその箇所で止める事もできる。

Dispatch Barrier Async

上記Dispatch Groupと少し似ているが、Dispatch barrier asyncでは、Concurrent dispatch queueに追加された処理が実行完了されるまで待ち、Serial Dispatch Queueに新たなタスクを追加し、そのタスクが実行完了されるまで待つことが可能だ。 例えば以下のように使うことができる。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{NSLog(@"1");});
    dispatch_async(queue, ^{NSLog(@"2");});
    dispatch_async(queue, ^{NSLog(@"3");});
    dispatch_barrier_async(queue, ^{NSLog(@"wait");});
    dispatch_async(queue, ^{NSLog(@"4");});
    dispatch_async(queue, ^{NSLog(@"5");});

dispatch_barrier_asyncメソッドを使うだけで、前の処理を待ってくれる。なんて便利なんだ。

まとめ

GCDを復習した。 dispatch_groupやdispatch_barrier_asyncを用いて他の非同期処理を待つ事ができる。 これでsemaphoreで無理やり処理していた箇所が書き換える事ができそうだ。

おまけ

Rebuild.fmで紹介されていたHBOのSilicon Valleyを見ている。あの近辺でのスタートアップがリアルに描かれていてかなり面白く、おすすめだ。