Есть ли SELF-указатель для блоков? - PullRequest
18 голосов
/ 28 января 2011

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

Ответы [ 4 ]

27 голосов
/ 28 января 2011

Веселая история!Блоки на самом деле являются объектами 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 должен гарантировать, что блок не фиксирует сильную ссылку на себя, что может вызвать утечку памяти.

14 голосов
/ 12 мая 2012

Следующий рекурсивный код блока будет скомпилирован и запущен с использованием 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, чтобы блок не захватывал пока еще не назначенное значение ссылки на блок.
  • Если вы выполняете ручное управление памятью, вам необходимоСамовосстановление скопированного блока самостоятельно.
6 голосов
/ 28 января 2011

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

typedef void (^MyBlock)(id);

__block MyBlock block = ^(id param) {
  NSLog(@"%@", param);
  block(param);
};
3 голосов
/ 21 июня 2013

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

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

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

...