これは、私が取り締まることができないというトリッキーな問題です。
Obj-C ブロック自体はクロージャではなく、その実装は Javascript クロージャとは多少異なることは理解していますが、達成しようとしていることを示すために Javascript の例を使用します (Javascript に精通している人は理解できます)。 .
Javascript では、以下のような「関数ファクトリ」を作成できます。
//EXAMPLE A
var _arr = [], i = 0;
for(;i<8;++i) {
_arr[i] = function() {
console.log('Result:' + i);
};
}
//BY THE END OF THIS LOOP i == 7
_arr[0]();
_arr[1]();
_arr[2]();
...
_arr[7]();
_arr という名前の配列に対応する関数を入力し、それらすべてを評価します。上記のコードの結果が出力されることに注意してください...
Result: 7
Result: 7
Result: 7
...
Result: 7
... すべての関数で '7' が正しいのは、関数が評価されるまでに i の値が 8 に等しいためです。作成中の i の値は 0...7 ですが、ここではi は値渡しではなく参照渡しであると結論付けます。
これを「修正」し、各関数が作成時に i の値を使用するようにしたい場合は、代わりに次のように記述します。
//EXAMPLE B
var _arr = [], i = 0;
for(;i<8;++i) {
_arr[i] = (function(new_i){
return function() {
console.log(new_i);
};
})(i); //<--- HERE WE EVALUATE THE FUNCTION EACH TIME THE LOOP ITERATES, SO THAT EVERYTHING INSIDE OF THIS 'RETAINS' THE VALUES 'AT THAT MOMENT'
}
//BY THE END OF THIS LOOP i == 7, BUT IT DOESN'T MATTER ANYMORE
_arr[0]();
_arr[1]();
_arr[2]();
...
_arr[7]();
最終関数を直接作成する代わりに、最終関数を返す中間クロージャーを使用し、その内部に正しい値が「固定」されています。その結果、以下が返されます。
Result: 0
Result: 1
Result: 2
...
Result: 7
今...
Objective-C ブロックを使用して同じことをしようとしています。
例 A (Obj-C) のコードは次のとおりです。
NSMutableArray *_arr = [NSMutableArray arrayWithCapacity:0];
int i = 0;
for(;i<8;++i) {
[_arr addObject:^{
NSLog(@"Result: %i", i);
}];
}
//BY THE END OF THIS LOOP i == 7
[_arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
((void (^)())obj)();
}];
そして、これは出力されます...
Result: 7
Result: 7
...
Result: 7
...関数が実際に i への参照を保持しているため、これも正しいです。
問題は、例 B に示されている動作をエミュレートするために、上記のループをどのように書き直すべきかということです。(関数作成時の値を保持しています)
私は次のようにループを書いてみました:
for(;i<8;++i) {
[_arr addObject:^(int new_i){
return ^{
NSLog(@"Result: %i", new_i);
};
}(i)];
}
しかし、コンパイル時に次のエラーが発生します: Returning block that lives on local stack
ありがとう、そして最高;D!