将Objective-C块定义为属性 – 最佳实践

我最近遇到了一个Apple文档 ,显示了以下块的属性声明:

@interface XYZObject : NSObject @property (copy) void (^blockProperty)(void); @end 

另外,这篇文章指出:

注意:您应该指定copy作为属性属性,因为需要复制一个块以跟踪其捕获的状态超出原始范围。 这不是您使用自动引用计数时需要担心的事情,因为它会自动发生,但是最好的做法是使用property属性来显示结果行为。 有关更多信息,请参阅块编程主题。

我也读了build议的块编程主题,但没有发现任何相关的东西。

我仍然很好奇,为什么将块属性定义为“复制”是最佳实践。 如果你有一个很好的答案,请尝试区分ARC和MRC的差异,如果有的话。

谢谢

默认情况下,在堆栈上创build块。 意思是他们只存在于他们创造的范围内。

如果您想稍后访问它们,则必须通过向块对象发送copy消息来将其复制到堆中。 一旦它检测到一个块需要在其创build的范围之外访问,ARC会为你做这件事。作为一个最佳实践,你将任何块属性声明为复制,因为这是它应该在自动内存pipe理下的方式。

在Objective-C中通过Mike Ash阅读堆栈和堆对象以获取堆栈与堆的更多信息。

块默认分配在堆栈上。 这是一个优化,因为堆栈分配比堆分配便宜得多。 堆栈分配意味着,默认情况下,一个块将在其声明的范围退出时停止存在。 因此,带有retain语义的块属性将导致指向不再存在的块的悬挂指针。

为了将一个块从堆栈移动到堆(因此使其具有正常的Objective-C内存pipe理语义和延长的生命周期),必须通过[theBlock copy]Block_copy(theBlock)等来复制块。 ,块的使用期限可以通过保留/释放来按需要进行pipe理。 (是的,这也适用于ARC,你只需要不必自己拨打电话 – -retain / -release 。)

所以你想用copy语义来声明块属性,所以当属性被设置时,块被复制,从而避免了指向基于栈的块的悬挂指针。

你提到的“最佳实践”只是简单地说:“无论你在这里写什么,看到ARC都会奇迹般地复制你的块,最好你明确地写'复制',以免混淆后代看你的代码。

解释如下:

通常,您不需要复制(或保留)块。 你只需要做一个副本,当你希望在声明范围被破坏之后使用这个块。 复制将块移到堆中。
块编程主题:使用块,复制块

很明显,将一个块分配给一个属性意味着它可以在被声明的范围被销毁后使用。 因此,根据块编程专题 ,块应该被Block_copy复制到堆中。

但ARC为您照顾:

当你在ARC模式下传递数据块时,块“正常工作”,比如返回。 您不必再调用Block Copy。
– 过渡到ARC

请注意,这不是关于块的retain语义。 没有被移出(即将被popup的)堆栈并且堆到堆上,块的上下文根本就没有办法存在。 所以无论你用什么属性来限定@property ,ARC仍然会复制块。