Blocks, retain cycles, and ARC

261 views
Skip to first unread message

Brian Webster

unread,
Sep 18, 2011, 7:49:43 PM9/18/11
to xcodep...@googlegroups.com
Hi folks,

Just a little follow up to the discussion we had at last week's meeting regarding avoiding retain cycles with blocks. It turns out there are not one, not two, not three, but FOUR different patterns you can follow, depending on how you're compiling your code.

For those not at the meeting, the basic problem is when you having some code like this:

- (void)loadView
{
[[self representedObject] addObserverForKeyPath:@"name task:^(id object, NSDictionary* change) {
[self updateTextField];
}];
}

Because the block is hanging around past the scope of the -loadView method, the -addObserverForKeyPath:task: method will make a copy of the block. However, when a block is copied, any variables it refers to will be retained. In this case, "self" is referred to by the block, and because the block will also be retained (a couple levels removed) from self, you end up with a retain cycle, and both the block and self will eventually leak.

The typical way to deal with this is to declare a local variable with the __block modifier, and refer to that variable instead of self.

- (void)loadView
{
__block id blockSelf = self;
[[self representedObject] addObserverForKeyPath:@"name task:^(id object, NSDictionary* change) {
[blockSelf updateTextField];
}];
}

The reason this works is because variables marked with the __block attribute are not retained when the block is copied, so doing this does not result in a retain cycle...

...EXCEPT! When you're compiling under ARC, apparently. According to the ARC documentation on LLVM's website:

"The implicit const capture variables created when evaluating a block literal expression have the same ownership semantics as the local variables they capture. The capture is performed by reading from the captured variable and initializing the capture variable with that value; the capture variable is destroyed when the block literal is, i.e. at the end of the enclosing scope.

The inference rules apply equally to __block variables, which is a shift in semantics from non-ARC, where __block variables did not implicitly retain during capture."

http://clang.llvm.org/docs/AutomaticReferenceCounting.html#misc.blocks

So, while the __block trick works with manual memory management, the semantics are actually different when using ARC, so this doesn't work if the code is compiled with ARC enabled.

So, here are the four possible ways to deal with this, depending on your compilation environment:

1. If you're using garbage collection, you don't have to worry about any of this, since GC can just detect and eliminate the retain cycles for you
2. If you're using manual memory management, marking the variable as __block works to break the retain cycle
3. If you're using ARC, and are deploying either to iOS 5 or OS X 10.7, you can use the __weak modifier instead of __block. This will not only not retain the referenced object, but if the object gets deallocated, the block's reference to it will be zeroed out, so you don't have a dangling pointer.
4. If you're using ARC, but need to deploy on iOS 4 or OS X 10.6, where __weak isn't supported, you can instead use the __unsafe_unretained modifier. This acts the same as __weak, except that the variable won't get automagically zeroed out if the object it refers to is deallocated, so you're responsible for making sure you don't end up with a dangling pointer to a deallocated object.

Simple!

--
Brian Webster
bewe...@gmail.com


Brad Miller

unread,
Sep 18, 2011, 8:02:40 PM9/18/11
to xcodep...@googlegroups.com
Thanks Brian. I hadn't seen the change under arc before. It makes sense though when you think about what's going on. The __block trick works because you're exploiting knowledge you have about what's going on and it doesn't follow standard rules.

Brad

> --
> You received this message because you are subscribed to the Google Groups "XCodePhoenix" group.
> To post to this group, send email to xcodep...@googlegroups.com.
> To unsubscribe from this group, send email to xcodephoenix...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/xcodephoenix?hl=en.
>

Brad Miller
br...@cynicalpeak.com
http://cynicalpeak.com/

Matthew Frederick

unread,
Sep 18, 2011, 10:14:56 PM9/18/11
to xcodep...@googlegroups.com
Excellent, very useful!

Jiva DeVoe

unread,
Sep 18, 2011, 11:00:15 PM9/18/11
to xcodep...@googlegroups.com
Curious... does __block flag as an error under ARC? and if so, does LLVM suggest fixing with __weak?

Reply all
Reply to author
Forward
0 new messages