Подтвердить что ты не робот

Как код внутри блока Objective-C ссылается на объект блока?

self - это всего лишь захваченная переменная внутри блока и не ссылается на сам блок, поэтому как сама ссылка блока не имеет явной захваченной переменной для этой цели?

4b9b3361

Ответ 1

__block void(^strawberryFields)();
strawberryFields = [^{ strawberryFields(); } copy];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),
               strawberryFields);
  • вы используете __block, потому что блок создаст копию значения strawberryFields, когда будет создан блок, который будет перед назначением.

  • вы также должны copy блокировать до любой другой операции копирования, иначе вы получите блок, который ссылается на исходную версию на стеке.

  • обратите внимание, что приведенный выше код утечки блока. Где-то там должен быть release этого блока, чтобы сбалансировать копию.

Ответ 2

Я нашел этот шаблон работоспособным и стабильным для ARC (автоматический подсчет ссылок), как в сборках Debug, так и Release.

-(void) someMethod
{
    // declare a __block variable to use inside the block itself for its recursive phase.
    void __block (^myBlock_recurse)();

    // define the block
    void (^myBlock)() = ^{
        // ... do stuff ...
        myBlock_recurse(); // looks like calling another block, but not really.
    };

    // kickstart the block
    myBlock_recurse = myBlock; // initialize the alias
    myBlock(); // starts the block
}

Сначала я попробовал просто поместить модификатор __block в myBlock и использовать эту переменную непосредственно для рекурсии в реализации блока. Это работает в сборке ARC Debug, но ломается с EXC_BAD_ACCESS в сборке Release. С другой стороны, удаление модификатора __block вызывает предупреждение "переменная, не определенная при захвате блоком" (и я не хотел ее запускать и тестировал).

Ответ 3

Я никогда не пробовал это раньше, а не 100% уверен, что это полезно, если оно действительно, но, например:

typedef void (^BasicBlock)(void);

__block BasicBlock testBlock;
testBlock  = ^{NSLog(@"Testing %p", &testBlock);};
testBlock();

Вероятно, вы указали переменную с __block, чтобы предотвратить цикл самообеспечения.

Ответ 4

Блок нуждается в некотором роде для извлечения собственной ссылки. Обычно это делается путем сохранения блока в свойстве класса.

Иногда вы можете не использовать свойство. Вот как вы это делаете без свойства:

    __weak id weakSelf = self;
    __block id block = ^{

        if(weakSelf) {

            // .. do whatever

            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), block);
        }
        else {

            block = nil;
        }
    };

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), block);

Главное, что нужно иметь в виду, это то, что все пути кода должны приводить к блоку = nil. Мы делаем это здесь, вызывая блок каждые 5 секунд, пока weakSelf не станет нулевым.

Ответ 5

Обратите внимание, что в ARC это немного отличается - переменные указателя объекта __block по умолчанию сохраняются в ARC, в отличие от MRC. Таким образом, это вызовет цикл удержания. Необходимо, чтобы блок фиксировал слабую ссылку на себя (используя __weak), чтобы не иметь цикла сохранения.

Однако нам все еще нужна сильная ссылка на блок где-то. Если нет сильных ссылок, блок (который находится на куче с момента его копирования) будет освобожден. Таким образом, нам нужны две переменные: одна сильная и одна слабая, а внутри блока используется слабый для ссылки:

__block __weak void(^weakBlock)();
void(^myBlock)();
weakBlock = myBlock = [^{ weakBlock(); } copy];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),
               myBlock);