Basil Bourque
Basil Bourque

Reputation: 339472

Does object need (a) `__block` modifier or (b) weak reference, when accessed from within a block in Objective-C?

In Objective-C (as of Xcode 7), the __block modifier for use with primitives is clearly explained in Stack Overflow such as here and by Apple here in the Blocks Programming Guide. Without the label, a copy of the primitive is captured and the original cannot be modified from within the block. With the label, no copy, and the original can be modified from within the block.

But for use with pointers to objects, the situation is not so well explained. Here Apple says either “a strong reference is made to self” or “a strong reference is made to the variable”. But then at the end of the page Apple says:

To override this behavior for a particular object variable, you can mark it with the __block storage type modifier.

What does “override this behavior” mean?

Further complicating things is that some posts on Stack Overflow talking about making a weak reference when calling an object from within a block.

I am not trying to establish an object. I just want to modify an existing object’s state from within a block. I believe the block is being called synchronously, within the same thread.

Imagine:

Clicker* myClicker = [[Clicker alloc] init] ;
…
// Declare a block.
void (^myBlock)( );

// Populate the block.
myBlock = ^ void ( ) {
    [myClicker click] ; // Updating some state in that object, such as incrementing a counter number or adding an element to a collection.
};

// Call `myBlock` synchronously (same thread) from some other code.
…  // … invokes `myBlock` repeatedly …

My questions:

Upvotes: 1

Views: 353

Answers (2)

Rob
Rob

Reputation: 437917

You quote the somewhat dated Blocks Programming Topics, which says:

To override this behavior for a particular object variable, you can mark it with the __block storage type modifier.

That document dates back to the days of manual reference counting, back before ARC. In manual reference counting code you could use __block to avoid establishing a strong reference to objects referenced inside the block.

But this behavior has changed with ARC, as outlined in Transitioning to ARC Release Notes. We now use weak in those cases where we don't want to establish strong references to the objects referenced in the block. (You can use unsafe_unretained in special cases where you know the resulting dangling pointer isn't a problem.)

So, go ahead and use __block when dealing with fundamental types that you want to mutate inside the block. But when dealing with objects in blocks under ARC, the __block qualifier generally doesn't enter the discussion. The question is simply whether you want a strong reference or a weak one (or an unsafe, unretained one). And that's largely dictated by the object graph of your app, namely (a) whether you need weak/unretained reference to prevent strong reference cycle; or (b) you don't want want some asynchronously executing block to unnecessarily prolong the life of some object referenced in the block. Neither of those situations would appear to be the case here.

Upvotes: 2

newacct
newacct

Reputation: 122489

First of all, the basic point of __block is the same for all types of variables (primitive variables, object-pointer variables, and all other variables) -- __block makes the variable shared between the outside scope and the scopes of the blocks that capture it, so that an assignment (=) to the variable in one scope is seen in all other scopes.

So if you don't use __block, if you assign to an object-pointer variable to point to another object outside the block after the block is created, the block won't see the assignment to the variable, and will still see it pointing to the object it was pointing to when the block was created. Conversely, inside the block you won't be able to assign to the variable. If you do use __block, then assignments to the variable to point to another object, either inside or outside the block, will be reflected in the other scopes.

Note that mutation of objects' state has nothing to do with assignment to variables. You mutate objects' state by calling a mutating method on a pointer to the object, or you can alter a field directly using a pointer to the object using the -> syntax. Neither of these involve assigning to a variable holding the pointer to the object. On the other hand, assigning to a variable holding a pointer to an object only makes the pointer point to another object; it does not mutate any objects.

The part you are reading about strong references and "override this behavior" has to do with the separate issue of memory management behavior of blocks under MRC only. Under MRC, there is no concept of variables being __strong or __weak like in ARC. When a variable of object-pointer type is captured by a block in MRC, it is by default captured as a strong reference (the block retains it, and releases it when the block is deallocated), because that is the desired behavior most of the time. If you wanted the block to not retain the captured variable, the only way to do that was to make it __block, which not only made the variable shared between the two scopes, but also made the block not retain the variable. So the two concepts were conflated in MRC.

In ARC, whether a block captures a variable strongly or weakly depends on the captured variable being __strong or __weak (or __unsafe_unretained), and is completely orthogonal with whether the variable is __block or not. You can have object-pointer variables that are __block without being weakly captured, or weakly captured without being __block, or both weakly captured and __block if you want.

Upvotes: 2

Related Questions