Я хотел бы рекурсивно вызывать блок изнутри. В объекте obj-c мы используем "self", есть ли что-то подобное, чтобы ссылаться на экземпляр блока изнутри самого себя?
Есть ли указатель SELF для блоков?
Ответ 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
необходим, чтобы убедиться, что блок скопирован в кучу. Это может быть ненужным с новыми версиями компилятора, но это не повредит.