Re: ARC and dealloc
Re: ARC and dealloc
- Subject: Re: ARC and dealloc
- From: Matt Neuburg <email@hidden>
- Date: Thu, 08 Dec 2011 08:36:12 -0800
On Wed, 07 Dec 2011 21:56:56 +0100, Mikkel Islay <email@hidden> said:
>Dear list,
>
>A question regarding the proper use of dealloc in ARC-environments (under iOS):
>I have a need to unregister NSNotification observers during exchange of UiViewControllers. Is implementing - (void)dealloc considered a proper use for this purpose? Is it safe to make assumptions regarding when in the release-process dealloc is called?
You can certainly make assumptions about when dealloc is called - it is extremely deterministic, actually. See Greg Parker's discussion (in the WWDC 2011 videos) of the order in which things happen as an object goes out of existence.
However, that doesn't necessarily make dealloc a good place to unregister observers. You should certainly use dealloc as a backstop - in other words, even under ARC, you should always unregister observers there just in case. But, in particular, if you are using a block when you register, the notification center is retaining the block and, if the block mentions self or any ivar, the block is retaining you. That's a retain cycle, and it won't be broken until you break it by unregistering the observer you were handed when you registered. This means that if you unregister only in dealloc, dealloc will never be called and you will leak.
I have discussed this problem and provided some solutions in the draft of the new edition of my iOS programming book:
http://www.apeth.com/iOSBook/ch12.html#_memory_management
Here's a thumbnail sketch from a response I posted on stackoverflow (it was down-marked by some reader, but I don't see why - no comment explains the downmark, and simple experimentation proves that what I'm saying is true):
When you issue addObserverForName: you must capture the returned value; this is the observer token. You store this somewhere (e.g. an instance variable):
self->observer = [[NSNotificationCenter defaultCenter]
addObserverForName:@"woohoo" object:nil queue:nil
usingBlock:^(NSNotification *note)
{
//whatever
}];
Later, when you're going out of existence, you remove that observer token from the notification center by calling removeObserver: with that token as argument. If you don't do that, you can crash later.
[[NSNotificationCenter defaultCenter] removeObserver:self->observer];
But under ARC, when the block is copied, you'll get a retain cycle. This is because the stored observer token contains the block and is itself retaining self (and both the notification center and now, apparently, you are retaining the observer token). I will give three ways to break this retain cycle:
(a) Store the observer token as a weak reference:
__weak id observer;
(b) Store the observer token as a strong reference, but explicitly release it (by nilifying it) when you remove the observer:
[[NSNotificationCenter defaultCenter] removeObserver:self->observer];
self->observer = nil; // crucial
(c) Do the "weak-strong dance", like this, when you create the block (I am pretending that self is a FlipsideViewController):
__weak FlipsideViewController* wself = self;
observer = [[NSNotificationCenter defaultCenter]
addObserverForName:@"user.login"
object:nil queue:nil usingBlock:^(NSNotification *note) {
FlipsideViewController* sself = wself;
[sself dismissModalViewControllerAnimated:YES];
}];
Now, you might think that the "weak-strong dance" is an extreme approach,. But it has one huge advantage: it is the only one of these three solutions that allows you to remove the observer in dealloc. With the other two solutions, dealloc will never be called until after you have called removeObserver: - and finding a better place to call it may not be easy.
m.
PS In the case of a view controller on iOS, I usually register in viewDidAppear and unregister in viewWillDisappear. But you still have to use one of these solutions to make sure the observer isn't also retained by self, or you'll leak.
--
matt neuburg, phd = email@hidden, <http://www.apeth.net/matt/>
A fool + a tool + an autorelease pool = cool!
Programming iOS 4!
http://www.apeth.net/matt/default.html#iosbook_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden