ObjC:在以后的函数中调用阻塞^ {}时的BAD ACCESS?

时间:2012-06-30 22:33:45

标签: objective-c block exc-bad-access

discussion之后,我遇到了一个糟糕的访问问题;

循环有几个步骤:a,b,c,... x,y,z:

-(void)cycle:(float)delta{
[self stepA]
[self stepB]
// etc.
[self stepZ]
}

在某些时候,步骤x执行以下操作:

// IRQ is an NSMutableArray
// Self is a reference to the engine running the cycles
[IRQ addObject:^{ NSLog(@"hello! %@", self); } ];

稍后,步骤z将处理所有“延迟”呼叫:

            for (int i = 0; i < [IRQ count]; i++){
                void (^delayedCall)(void) = [IRQ objectAtIndex:i];
                delayedCall();
            }

            [IRQ removeAllObjects];

结果:EXEC_BAD_ACCESS

现在,如果步骤x只添加一个没有对象引用的普通字符串,如下所示,步骤Z工作正常:

[IRQ addObject:^{ NSLog(@"hello!"); } ];

最后一次观察,如果同一步骤都向队列添加块并迭代队列以执行块,则不会出现问题。 就像对象的引用被“丢失”一样,因为步骤:方法被留下了?

我对这方面的了解不多,需要更多的帮助!

编辑: 詹姆斯,刚试过以下内容以避免引用cyle:

NSString *userName = @"James";
[IRQ addObject:^{ NSLog(@"hello %@", userName); } ];

它也会发生。您的解决方案将如何适用于此?

提前致谢!

3 个答案:

答案 0 :(得分:3)

当您使用^{}语法创建块时,它会在堆栈上创建。要长时间保持块(超出创建它的函数的范围),必须将块复制到堆中:

void (^ myBlock)(void) = ^ {
    // your block code is here.
};
[IRQ addObject:[[myBlock copy] autorelease]];

如果使用ARC,请跳过-autorelease消息。

答案 1 :(得分:3)

问题是在堆栈上创建了块对象。如果希望在声明它们的作用域被销毁之后使用,并且不为您复制块,则需要将块复制到堆中。

在这里,您将一个“向下堆栈”的对象传递给一个不知道块的方法。取代

[IRQ addObject:^{ NSLog(@"hello! %@", self); } ];

[IRQ addObject:[^{ NSLog(@"hello! %@", self); } copy]];

此时EXC_BAD_ACCESS将消失。

但在大多数情况下,您无需复制块!几个例子:

  1. 如果从方法中返回一个块(“在堆栈中”),ARC将自动复制它。
  2. 如果调用不保留块的方法,则不需要复制块,因为它保留在范围内。示例:块传递给-[NSArray sortedArrayUsingComparator:]
  3. 如果您稍后调用使用该块的方法,则该方法应负责复制块,否则每个调用者都需要复制该块。我所知道的Apple库中的所有方法/功能都遵循这种模式。示例:完成块传递给+[UIView animateWithDuration:options:animations:completion:]

答案 2 :(得分:0)

您传递的对象似乎是..在您的示例中:selfuserName过早被释放。这不是我期望的块行为。正如我之前的回答,我预计问题是因为保留太多了!

作为测试,您可以尝试:

NSString *userName = [@"James" retain];
[IRQ addObject:^{ NSLog(@"hello %@", userName); } ];

这将是内存泄漏,但它有助于指示对象是否正在被释放。

这是由于“保留周期”导致块保留selfself保留该块。

试试这个:

__block typeof(self) blockSafeSelfReference = self;
[IRQ addObject:^{ NSLog(@"hello! %@", blockSafeSelfReference); } ];

如果使用ARC,请使用__unsafe_unretained代替__block