EXC_BAD_ACCESS при использовании рекурсивного блока - PullRequest
7 голосов
/ 30 января 2011

Я пытаюсь создать рекурсию, используя блоки. Это работает некоторое время, но в конечном счете это терпит крах и дает мне исключение плохого доступа. Это мой код:

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. Он также имеет 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 голосов
/ 30 января 2011

Вам нужно __block на Block, измените объявление на:

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

Если в блоке указана переменная (Block), то ее текущее значение копируется в блок. В вашем коде Block еще не задано значение, так как вы строите блок в присваивании ...

Префикс __block передает переменную по ссылке - к тому времени, когда ваш блок делает свой рекурсивный вызов Block имеет значение, ссылка на него используется для получения этого значения, а рекурсивная вызов в порядке.

Я не знаю, почему это сработало для вас без __block - сразу провалилось для меня. Однако с модификатором я могу вернуться на глубину не менее 10 000 - так что пространство в стеке не является проблемой!

1 голос
/ 30 января 2011

Вам нравится делать что-то не так с настройкой - ваши 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 голосов
/ 30 января 2011

Вы, кажется, добавляете squares к массиву при обходе массива.Я говорю об этой строке:

[processedSquares addObject:square];

Может ли это иметь отношение к этому?Вы добавляете объект во время обхода.Я удивлен, что это работает вообще.

...