Что происходит, когда блок устанавливается равным нулю во время его выполнения?
Предположим, что у меня есть объект с сильной ссылкой на блок. В какой-то момент во время выполнения этого блока сильная ссылка устанавливается на ноль. Блок гарантированно завершит свое выполнение, или это может привести к сбою? Я видел ошибки exc-bad-access, но я не могу их достоверно воспроизвести, поэтому я точно не знаю, почему они появляются.
Например:
-(void)method
{
self.block = ^{
//code
self.block = nil;
//more code - crash here?
}
}
-(void)otherMethod
{
block();
}
3 ответа:
Документы, похоже, не гарантируют, что блок будет сохранен во время его выполнения. И наоборот, документация для вызовов GCD, таких как
dispatch_async
, действительно дает такие гарантии. Из этого следует, что вы не можете предположить, что обычный вызов в блок сохранит его.Итак, в вашем коде вы, вероятно, хотите:
-(void)otherMethod { dispatch_block_t localBlock = Block_copy(block); localBlock(); Block_release(localBlock); }
Я полагаю, что наконец-то получил удовлетворительный ответ на этот вопрос. Обратите внимание, что это все в контексте ARC.
Блок может быть освобожден во время его выполнения. Блок будет продолжать выполняться в обычном режиме, но любой из его указателей на захваченные переменные станет подозрительным (и потенциально опасным).
Предположим, что ObjectA имеет свойство block copy с именем completion:
@property (nonatomic, copy) void (^completion)();
...где задание выглядит примерно так:
__weak ObjectA * weakSelf = self; self.completion = ^{ weakSelf.completion = nil; [weakSelf doSomethingElse]; };
Если блок является называется так...
-(void)method { _completion(); //directly uses ObjectA's instance of the block }
...затем, предполагая, что ничто другое не имеет ссылки на этот экземпляр блока, он освобождается и его захваченная переменная weakSelf становится нулевой. doSomethingElse никогда не вызывается. Лучший способ обойти это-просто вызвать блок с помощью его метода доступа - это выделит новую копию в стеке. Оригинал будет освобожден, но новая копия и все захваченные переменные сохранятся в текущем контексте.
-(void)method { self.completion(); //uses new copy of the block }
Когда метод, выполняющий блок, сначала не проверяет, есть ли еще ссылка на блок, может произойти сбой. У вас могли бы быть эти сбои в методах, если бы чек, как этот, отсутствовал.
- (void)methodWithBlock:(void (^)(void))block { if (block) // this check is to prevent crashes when calling to a released block pointer ... { block(); } }
Вы могли столкнуться с кодом, в котором такая проверка отсутствовала, что могло привести к сбоям, которые вы испытали. Я определенно испытал то же самое.