Правила захвата переменной блоком в objective-C
какова семантика захвата переменной блоком в objective-C?
#import <Foundation/Foundation.h>
#include <stdio.h>
int main()
{
NSMutableArray *arr = [NSMutableArray array];
for (int i = 0; i < 100; ++i) {
int j = i;
[arr addObject:^(void) {printf("%d %dn", i, j); }];
}
for (void (^blk)(void) in arr) {
blk();
}
}
Я был expecing это, чтобы напечатать что-то вроде:
100 0
100 1
...
100 99
вместо этого он печатает:
99 99
99 99
...
99 99
как это вообще возможно, что это перевод j
равным 99
? j
даже не жив вне цикла for.
2 ответов
потому что вы не используете ARC! Без него ваш блок не копируется. Тебе просто везет, и ты каждый раз пробегаешь последний квартал.
причина, по которой вы видите 99 99
много раз просто из-за неопределенного поведения.
давайте возьмем первый for-loop:
for (int i = 0; i < 100; ++i) {
int j = i;
dispatch_block_t block = ^(void) {printf("%d %d\n", i, j); };
[arr addObject:block];
}
[я вытащил блок для ясности.]
внутри этого for-loop создается блок. Он создается в стеке и никогда не перемещается в кучу, потому что для этого нет копии блока.
каждый раз вокруг for-loop чрезвычайно вероятно (ну, действительно), что одно и то же пространство стека используется для блока. А затем адрес блока (который находится в стеке) добавляется в arr
. Каждый раз один и тот же адрес. Но каждый раз это новая реализация блока.
после выхода из первого for-loop,arr
содержит то же значение 100 раз. И это значение указывает на последний созданный блок, который все еще находится в стеке. Но он указывает на блок в стеке, к которому больше нельзя безопасно получить доступ, потому что он выходит за рамки.
в случае этого пример однако пространство стека, занимаемое блоком чистой удачей (ОК, простой код), не было повторно использовано. Поэтому, когда вы идете и используете блок, он "работает".
правильным решением является копирование блока при его добавлении в массив. Либо позвонив copy
или позволить арку сделать это за вас. Таким образом, блок копируется в кучу, и у вас есть ссылочный подсчитанный блок, который будет жить столько, сколько необходимо массиву и области, в которой он создан.
если вы хотите узнайте больше о том, как работают блоки и понять этот ответ более глубоко, то я предлагаю свои объяснения здесь:
http://www.galloway.me.uk/2012/10/a-look-inside-blocks-episode-1/ http://www.galloway.me.uk/2012/10/a-look-inside-blocks-episode-2/ http://www.galloway.me.uk/2013/05/a-look-inside-blocks-episode-3-block-copy/