前回のコードを少し改良してみる。
改良版 Blocks対応NSTimer
前回は userInfo を持回す為に NSMutableDictionary を導入したり、Blocksの引数に userInfo を付けたりしていたが、考えてみると Blocksは定義されているそのレキシカルスコープ内の self やメンバ変数、auto変数が参照できるので userInfo は無くてもいい。そこで userInfo の無い改良版を作ってみた。
まず定義
typedef void (^TIMER_BLOCK__)(NSTimer*); @interface NSTimer (Extension) + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds block:(TIMER_BLOCK__)block repeats:(BOOL)repeats; @end
実装
+ (void)executeBlock__:(NSTimer*)timer { if (![timer isValid]) { return; // do nothing } TIMER_BLOCK__ block = [timer userInfo]; block(timer); } + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds block:(TIMER_BLOCK__)block repeats:(BOOL)repeats { NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:@selector(executeBlock__:) userInfo:[[block copy] autorelease] repeats:repeats]; return timer; }userInfo の持回しが無くなったので、ものすごいシンプルになった。
サンプルによる検証
前回のサンプルを書き換えて上記メソッドを使うようにした。
※ソースコードは GitHubからどうぞ。
NSTimerBlocks at 2010-08-07.2 from xcatsan's iOS-Sample-Code - GitHub
タイマー利用部分はこんな感じ。
- (void)viewDidLoad { [super viewDidLoad]; NSArray* array = [NSArray arrayWithObjects:@"DOG", @"CAT", @"MONKEY", nil]; timer1 = [NSTimer scheduledTimerWithTimeInterval:1.0 block:^(NSTimer* timer) { self.counterLabel1.text = [NSString stringWithFormat:@"%d", counter1++]; NSLog(@"%@", array); } repeats:YES]; timer2 = [NSTimer scheduledTimerWithTimeInterval:2.0 block:^(NSTimer* timer) { self.counterLabel2.text = [NSString stringWithFormat:@"%d", counter2]; counter2 += 10; NSLog(@"%@", array); } repeats:YES]; timer3 = [NSTimer scheduledTimerWithTimeInterval:3.0 block:^(NSTimer* timer) { self.counterLabel3.text = [NSString stringWithFormat:@"%d", counter3]; counter3 += 100; NSLog(@"%@", array); } repeats:YES];前回と比べると userInfo が無くなった。また auto変数 array への参照を Block内に記述してある。実行すると NSLog出力で array の内容が参照出来ていることがわかる。
NSTimerBlocks[4485:207] ( DOG, CAT, MONKEY )
block内でのauto変数参照について
これまで見てきた通り、block内ではその外側で定義されている auto変数への参照も可能である。blockがどのようにして auto変数を扱っているかデバッガで調べてみた。
+[NSTimer executeBlock__] が呼び出された時の引数 block の内容は次の通り。
block のメンバ変数に self と array が存在することがわかる。恐らくコンパイラが blockクラスを定義するときに参照している変数を、blockのメンバ変数として定義するのだと思われる。ただ以前の調査では、blockがスタック上にある場合はスコープを外れるとデバッガ上でこれらのメンバ変数が消えていたことが確認できているので、追加・除去は動的に行われるようだ。
Responses
Leave a Response