iOS blocks and strong/weak references to self
I have a question about strong and weak references to self in blocks in iOS. I know the proper way to refer to self inside a block is to create a weak reference outside the block, and then a strong reference to that weak reference inside the block, like this:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
typeof(self) strongSelf = weakSelf;
NSLog(@"%@", strongSelf.someProperty);
});
However, what happens if you have nested blocks? Is the one set of references enough? Or do you need a new set for each block? For example, which of the following is correct?
This:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
typeof(self) strongSelf = weakSelf;
NSLog(@"%@", strongSelf.someProperty);
dispatch_async(dispatch_get_main_queue(), ^ {
strongSelf.view.frame = CGRectZero;
});
});
Or this:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
typeof(self) strongSelf = weakSelf;
NSLog(@"%@", strongSelf.someProperty);
__weak typeof(strongSelf) weakSelf1 = strongSelf;
dispatch_async(dispatch_get_main_queue(), ^ {
typeof(strongSelf) strongSelf1 = weakSelf1;
strongSelf1.view.frame = CGRectZero;
});
});
Any information or explanation is much appreciated!
You don’t need to make two sets of weak references. What you want to avoid with blocks is a retain cycle—two objects keeping each other alive unnecessarily.
If I have an object with this property:
@property (strong) void(^completionBlock)(void);
and I have this method:
- (void)doSomething
{
self.completionBlock = ^{
[self cleanUp];
};
[self doLongRunningTask];
}
the block will be kept alive when I store it in the completionBlock
property. But since it references self
inside the block, the block will keep self
alive until it goes away—but this won’t happen since they’re both referencing each other.
In this method:
- (void)doSomething
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self cleanUp];
}];
[self doLongRunningTask];
}
you don’t need to make a weak reference to self
. The block will keep self
alive, since it references self
from within, but since all we’re doing is handing the block off to [NSOperationQueue mainQueue]
, self
isn’t keeping the block alive.
Hope this helps.
Both constructs are fine. It just depends upon your intent. What do you want to happen if the object is (a) released after the outer block commences but (b) before the inner block starts on the main queue? If you do not want it retained in this scenario (which I might guess was your intent, given that you're going through this weakSelf
exercise in the first place), then use your final example, where you have the second weak pointer. Otherwise you can use your other example.
Having said that, a couple of observations:
-
It's not a forgone conclusion that you have to use this
weakSelf
pattern in the first place. Some people mistakenly think that they have to use thisweakSelf
pattern to avoid a strong reference cycle (a.k.a. retain cycle). But this code sample does not constitute a strong reference cycle. It simply retains the object while the dispatched code executes, which is a very different consideration.In fact, sometimes you need/want that. Sometimes you don't. It depends upon the business problem you're solving. Absolutely, you frequently don't want it to keep a strong reference to
self
, in which case theweakSelf
pattern makes perfect sense. But that's not always the case.But my point is that you shouldn't be pursing this
weakSelf
pattern (at least in thisdispatch_async
scenario) to avoid a strong reference cycle. No such cycle exists. Where this is an issue is where you have a block variable (e.g. somecompletionHandler
block). In that case, theweakSelf
pattern is critical. But not here. -
But let's consider for a second that scenario in which you don't want
self
retained. Then there's a question of whether you want the dispatched code continuing at all in the first place. If not, maybe you should be using a operation queue with cancelable operations instead of GCD.For example, I'm surprised how often people agonize over whether they're going to retain the view controller while some background network request is running, but don't worry about whether they should be canceling that background network request in the first place. Often, the latter is a far more significant design consideration (e.g. the PDF or image you're downloading takes up far more system resources (both memory and network bandwidth) than the view controller ever will).
But let's assume that (a) you really want the dispatched code to continue to execute, but (b) you don't want to retain
self
. (This seems like a rare scenario, but it's the one you've asked about, so let's pursue that.) The final question of whether you need yourstrongSelf
construct, also. In your case, where you're just calling a single method ofself
, you don't need to bother with thisstrongSelf
construct. That's critical only if you're going to deference ivars or otherwise need to avoid race conditions. But, in this example, given that a message sent to anil
object does nothing, you technically often don't need to worry about thisstrongSelf
construct at all.
Don't get me wrong. It's good to get one's arms around the weakSelf
pattern, as well as the nested strongSelf
pattern that sometimes accompanies it. I'm just suggesting it's good to understand when these patterns are truly needed. And I think the choice of GCD versus a cancelable NSOperation
is often a far more critical, but often overlooked, question.
Blocks are created and stored on the stack. So the block will be destroyed when the method that created the block returns.
If a block becomes an instance variable ARC copy the block from the stack to the heap. You can explicit copy a block with the copy message. Your block is now a heap-based block instead of a stack-based block. And you have to deal with some memory management issues. The block itself will keep a strong reference to any objects it references. Declare __weak pointers outside the block and then reference this pointer within the block to avoid retain cycles.