块和ARC - 使用发布版本复制或崩溃(由优化级别引起)

Kre*_*ase 10 objective-c ios objective-c-blocks automatic-ref-counting

我正在使用Xcode 4.3.3并开发iOS 5.0+.在开发ARC iOS应用程序时,我开始使用块作为异步操作的回调机制.该应用程序在模拟器和设备上运行良好.

然后我第一次运行了它的探查器,它几乎马上就开始崩溃 - 特别是在尝试调用第一个回调块时的EXC_BAD_ACCESS.

经过一番调查后,很明显行为上的差异是因为默认情况下探查器以"发布模式"运行 - 特别是,优化级别设置为"最快,最小[-Os]"而不是"无[-O0] ".

例如,尝试执行callbackBlock时,以下代码(此问题已简化)将崩溃:

- (void) setCallbackBlock:(void (^)(NSString *input))block
{
    callbackBlock = block;
}

- (void) invokeCallbackWithInput:(NSString *)input
{
    if (callbackBlock) {
        callbackBlock(input);
    }
}
Run Code Online (Sandbox Code Playgroud)

调试它,调用setCallbackBlock,优化级别设置为"None",传入的块将是一个NSStackBlock,并且callbackBlock将成为一个NSMallocBlock.

然而,随着优化级别"最快,最小",它仍然是一个NSStackBlock.

更改setter代码以使用[block copy]修复崩溃问题(基于iOS 5块仅使用Release Build崩溃).

但是,另一个相关问题表明,ARC 不需要这样做 - 块变量被复制到ARC中的堆中 - 为什么Objective-C块仍然可以在不将其复制到堆中的情况下工作?

所以我的问题是:这里发生了什么,为什么?(另外,这两个答案如何才能正确......?)

编辑:澄清如何声明callbackBlock - 就在我的@implementation之上,这些方法是这样的:

@interface MyClass ()
{
    void (^callbackBlock)(NSString *input);
}

@end
Run Code Online (Sandbox Code Playgroud)

Fir*_*eer 14

所以我的问题是:这里发生了什么,为什么?(另外,这两个答案如何才能正确......?)

我实际上认为对另一个问题的答案是错误的,因为它没有回答关于ARC中的块的特定问题.问题是关于将基于堆栈的块从一个函数/方法传递到另一个函数/方法.答案是关于不同的东西,即在块中捕获__block变量.那是一个不同的问题.

您的问题的答案在过渡到ARC发行说明的常见问题解答中:

在ARC模式下向块传递块时块"正常工作",例如在返回中.您不必再调用Block Copy.将堆栈"向下"传递给arrayWithObjects:以及其他执行保留的方法时,仍然需要使用[^ {} copy].

因此,它的工作方式是,当您传递一个块(在您的情况下是在堆栈上分配的块文字)时,编译器在初始化该调用的参数时不会复制该块.被调用的函数或方法有责任在需要时复制该块本身.

ARC 从函数或方法返回块时自动复制块的位置.在这种情况下,编译器知道它必须为您复制到堆,所以它确实如此.

因此,即使使用ARC,您的setter也应该进行块复制.

我希望有所帮助.

  • 它可能是保留块,但它不是复制它.这就是问题,因为基于堆栈的块在以后使用之前已经消失了.此处的块需要复制,而不是保留. (5认同)
  • +1表示完全正确且措辞良好的答案.引用和接受的答案与原始问题不匹配的事实确实很奇怪,让我颤抖了一下.在上一条评论中指出副本和保留之间的差异也非常重要且有用.这个答案很棒. (3认同)