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

Есть ли указатель SELF для блоков?

Я хотел бы рекурсивно вызывать блок изнутри. В объекте obj-c мы используем "self", есть ли что-то подобное, чтобы ссылаться на экземпляр блока изнутри самого себя?

4b9b3361

Ответ 1

Веселая история! Блоки фактически являются объектами Objective-C. Тем не менее, нет открытого API для получения указателя self блоков.

Однако, если вы объявляете блоки перед их использованием, вы можете использовать их рекурсивно. В среде, не связанной с мусором, вы сделали бы что-то вроде этого:

__weak __block int (^block_self)(int);
int (^fibonacci)(int) = [^(int n) {
    if (n < 2) { return 1; }
    return block_self(n - 1) + block_self(n - 2);
} copy];

block_self = fibonacci;

необходимо применить модификатор __block к block_self, так как в противном случае ссылка block_self внутри fibonacci будет ссылаться на нее до ее назначения (сбой вашей программы на первый рекурсивный вызов). __weak заключается в том, чтобы гарантировать, что блок не фиксирует сильную ссылку на себя, что может вызвать утечку памяти.

Ответ 2

Следующий рекурсивный блок-код будет компилироваться и выполняться с использованием ARC, GC или ручного управления памятью без сбоев, утечек или выдачи предупреждений (анализатор или обычный):

typedef void (^CountdownBlock)(int currentValue);

- (CountdownBlock) makeRecursiveBlock
{
    CountdownBlock aBlock;
    __block __unsafe_unretained CountdownBlock aBlock_recursive;
    aBlock_recursive = aBlock = [^(int currentValue)
    {
        if(currentValue >= 0)
        {
            NSLog(@"Current value = %d", currentValue);
            aBlock_recursive(currentValue-1);
        }
    } copy];
#if !__has_feature(objc_arc)
    [aBlock autorelease];
#endif

    return aBlock;
}

- (void) callRecursiveBlock
{
    CountdownBlock aBlock = [self makeRecursiveBlock];

    // You don't need to dispatch; I'm doing this to demonstrate
    // calling from beyond the current autorelease pool.
    dispatch_async(dispatch_get_main_queue(), ^
                   {
                       aBlock(10);
                   });
}

Важные соображения:

  • Вы должны скопировать блок в кучу вручную, иначе он попытается получить доступ к несуществующему стеку, когда вы вызываете его из другого контекста (ARC обычно делает это для вас, но не во всех случаях. Лучше играть в нее безопасно).
  • Вам понадобятся две ссылки: одна, чтобы удерживать сильную ссылку на блок, и одну, чтобы удерживать слабую ссылку для рекурсивного блока для вызова (технически это необходимо только для ARC).
  • Вы должны использовать квалификатор __block, чтобы блок не фиксировал неназначенное значение ссылки на блок.
  • Если вы выполняете ручное управление памятью, вам необходимо автоматически авторизовать скопированный блок.

Ответ 3

Вы должны объявить переменную блока как __block:

typedef void (^MyBlock)(id);

__block MyBlock block = ^(id param) {
  NSLog(@"%@", param);
  block(param);
};

Ответ 4

Нет блоков self для блоков (пока). Вы можете построить такой, как это (при условии ARC):

__block void (__weak ^blockSelf)(void);
void (^block)(void) = [^{
        // Use blockSelf here
} copy];
blockSelf = block;
    // Use block here

Требуется __block, поэтому мы можем установить blockSelf в блок после создания блока. __weak необходим, поскольку в противном случае блок будет содержать сильную ссылку на себя, что приведет к сильному эталонному циклу и, следовательно, утечке памяти. copy необходим, чтобы убедиться, что блок скопирован в кучу. Это может быть ненужным с новыми версиями компилятора, но это не повредит.