I'm trying to create recursion using blocks. It works for a while, but eventually it crashes and gives me a bad access exception. This is my code:
BOOL (^Block)(Square *square, NSMutableArray *processedSquares) = ^(Square *square, NSMutableArray *processedSquares) {
[processedSquares addObject:square];
if (square.nuked) {
return YES; // Found a nuked square, immediately return
}
for (Square *adjacentSquare in square.adjacentSquares) {
if ([processedSquares containsObject:adjacentSquare]) {
continue; // Prevent infinite recursion
}
if (Block(adjacentSquare, processedSquares)) {
return YES;
}
}
return NO;
};
__block NSMutableArray *processedSquares = [NSMutableArray array];
BOOL foundNukedSquare = Block(square, processedSquares);
Explanation: I have a Square
class that has a BOOL nuked
. It also has an NSArray adjacentSquares
containing other Squares.
I want to check whether a square, or one of its 'connected' squares, is nuked or not.
The array processedSquares
is to keep track of the squares I have checked to prevent infinite recursion.
When I run this, it's doing a lot of calls of this block (as expected). But at some point, it crashes at the last line with a bad access exception.
I also get this in the console:
Cannot access memory at address 0x1
Cannot access memory at address 0x1
Cannot access memory at address 0x1
Cannot access memory at address 0x1
warning: Cancelling call - objc code on the current thread's stack makes this unsafe.
I'm not that familiar with blocks and recursion. Any ideas?
Edit 1
As requested, the backtrace:
#0 0x00000001 in ??
#1 0x000115fb in -[Square connectedToNukedSquare] at Square.m:105
#2 0x00010059 in __-[Bot makeMove]_block_invoke_1 at Bot.m:94
#3 0x91f3f024 in _dispatch_call_block_and_release
#4 0x91f31a8c in _dispatch_queue_drain
#5 0x91f314e8 in _dispatch_queue_invoke
#6 0x91f312fe in _dispatch_worker_thread2
#7 0x91f30d81 in _pthread_wqthread
#8 0x91f30bc6 in start_wqthread
You seem to be adding
squares
to the array while traversing the array. I'm speaking of this line:[processedSquares addObject:square];
Might that have to do with it? You're adding an object while traversing. I'm surprised that this works at all.
You need a
__block
onBlock
, change the declaration to:When a variable (
Block
) is referenced within a block then its current value is copied into the block. In your codeBlock
hasn't yet been given a value, as you are constructing the block in the assignment...The
__block
prefix passes the variable by reference - by the time your block makes its recursive callBlock
has a value, the reference to it is used to obtain that value, and the recursive call is OK.I don't know why its worked at all for you without the
__block
- failed straightaway for me. With the modifier however I can recurse to at least a depth of 10,000 - so stack space isn't a problem!You're liking doing something wrong with the setup -- your
Square
objects are probably messed up somehow. Here's a complete example that works fine for me, maybe it can help you find your mistake: