On Wed, 07 Dec 2011 21:56:56 +0100, Mikkel Islay 
<my.inputstr...@googlemail.com> 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 = m...@tidbits.com, <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 (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to