Нужно ли использовать спецификатор типа __block для изменяемых объектов, измененных в блоках?

Скажем, карта - это NSDictioanry, насколько я понимаю, поскольку адрес res не меняется в блоке, нам не нужно использовать для него __block. Это правильно?

//__block NSMutableArray *res = [@[] mutableCopy];
NSMutableArray *res = [@[] mutableCopy];
__block NSInteger i = 0;
[map enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSArray *v, BOOL *stop) {
    [res addObject:v];
    i++;
}];

Кстати, я запустил код, он отлично работает.


person user3315620    schedule 19.02.2017    source источник
comment
Компилятор жалуется? Запустите код и напечатайте res. Что просходит?   -  person vadian    schedule 19.02.2017
comment
я запускал, работает нормально   -  person user3315620    schedule 19.02.2017
comment
Тогда ответ: Нет :-) Компилятор сообщит вам, если __block отсутствует.   -  person vadian    schedule 19.02.2017
comment
Кстати, нет никакой реальной причины использовать [@[] mutableCopy] — он не быстрее, чем [NSMutableArray array], и менее понятен для чтения.   -  person Itai Ferber    schedule 19.02.2017


Ответы (2)


Спецификатор типа __block необходим только для хранения изменяемых элементов, которые обычно помещаются в стек.

Поскольку не гарантируется, что блок будет вызываться только в рамках метода, который его создает (это называется экранированием), может потребоваться изменить переменные, на которые он ссылается, после того, как они исчезли. Ссылки на вещи, которые находятся в стеке (например, i в вашем примере), могут стать висячими указателями, если блок удерживается за концом исходного метода или функции, поэтому, если они должны быть изменены, их нужно хранить где-то в другом месте. чем стек. Спецификатор __block говорит компилятору поместить переменную в отдельное хранилище, которое будет действительным при каждом вызове блока.

Для вещей, которые размещаются в куче (например, res выше), это не обязательно, так как указатель на объект не изменится, пока объект находится рядом (что и должно быть, так как блок сохранит его по умолчанию), локальное хранилище указателя объекта не нужно помещать в альтернативное хранилище, и оно может оставаться в стеке.

Если вы хотите узнать больше об этом, Блоки и переменные содержится подробная информация об управлении памятью с помощью блоков и многое другое о типе хранилища __block.

person Itai Ferber    schedule 19.02.2017

Это верно. __block необходим только в том случае, если вы присваиваете (= или эквивалент) переменной внутри блока или если вы хотите, чтобы блок мог видеть присвоения переменной вне блока после того, как блок был создан. (Это верно независимо от типа переменной.)

Здесь переменная i назначается внутри блока с оператором ++, поэтому необходимо __block. Переменная res никогда и нигде не присваивается; переменная res читается из (не присваивается) внутри блока.

person newacct    schedule 28.02.2017