21

I have an iOS 5 ARC-based project, and am having difficulty about where I should be removing the observer for the NSNotificationCenter observations which I have registered within a UIViewController. Similar posts on SO have said this should be done in the -dealloc method. Even though this method is not required in ARC projects I have added it with the following code:

- (void)dealloc {

    [[NSNotificationCenter defaultCenter] removeObserver:self];

}

As a test, I open the UIViewController (within a UINavigationController), do some things which trigger the notifications, and then pop it off the stack by tapping the Back button. I then reopen the UIViewController, and do some more things to trigger the notifications, but notice that each callback is being called twice - an indication that the previous notifications have not been deregistered. Repeating this procedure just causes each callback to be called more than more times, so they appear to never be deregistering.

Any help would be appreciated!

Skoota
  • 5,280
  • 9
  • 52
  • 75
  • I had the same problem - infuriating. After hours of debugging, a clean + build 'fixed' the problem. All the while I was stepping through the dealloc and call to -removeObsever, on both the iPhone and simulator. So if anyone else sees this rare problem, rebuild first. – emp Jan 10 '12 at 00:07
  • How did you register in the first place? You need to show more code. – matt Feb 16 '12 at 00:10
  • Does the NSNotification center retain the observer? – Nick Weaver Mar 13 '12 at 10:39
  • 1
    In fact dealloc will never be called when using ARC. This is as designed. – Johan Karlsson Oct 31 '14 at 10:14

3 Answers3

7

It's pretty clear your dealloc method isn't being called (nor is the removeObserver call).

Why not remove your UIViewController's observer in the viewDidUnload: or viewWillDisappear: methods?

Michael Dautermann
  • 88,797
  • 17
  • 166
  • 215
  • 5
    `viewDidUnload:` is only called in low memory conditions, as far as I know. I also need the notification callbacks to still be fired if the view is off-screen, so that means I can't use `viewWillAppear:` and `viewWillDisappear:` – Skoota Dec 04 '11 at 02:56
  • 1
    b.t.w., if you're popping your view controller off the stack, that *should* be deallocing it. If it isn't, is something else retaining your view controller? Check instruments to be sure. Oh, and [here is a related question that might be helpful for you](http://stackoverflow.com/questions/7292119/custom-dealloc-using-arc-objective-c) – Michael Dautermann Dec 04 '11 at 03:35
7

If your dealloc isn't being called, it's likely because someone is still holding a reference to the view controller. Perhaps you need to mark something as __weak? You can use the allocations instrument to help track down what's holding on to your view controller.

Jesse Rusak
  • 56,530
  • 12
  • 101
  • 102
3

"I also need the notification callbacks to still be fired if the view is off-screen" -> you may need to register UIApplicationWillEnterForegroundNotification. If so, let try this:

- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"viewWillAppear");
    [super viewWillAppear:animated];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationDidEnterBackground:)
                                                 name:UIApplicationDidEnterBackgroundNotification
                                               object:nil];
}

- (void)viewWillDisappear:(BOOL)animated {
    NSLog(@"viewWillDisappear");
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    NSLog(@"applicationWillEnterForeground");
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationDidEnterBackground:)
                                                 name:UIApplicationDidEnterBackgroundNotification
                                               object:nil];
    // do your stuff here
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSLog(@"applicationDidEnterBackground");
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationWillEnterForeground:)
                                                 name:UIApplicationWillEnterForegroundNotification
                                               object:nil];
}

The idea is adding or removing UIApplicationDidEnterBackgroundNotification whenever coming in and out of your screen. We just register UIApplicationWillEnterForegroundNotification when the app enter background and remove once it's back. Be noticed that we just remove UIApplicationDidEnterBackgroundNotification when viewWillDisappear.

My dealloc() is not called by somehow, so I found this way, hope it useful for you too.

Enjoy :)

thanhbinh84
  • 17,876
  • 6
  • 62
  • 69
  • you forgot to call [super ...] in those. Per documentation. – GregJaskiewicz Nov 21 '12 at 11:19
  • Are they required? I don't see any problem without them since I am using this code in my app. – thanhbinh84 Nov 22 '12 at 07:04
  • 7
    You'll see a problem when the super object will perform its own actions. Get in the habit of doing it, because the doc says so. Otherwise one day you might get into weird issues. – GregJaskiewicz Nov 22 '12 at 21:28
  • 1
    Thanks Greg, you are right. They mention in the api doc http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html – thanhbinh84 Nov 23 '12 at 02:29
  • 1
    @doraemon Yes, you will sooner or later run into problems if you do not call super in these methods. They are in fact doing some "magic stuff". I have seen several problems that have been solved by adding calls to super. – Johan Karlsson Oct 31 '14 at 10:16
  • Instead of adding and removing these notifications, why not just add them in `init` or `viewDidLoad` and remove all observers in `dealloc`? Or if you really must have them removed when the view disappears, add them both in `viewWillAppear` and remove them in `viewDidDisappear`. No need to juggle :) – Chris Nolet Nov 22 '14 at 07:02