我最近遇到一个Apple document,它显示了一个块的以下属性声明:
@interface XYZObject : NSObject
@property (copy) void (^blockProperty)(void);
@end
此外,本文还指出:
注意:您应该将copy指定为属性属性,因为需要复制块以跟踪其在原始范围之外的捕获状态。在使用自动引用计数时,您不必担心这一点,因为它会自动发生,但属性属性的最佳做法是显示结果行为。有关更多信息,请参阅块编程主题。
我还阅读了建议的Blocks Programming Topics,但没有找到任何相关内容。
我仍然很好奇为什么将块属性定义为“copy”是最佳实践。如果您有一个好的答案,请尝试区分ARC和MRC差异(如果有的话)。
谢谢
答案 0 :(得分:23)
默认情况下,会在堆栈上创建块。这意味着它们只存在于它们创建的范围内。
如果您想稍后访问它们,则必须通过向块对象发送copy
消息将它们复制到堆中。一旦检测到需要在其创建的作用域之外访问块,ARC将立即为您执行此操作。作为最佳实践,您将任何块属性声明为副本,因为这是它在自动内存管理下的应用方式。
阅读Mike Ash的Stack and Heap Objects in Objective-C,了解有关堆栈与堆的更多信息。
答案 1 :(得分:7)
默认情况下,块在堆栈上分配。这是一种优化,因为堆栈分配比堆分配便宜得多。堆栈分配意味着,默认情况下,当声明的范围退出时,块将不再存在。因此,具有retain
语义的块属性将导致指向不再存在的块的悬空指针。
要将块从堆栈移动到堆(从而为其提供正常的Objective-C内存管理语义和延长的生命周期),您必须通过[theBlock copy]
,Block_copy(theBlock)
等复制块。一旦在堆上,可以通过保留/释放它来根据需要管理块的生命周期。 (是的,这也适用于ARC,您不必自己致电-retain
/ -release
。)
因此,您希望使用copy
语义声明块属性,以便在设置属性时复制块,从而避免指向基于堆栈的块的悬空指针。
答案 2 :(得分:6)
你所指的“最佳实践”只是简单地说:“无论你在这里写什么,看到ARC会神奇地复制你的块,你最好明确地写'复制',以免混淆看待你的后代。代码“。
说明如下:
通常,您不需要复制(或保留)块。当您希望在销毁声明范围之后使用该块时,您只需要制作一个副本。复制将块移动到堆中 -Blocks编程主题:使用块,复制块
显然,将一个块分配给一个属性意味着它可以在声明它的范围被破坏后使用。因此,根据 Blocks Programming Topics ,应该使用Block_copy
将该块复制到堆中。
但是ARC会为你解决这个问题:
在ARC模式下将块传递到堆栈时阻止“正常工作”,例如在返回中。您不必再调用Block Copy了。
- Transitioning to ARC
请注意,此不是关于块的retain
语义。块的上下文根本没有办法存在而不会从(即将被弹出的)堆栈移到堆上。因此,无论您使用@property
符合哪些属性,ARC仍然会复制该块。