I\'d like to recursively call a block from within itself. In an obj-c object, we get to use \"self\", is there something like this to refer to a block instance from inside i
You have to declare the block variable as __block
:
typedef void (^MyBlock)(id);
__block MyBlock block = ^(id param) {
NSLog(@"%@", param);
block(param);
};
There is no self
for blocks (yet). You can build one like this (assuming ARC):
__block void (__weak ^blockSelf)(void);
void (^block)(void) = [^{
// Use blockSelf here
} copy];
blockSelf = block;
// Use block here
The __block
is needed so we can set blockSelf
to the block after creating the block. The __weak
is needed because otherwise the block would hold a strong reference to itself, which would cause a strong reference cycle and therefore a memory leak. The copy
is needed to make sure that the block is copied to the heap. That may be unnecessary with newer compiler versions, but it won't do any harm.
Fun story! Blocks actually are Objective-C objects. That said, there is no exposed API to get the self
pointer of blocks.
However, if you declare blocks before using them, you can use them recursively. In a non-garbage-collected environment, you would do something like this:
__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;
It is necessary to apply the __block
modifier to block_self
, because otherwise, the block_self
reference inside fibonacci
would refer to it before it is assigned (crashing your program on the first recursive call). The __weak
is to ensure that the block doesn't capture a strong reference to itself, which would cause a memory leak.
The following recursive block code will compile and run using ARC, GC, or manual memory management, without crashing, leaking, or issuing warnings (analyzer or regular):
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);
});
}
Important considerations: