ブロックはスタック上でライフを開始するため、ブロックのライフスパンはそれが宣言されているスコープの間だけです。
for() ループの本体 ({} 内のループの本体) は、それ自体がスコープです。したがって、あなたのコードは、スタック [ブロック] 上の何かへの参照を、周囲のスコープ [言語配列] の変数に入れています。
ブロックを存続させるには、ブロックをヒープにコピーする必要があります。
void dontDoThis() {
void (^blockArray[3])(void); // an array of 3 block references
for (int i = 0; i < 3; ++i) {
blockArray[i] = [^{ printf("hello, %d\n", i); } copy];
}
}
ARC を使用しない場合は-release
、ある時点でコピーされたブロックも必要になります。
このブログ投稿が便利だと思うかもしれません(Blocks が公開された直後に書きました)。これは、いくつかのヒント、トリック、および落とし穴に入ります。
待ってください -- そうです -- あなたの言う通りです。ブロックが魔法のようにヒープ上にあるように見える魔法が ARC コンパイラーで進行中です。ただし、この動作を明示的に文書化した LLVM ドキュメントには何も見つかりません。ARC をオフにすると、出力は 0,1,2 ではなく 2,2,2 のようになります。
これはやや新しい動作です。これがどのようにサポートされているかを正確に定義する明示的なメモがコンパイラで見つかるまで、私はこの動作に依存しません。
@autoreleasepool {
void (^blockArray[3])(void); // an array of 3 block references
for (int i = 0; i < 3; ++i) {
void (^block)(void) = ^{ printf("hello, %d\n", i); };
NSLog(@"%p", block);
blockArray[i] = block;
NSLog(@"%p", blockArray[i]);
}
for (int i = 0; i < 3; ++i) blockArray[i]();
}
出力:
2012-12-24 16:15:36.752 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.755 jkdfjkfdjkdfjk[70708:303] 0x100108160
2012-12-24 16:15:36.758 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.759 jkdfjkfdjkdfjk[70708:303] 0x100108000
2012-12-24 16:15:36.760 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.760 jkdfjkfdjkdfjk[70708:303] 0x100102e70
hello, 0
hello, 1
hello, 2
したがって、ブロックはスタック上に作成され、 for() ループの範囲外の割り当てで自動的にヒープにコピーされます。
同様のテストでは、ブロックが引数として NSArray の addObject: に渡されたときにコピーされることも明らかになりました。