Правила захвата переменной блоком в 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/