Suppose I run this code:
__block int step = 0;
__block dispatch_block_t myBlock;
myBlock = ^{
if(step == STEPS_COUNT)
{
return;
}
step++;
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
dispatch_after(delay, dispatch_get_current_queue(), myBlock);
};
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
dispatch_after(delay, dispatch_get_current_queue(), myBlock);
The block is invoked once from outside. When the inner invocation is reached, the program crashes without any details. If I use direct invocations everywhere instead of GCD dispatches, everything works fine.
I've also tried calling dispatch_after with a copy of the block. I don't know if this was a step in the right direction or not, but it wasn't enough to make it work.
Ideas?
When trying to solve this problem, I found a snippet of code that solves much of the recursive block related issues. I have not been able to find the source again, but still have the code:
RecursiveBlock
allows for non-argument blocks. It can be rewritten for single or multiple argument blocks. The memory management is simplified using this construct, there is no chance of a retain cycle for example.It looks like there are no problem except delay variable. The block uses always the same time that is generated at line 1. You have to call dispatch_time every time if you want to delay dispatching the block.
EDIT:
I understand.
The block is stored in stack by the block literal. myBlock variable is substituted for the address of the block in stack.
First dispatch_after copied the block from myBlock variable that is the address in stack. And this address is valid at this time. The block is in the current scope.
After that, the block is scoped out. myBlock variable has invalid address at this time. dispatch_after has the copied block in heap. It is safe.
And then, second dispatch_after in the block tries to copy from myBlock variable that is invalid address because the block in stack was already scoped out. It will execute corrupted block in stack.
Thus, you have to Block_copy the block.
And don't forget Block_release the block when you don't need it any more.
I think you have to copy the block if you want it to stick around (releasing it when you don't want it to call itself anymore).
Opt for a custom dispatch source.
My solution was derived entirely from Berik's, so he gets all the credit here. I just felt that a more general framework was needed for the "recursive blocks" problem space (that I haven't found elsewhere), including for the asynchronous case, which is covered here.
Using these three first definitions makes the fourth and fifth methods - which are simply examples - possible, which is an incredibly easy, foolproof, and (I believe) memory-safe way to recurse any block to arbitrary limits.
You'll note that in the following two examples that the machinery of interacting with the recursion mechanism is extremely lightweight, basically amounting to having to wrap a block in
recurse
and that block must take aBOOL *stop
variable, which should be set at some point to exit recursion (a familiar pattern in some of the Cocoa block iterators).recurseAfter
works much the same, though I won't offer a contrived example here. I am using all three of these without issue, replacing my old-performBlock:afterDelay:
pattern.