NSBlockOperation
NSBlockOperation Class Reference
NSBlockOperation は従来 -[NSOperation main] に実装していた処理を Blocksで書くことができる。こんな感じ。
NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock: ^{ // 処理1 }];
NSOperation のサブクラスなので実行開始は start を投げてやればよい。
[operation start];複数の処理を走らせたい時は addExecutionBlock: で処理 Blocksを追加していく。
[operation addExecutionBlock:^{ // 処理2 }];終了時に処理を実行したい場合は iOS4 から NSOperation に追加された setCompletionBlock: で処理を指定しておくことができる。
NSOperation Class Reference - setCompletionBlock:
[operation setCompletionBlock:^{ // 終了時処理 }];なお start で実行した後は、すべての処理が完了するまでその場でブロックされる。
サンプル
NSBlockOperation を使ったサンプルプログラムを作って動きを確かめてみた。
実行すると2つの処理が同時に走る。thread1は1秒間隔でラベルを更新していく。thread2の方は2秒間隔で更新する。
ソースコードはこんな感じ。
- (IBAction)start:(id)sender { NSLog(@"setup"); NSLog(@"1: %@", [NSThread currentThread]); NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock: ^{ NSLog(@"2: %@", [NSThread currentThread]); for (int i=0; i < 10; i++) { [label performSelectorInBackground:@selector(setText:) withObject:[NSString stringWithFormat:@"running: %d", i+1]]; [NSThread sleepForTimeInterval:1.0]; } }]; [operation addExecutionBlock:^{ NSLog(@"3: %@", [NSThread currentThread]); for (int i=0; i < 5; i++) { [label2 performSelectorInBackground:@selector(setText:) withObject:[NSString stringWithFormat:@"running: %d", i+1]]; [NSThread sleepForTimeInterval:2.0]; } }]; [operation setCompletionBlock:^{ NSLog(@"completion"); label.text = @"completion"; label2.text = @"completion"; }]; NSLog(@"start"); [operation start]; NSLog(@"end"); }UILabelの更新はメインスレッドで行う必要があるため -[NSObject performSelectorInBackground:withObject:] を使っている。
動作中のコンソール:
setup 1: <NSThread: 0x590db20>{name = (null), num = 1} start 2: <NSThread: 0x590db20>{name = (null), num = 1} 3: <NSThread: 0x5d36840>{name = (null), num = 3} end 4: <NSThread: 0x5d36840>{name = (null), num = 3}
これを見ると次のことがわかる。
- 1番目の処理はメインスレッドで走っている(NSThreadが同じ)
- すべての処理が完了するまでブロックされる
- completion処理はブロック解除後に非メインスレッドで実行される
最後の動作が腑に落ちない感じもするがそういうものなのだろう。
ソースは GitHub からどうぞ。
WaitScreenSample at 2010-09-05 from xcatsan's iOS-Sample-Code - GitHub
実機での動作
ここまでは iPhone シミュレータでの話。
実機で走らせるとまた違った動作になった。
setup 1: <NSThread: 0x105210>{name = (null), num = 1} start 2: <NSThread: 0x105210>{name = (null), num = 1} 3: <NSThread: 0x105210>{name = (null), num = 1} end 4: <NSThread: 0x151820>{name = (null), num = 18}
メインスレッドだけで処理が走っている?実際画面上の動作を見ても thread1 のカウントアップが終了した後、thread2のカウントアップが走る。同時には動作しない。
うーむなぜだろう。書き方が悪いのか?
...今日は時間切れ。
参考情報
- Concurrency Programming Guide: Operation Queues (日本語訳) - steelwheelsの日記
- Concurrency Programming Guide: Introduction
- Using Concurrency To Improve Responsiveness
- Togetter - 「【技術情報】iOS 4のNSOperationはGCD使用か否かの俺様用まとめ」
Responses
Leave a Response