使用递归块时出现EXC_BAD_ACCESS错误

7

我试图使用块来创建递归。它一开始运行良好,但最终会崩溃并显示一个无法访问的异常。这是我的代码:

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);

说明:我有一个Square类,其中包含一个BOOL类型的nuked变量和一个包含其他Squares的NSArray类型的adjacentSquares数组。
我想检查一个方块或其“连接”的方块是否已被毁坏。 processedSquares数组用于跟踪我已经检查过的方块,以防止无限递归。
当我运行此代码时,它会执行很多次这个块(如预期的那样)。但是在最后一行发生了错误访问异常时,它会崩溃。
控制台还会出现以下内容:
不能访问地址0x1 不能访问地址0x1 不能访问地址0x1 不能访问地址0x1 警告:取消调用-当前线程堆栈上的objc代码使其不安全。
我对块和递归不是很熟悉。有什么想法?
编辑1
按要求,下面是回溯信息:
#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
3个回答

14

你需要在Block上使用__block关键字,将声明更改为:

__block BOOL (^Block)(Square *square, NSMutableArray *processedSquares);
Block = ^(Square *square, NSMutableArray *processedSquares) {

当一个变量(Block)在块内被引用时,它的当前值会复制到该块中。在您的代码中,由于您正在赋值语句中构造块,因此尚未给Block赋值...

__block前缀通过引用传递变量 - 在您的块进行递归调用时,Block已经有了值,使用对其的引用来获取该值,并且递归调用可正常工作。

我不知道为什么没有使用__block就可以让您的代码工作 - 对我来说,直接失败了。但是使用这个修饰符,我可以递归至少深度达到10,000 - 所以栈空间不是问题!


哦,天啊,我不敢相信我错过了那个。真的。干得好。删除了我的非答案(虽然在那个数组上仍然不需要__block)。 - bbum
我在获取一个警告,内容为 “在此块中强烈捕获‘block’可能会导致保留循环”。 解决方案在这里:https://dev59.com/_mUo5IYBdhLWcg3w9jR4 - ishahak

1
你在设置方面可能做错了一些事情——你的Square对象可能出了问题。这里有一个完整的例子,对我来说运行良好,也许它可以帮助你找到错误所在:
#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;
}

0

在遍历数组时,您似乎正在向数组中添加squares。我指的是这一行:

[processedSquares addObject:square];

这可能与问题有关吗?您正在遍历时添加一个对象。我很惊讶这竟然能工作。


OP似乎没有枚举正在处理的数组。 - bbum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接