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?
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 need a __block on Block, change the declaration to:
__block BOOL (^Block)(Square *square, NSMutableArray *processedSquares);
Block = ^(Square *square, NSMutableArray *processedSquares) {
When a variable (Block) is referenced within a block then its current value is copied into the block. In your code Block 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 call Block 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:
#include <stdio.h>
#include <Foundation/Foundation.h>
@interface Square : NSObject
{
  BOOL nuked;
  NSArray *adjacentSquares;
}
@property(nonatomic) BOOL nuked;
@property(nonatomic, retain) NSArray *adjacentSquares;
@end
@implementation Square
@synthesize nuked;
@synthesize adjacentSquares;
@end;
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;
};
int main(int argc, char **argv)
{
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  Square *s1, *s2;
  s1 = [[Square alloc] init];
  s2 = [[Square alloc] init];
  s1.adjacentSquares = [NSArray arrayWithObjects:s2, nil];
  s2.adjacentSquares = [NSArray arrayWithObjects:s1, nil];
  __block NSMutableArray *processedSquares = [NSMutableArray array];
  BOOL foundNukedSquare = Block(s1, processedSquares);
  printf("%d\n", foundNukedSquare);
  [s1 release];
  [s2 release];
  [pool release];
  return 0;
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With