1

Strange one here, dealloc is not being called when dismissed from inside a block. Code:

[[NSNotificationCenter defaultCenter] addObserverForName:@"user.login" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {

[self dismissModalViewControllerAnimated:YES];

 }];

Anyone know why this would be the case? How can i dismiss from inside the block and run dealloc at the same time?

I have tried self performselector but this did not make any difference.

Thanks

Skeep
  • 4,169
  • 2
  • 20
  • 20

3 Answers3

1

(1) Your code is wrong (incomplete). 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];

(2) But wait, there's more! 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. I will give you 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, as one of my commenters implies. 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.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Perhaps you could put the "other, easier" way first, since its also the safest and a better pattern. I realize its not as cool as the "weak-strong dance", and doesn't allow you to demonstrate how clever you are, but advocating unnecessary weak references is a great way for somebody not as clever as yourself to shoot themselves in the foot. – jamie Dec 07 '11 at 10:30
  • Actually, I omitted the best solution of all, which is to make the observer reference weak to start with. I will edit to present all three solutions. Thanks for prodding me on this, @jamie. – matt Dec 07 '11 at 17:07
  • @jamie - added a further edit - there was in fact a reason why I put the weak-strong dance first, as I now explain. – matt Dec 07 '11 at 20:59
  • In the weak-strong dance version, Why is the FlipsideViewController* sself = wself; line needed? – Mike Akers Aug 17 '12 at 19:55
  • 1
    @MikeAkers In this _particular_ code you probably don't need it, since the worst that can happen is that weak self is nil and no harm can come from a message to it. But look at minute 27:10 of Session 322 of the WWDC 2011 videos. The assignment is to make the reference strong again once inside the block, so that if self isn't nil, it won't suddenly become nil between one line and the next. (That is the "strong" part of the weak-strong dance.) – matt Aug 18 '12 at 04:22
0

Could this be related to UIKit being not completely thread-safe? UIKit should be only used on the main thread...

If so, I would suggest using:

performSelectorOnMainThread:withObject:waitUntilDone:

(reference)

sergio
  • 68,819
  • 11
  • 102
  • 123
0

I refer you to: Reference Counting of self in Blocks

The block will retain self until the block is released. So to dealloc self, you'll need to remove the observer.

Community
  • 1
  • 1
jamie
  • 2,963
  • 1
  • 26
  • 27
  • where should I remove the observer if dealloc is not being called? I think viewDidDisappear is unreliable but I don't know any other possibilities. :/ – Hlung Jul 20 '12 at 08:32