Defining Objective-C blocks as properties - best p

2019-01-21 20:59发布

问题:

I've recently come across an Apple document that shows the following property declaration for a block:

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

Also, this article states:

Note: You should specify copy as the property attribute, because a block needs to be copied to keep track of its captured state outside of the original scope. This isn’t something you need to worry about when using Automatic Reference Counting, as it will happen automatically, but it’s best practice for the property attribute to show the resultant behavior. For more information, see Blocks Programming Topics.

I also read the suggested Blocks Programming Topics but haven't found anything relevant there.

I'm still curious as to why defining a block property as "copy" is best practice. If you have a good answer, please try to distinguish between ARC and MRC differences if there are any.

Thank you

回答1:

By default blocks are created on the stack. Meaning they only exist in the scope they have been created in.

In case you want to access them later they have to be copied to the heap by sending a copy message to the block object. ARC will do this for you as soon as it detects a block needs to be accessed outside the scope its created in. As a best practise you declare any block property as copy because that's the way it should be under automatic memory management.

Read Stack and Heap Objects in Objective-C by Mike Ash for more info on stack vs. heap.



回答2:

Blocks are, by default, allocated on the stack. This is an optimization, since stack allocation is much cheaper than heap allocation. Stack allocation means that, by default again, a block will cease to exist when the scope in which it is declared exits. So a block property with retain semantics will result in a dangling pointer to a block that doesn't exist anymore.

To move a block from the stack to the heap (and thus give it normal Objective-C memory management semantics and an extended lifetime), you must copy the block via [theBlock copy], Block_copy(theBlock), etc. Once on the heap, the block's lifetime can be managed as needed by retaining/releasing it. (Yes, this applies in ARC too, you just don't have to call -retain/-release yourself.)

So you want to declare block properties with copy semantics so the block is copied when the property is set, avoiding a dangling pointer to a stack-based block.



回答3:

The "best practices" you refer to simply say, "Seeing as ARC is going to magically copy your block no matter what you write here, it's best you explicitly write 'copy' so as not to confuse future generations looking at your code."

Explanation follows:

Typically, you shouldn’t need to copy (or retain) a block. You only need to make a copy when you expect the block to be used after destruction of the scope within which it was declared. Copying moves a block to the heap.
–Blocks Programming Topics: Using Blocks, Copying Blocks

Clearly, assigning a block to a property means it could be used after the scope it was declared in has been destroyed. Thus, according to Blocks Programming Topics that block should be copied to the heap with Block_copy.

But ARC takes care of this for you:

Blocks “just work” when you pass blocks up the stack in ARC mode, such as in a return. You don’t have to call Block Copy any more.
–Transitioning to ARC

Note that this isn't about the retain semantics the block. There's simply no way for the block's context to exist without being moved off the (soon-to-be-popped) stack and on to the heap. So regardless of what attributes you qualify your @property with, ARC is still going to copy the block.