Что происходит, когда блок устанавливается равным нулю во время его выполнения?


Предположим, что у меня есть объект с сильной ссылкой на блок. В какой-то момент во время выполнения этого блока сильная ссылка устанавливается на ноль. Блок гарантированно завершит свое выполнение, или это может привести к сбою? Я видел ошибки exc-bad-access, но я не могу их достоверно воспроизвести, поэтому я точно не знаю, почему они появляются.

Например:

-(void)method
{
    self.block = ^{
        //code
        self.block = nil;
        //more code - crash here?
    }
}

-(void)otherMethod
{
    block();
}
3 4

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();
   }
}

Вы могли столкнуться с кодом, в котором такая проверка отсутствовала, что могло привести к сбоям, которые вы испытали. Я определенно испытал то же самое.