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

Sta*_*ash 15 objective-c ios objective-c-blocks automatic-ref-counting

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

@interface XYZObject : NSObject
@property (copy) void (^blockProperty)(void);
@end
Run Code Online (Sandbox Code Playgroud)

此外,本文还指出:

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

我还阅读了建议的块编程主题,但没有找到任何相关的内容.

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

谢谢

Jor*_*ers 24

默认情况下,会在堆栈上创建块.这意味着它们只存在于已创建的范围内.

如果您以后想要访问它们,则必须通过向copy块对象发送消息将它们复制到堆中.一旦检测到需要在其创建的作用域之外访问块,ARC将立即为您执行此操作.作为最佳实践,您将任何块属性声明为副本,因为这是它在自动内存管理下的应用方式.

有关堆栈与堆的更多信息,请阅读Mike Ash 在Objective-C中读取堆栈和堆对象.


Jon*_*pan 7

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

移动从该堆块的堆(并因此给它正常Objective-C的存储器管理的语义和延长的寿命),则必须通过复制该块[theBlock copy],Block_copy(theBlock)等等.一旦在堆上,块的寿命可以被管理为保留/释放它所需要的.(是的,这也适用于ARC,你只需要打电话-retain/ -release你自己.)

因此,您希望使用copy语义声明块属性,以便在设置属性时复制块,从而避免指向基于堆栈的块的悬空指针.


jem*_*ons 6

"最佳做法"你是指简单地说,"看到作为ARC是要不管你写什么在这里奇迹般地复制你的块,这是最好的,你明确写入'复制’,以免混淆后人看你的代码."

说明如下:

通常,您不需要复制(或保留)块.当您希望在销毁声明范围之后使用该块时,您只需要制作一个副本.复制将块移动到堆.
-Blocks编程主题:使用块,复制块

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

但ARC会为您解决这个问题:

在ARC模式下向块传递块时块"正常工作",例如在返回中.您不必再调用Block Copy.
- 过渡到ARC

请注意,这与块的语义无关retain.块的上下文根本没有办法存在而不会从(即将被弹出的)堆栈移到堆上.因此,无论您@property使用哪种属性,ARC仍然会复制该块.