iOS 5 块对象 ARC 桥接转换

9
这个问题参考了这个问题:如何使用块简化回调逻辑? 我的头文件有这些typedefs。
typedef void (^StuffDoneBlock)(NSDictionary * parsedData);
typedef void (^StuffFailedBlock)(NSError * error);

在init中。
stuffDoneCallback = Block_copy(done);
StuffFailedCallback = Block_copy(error);

在这篇论文中,它说Block_copy是不必要的。但随后需要一个桥接转换。 编译器信息如下:
error: cast of block pointer type 'StuffDoneBlock' (aka 'void (^)(NSDictionary *__strong)') to C pointer type 'const void *' requires a bridged cast [4]
         stuffDoneCallback = _bridge(Block_copy(done));
                                     ^~~~~~~~~~~~~~~~
/Developer-4.2/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/usr/include/Block.h:60:61: note: instantiated from:
 #define Block_copy(...) ((__typeof(__VA_ARGS__))_Block_copy((const void *)(__VA_ARGS__)))
                                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~

你不应该需要桥接转换或块复制。你能发一下stuffDoneCallback和done是如何声明的吗? - Christopher Pickslay
1个回答

22
首先,为什么你要使用Block_copy()呢?除非你在写原始的C代码,否则你应该调用块对象的-copy方法,例如[done copy]。其次,ARC将自动复制那些需要在初始化作用域之外继续存在的块对象[1],因此你不再需要调用-copy方法了。唯一的“例外”是具有块类型的属性仍然需要有copy属性标识。
[1]: 这里可能需要澄清一下。只有当编译器确定需要在初始化作用域之外继续存在时,ARC才会隐式复制块对象。这基本上意味着当它被赋值给逃逸当前作用域的变量(在父作用域中声明的堆栈变量、实例变量、静态变量等)时。但是,如果将其作为参数传递给方法/函数,则编译器不会自动复制它。通常这不是问题,因为需要持有块对象超出堆栈帧的方法/函数(如dispatch_async()、完成块等)会为你复制它们。然而,那些不支持块对象(如NSArray)的API将不会隐式复制它们,因为它们期望简单的-retain就能解决问题。如果将块对象传递给不支持块对象的API,并且该块对象需要在当前作用域之外继续存在,则必须使用显式的-copy方法。

我有一个 NSMutableArray 存储的块,在使用ARC释放时出现了 segfault 错误。当我开始将通过 [block copy] 获得的块的副本传递给数组时,segfault 错误消失了,这表明在某些情况下需要显式复制块。 - zneak
是的,不希望接受块的API确实需要您在当前范围之外使用该块时进行“-copy”。这与ARC无关,因为不合理期望ARC复制传递给其他方法/函数的所有块。 - Lily Ballard
在这种情况下,您可能需要考虑进行编辑。句子“其次,ARC将为您复制那些需要在其初始化范围之外继续存在的块,因此您甚至不需要再调用-copy。”似乎表明相反的意思。 - zneak
@zneak:“live past their initialization scope”指它们存在于编译器可见的位置,将在作用域之外存在。换句话说,如果您声明了一个堆栈变量,进入作用域,将块分配给变量,退出作用域,ARC将为您复制该块。同样,如果将其分配给实例变量或静态变量。但是,如果将其传递给方法(或分配给属性),则该方法将负责知道块是否需要被复制。如果使用ARC编译该方法并且它知道它有一个块,则也可能会起作用。 - Lily Ballard
@zneak:唯一真正的问题是当你将一个块传递给一个接受任意 obj-c 对象并且不知道它刚刚被传递了一个块的方法时。在这种情况下,正常行为是,如果对象需要在堆栈帧之后继续存在,则 API 将 -retain 它。然而,块需要被 -copy。因此,在这种情况下,您只需要显式复制您的块。 - Lily Ballard
我了解并同意你所说的一切。我的建议是将你在最后评论中提出的精度推广到你的答案中。 - zneak

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