いまさらながら、GCDのサンプルソースはMac Dev Centerにもいろいろありますので参考に。中でもDispatch_Samplesなんかはシンプルなサンプルがたくさん集まってますので、最初に見るには良いと思います。
今回からはディスパッチの中でもソースというものを見ていこうと思います。前回のエントリにキューとソースは別なんて一度書いてしまいましたが、間違いでソースでもキューは使います。
と書いてて、ディスパッチのソースと、ソースコードの区別がわけ分からなくなりそうなので、ディスパッチのソースは「ディスパッチソース」と呼ぶ事にします。
ディスパッチソースの種類にはいろいろありますが、基本的には、何かしら外部からイベントを受け取ったらキューにブロックを渡すというもののようです。以下のようなものがあります。
DISPATCH_SOURCE_TYPE_DATA_ADD: DISPATCH_SOURCE_TYPE_DATA_OR: DISPATCH_SOURCE_TYPE_MACH_SEND DISPATCH_SOURCE_TYPE_MACH_RECV DISPATCH_SOURCE_TYPE_PROC DISPATCH_SOURCE_TYPE_READ DISPATCH_SOURCE_TYPE_SIGNAL DISPATCH_SOURCE_TYPE_TIMER DISPATCH_SOURCE_TYPE_VNODE DISPATCH_SOURCE_TYPE_WRITE
まあ、ほとんどは自分じゃ使わないなぁと思って調べてなかったりするんで、簡単なそうなタイマーを見ていきます。まずはサンプルソースから。
#include <stdio.h> #include <stdlib.h> #include <dispatch/dispatch.h> int main (int argc, const char * argv[]) { dispatch_queue_t queue = dispatch_queue_create("timerQueue", 0); //タイマーのソースを作成 dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); __block int i = 0; //イベントを受け取った時に実行されるブロックをソースに設定する dispatch_source_set_event_handler(timer, ^{ printf("%d\n", i); i++; if (i == 10) dispatch_source_cancel(timer); }); //ソースがキャンセルされてときに実行されるブロックをソースに設定する dispatch_source_set_cancel_handler(timer, ^{ dispatch_release(timer); dispatch_release(queue); printf("end\n"); exit(0); }); dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC); //今から1秒後 uint64_t interval = NSEC_PER_SEC / 5; //0.2秒 //タイマーを設定する dispatch_source_set_timer(timer, start, interval, 0); printf("start\n"); dispatch_resume(timer); dispatch_main(); return 0; }
実行すると、ログに0から10まで数字が0.2秒おきに出力されて、アプリが終了します。
では、コードを上から見ていきます。まずは、タイマーのディスパッチソースを作成しています。ディスパッチソースの作成はdispatch_source_createです。
dispatch_source_t dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue);
ひとつめの引数にはディスパッチソースの種類を渡します。タイマーはDISPATCH_SOURCE_TYPE_TIMERです。2つめの引数のhandleは、ディスパッチソースが動作する元になるものを渡しますが、タイマーは必要ないので0で良いです。3つ目もタイマーでは必要ありませんので、これも0です。最後のqueueには、ディスパッチソースからブロックを渡すキューを設定します。
ちなみに、キューはグローバルでもシリアルでも使えますが、グローバルキューだと他の色んなブロックが入っていたら後回しになって影響を受けて実行されるのが遅れやすくなりそうなので、シリアルキューにして完全にセパレートされている方が良いのかなぁと思います。あくまで予測ですけど。
dispatch_source_set_event_handlerは、イベントを受け取ったときに呼ばれるブロックを設定します。今回はタイマーですので、タイマーが定期的にイベントを投げてきて、ここのブロックが呼ばれる事になります。
dispatch_source_set_cancel_handlerはディスパッチソースがキャンセルされたときに呼ばれるブロックを設定します。基本、タイマーのディスパッチソースやキューを解放します。
dispatch_source_set_timerでは、タイマーの開始時間とインターバルを指定します。スタートの時間は、dispatch_timeを使うと今から何秒後みたいな感じで絶対時間のdispatch_time_tが取得できます。インターバルは、1秒がNSEC_PER_SECで定義されていますから、そこから求めるのが良いと思います。
で、忘れていけないのは、dispatch_resumeです。ディスパッチソースは作った段階ではsuspend状態ですので、resumeで発動させなければいけません。