Objective-C: Where to remove observer for NSNotification?

Objective CIosNsnotifications

Objective C Problem Overview


I have an objective C class. In it, I created a init method and set up a NSNotification in it

//Set up NSNotification
[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(getData)
                                             name:@"Answer Submitted"
                                           object:nil];

Where do I set the [[NSNotificationCenter defaultCenter] removeObserver:self] in this class? I know that for a UIViewController, I can add it into the viewDidUnload method So what needs to be done if I just created an objective c Class?

Objective C Solutions


Solution 1 - Objective C

The generic answer would be "as soon as you no longer need the notifications". This is obviously not a satisfying answer.

I'd recommend, that you add a call [notificationCenter removeObserver: self] in method dealloc of those classes, which you intend to use as observers, as it is the last chance to unregister an observer cleanly. This will, however, only protect you against crashes due to the notification center notifying dead objects. It cannot protect your code against receiving notifications, when your objects are not yet/no longer in a state in which they can properly handle the notification. For this... See above.

Edit (since the answer seems to draw more comments than I would have thought) All I am trying to say here is: it's really hard to give general advice as to when it's best to remove the observer from the notification center, because that depends:

  • On your use case (Which notifications are observed? When do they get send?)
  • The implementation of the observer (When is it ready to receive notifications? When is it no longer ready?)
  • The intended life-time of the observer (Is it tied to some other object, say, a view or view controller?)
  • ...

So, the best general advice I can come up with: to protect your app. against at least one possible failure, do the removeObserver: dance in dealloc, since that's the last point (in the object's life), where you can do that cleanly. What this does not mean is: "just defer the removal until dealloc is called, and everything will be fine". Instead, remove the observer as soon as the object is no longer ready (or required) to receive notifications. That is the exact right moment. Unfortunately, not knowing the answers to any of the questions mentioned above, I cannot even guess, when that moment would be.

You can always safely removeObserver: an object multiple times (and all but the very first call with a given observer will be nops). So: think about doing it (again) in dealloc just to be sure, but first and foremost: do it at the appropriate moment (which is determined by your use case).

Solution 2 - Objective C

Note : This has been tested and working 100% percent

Swift
override func viewWillDisappear(animated: Bool){
    super.viewWillDisappear(animated)
    
    if self.navigationController!.viewControllers.contains(self) == false  //any other hierarchy compare if it contains self or not
    {
        // the view has been removed from the navigation stack or hierarchy, back is probably the cause
        // this will be slow with a large stack however.
        
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}

PresentedViewController:

override func viewWillDisappear(animated: Bool){
    super.viewWillDisappear(animated)
    
    if self.isBeingDismissed()  //presented view controller
    {
        // remove observer here
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}
Objective-C

In iOS 6.0 > version , its better to remove observer in viewWillDisappear as viewDidUnload method is deprecated.

 [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];

There is many times its better to remove observer when the view has been removed from the navigation stack or hierarchy.

- (void)viewWillDisappear:(BOOL)animated{
 if (![[self.navigationController viewControllers] containsObject: self]) //any other hierarchy compare if it contains self or not
    {
        // the view has been removed from the navigation stack or hierarchy, back is probably the cause
        // this will be slow with a large stack however.
        
        [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
    }
}

PresentedViewController:

- (void)viewWillDisappear:(BOOL)animated{
    if ([self isBeingDismissed] == YES) ///presented view controller
    {
        // remove observer here
        [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
    }
}

Solution 3 - Objective C

Since iOS 9 it's no longer necessary to remove observers.

> In OS X 10.11 and iOS 9.0 NSNotificationCenter and > NSDistributedNotificationCenter will no longer send notifications to > registered observers that may be deallocated.

https://developer.apple.com/library/mac/releasenotes/Foundation/RN-Foundation/index.html#10_11NotificationCenter

Solution 4 - Objective C

If the observer is added to a view controller, I strongly recommend adding it in viewWillAppear and removing it in viewWillDisappear.

Solution 5 - Objective C

-(void) dealloc {
      [[NSNotificationCenter defaultCenter] removeObserver:self];
      [super dealloc];
}

Solution 6 - Objective C

In general I put it into the dealloc method.

Solution 7 - Objective C

In swift use deinit because dealloc is unavailable:

deinit {
    ...
}

Swift documentation:

> A deinitializer is called immediately before a class instance is > deallocated. You write deinitializers with the deinit keyword, similar > to how intializers are written with the init keyword. Deinitializers > are only available on class types. > > Typically you don’t need to perform manual clean-up when your > instances are deallocated. However, when you are working with your own > resources, you might need to perform some additional clean-up > yourself. For example, if you create a custom class to open a file and > write some data to it, you might need to close the file before the > class instance is deallocated.

Solution 8 - Objective C

*edit: This advice applies to iOS <= 5 (even there you should be adding in viewWillAppear and removing in viewWillDisappear - however the advice applies if for some reason you've added the observer in viewDidLoad)

If you've added the observer in viewDidLoad you should remove it in both dealloc and viewDidUnload. Otherwise you'll end up adding it twice when viewDidLoad is called after viewDidUnload (this will happen after a memory warning). This isn't necessary in iOS 6 where viewDidUnload is deprecated and won't be called (because views are no longer automatically unloaded).

Solution 9 - Objective C

In my opinion, the following code makes no sense in ARC:

- (void)dealloc
{
      [[NSNotificationCenter defaultCenter] removeObserver:self];
      [super dealloc];
}

In iOS 6, there's also no sense in removing observers in viewDidUnload, because it has been deprecated now.

To sum up, I always do it in viewDidDisappear. However, it depends on your requirements also, just like @Dirk said.

Solution 10 - Objective C

I think I found a reliable answer! I had to, as the answers above are ambiguous and seem contradicting. I looked through Cookbooks and Programming Guides.

First, the style of addObserver: in viewWillAppear: and removeObserver: in viewWillDisappear: does not work for me (I tested it) because I am posting a notification in a child view controller to execute code in the parent view controller. I would only use this style if I was posting and listening for the notification within the same view controller.

The answer I will rely on the most, I found in the iOS Programming: Big Nerd Ranch Guide 4th. I trust the BNR guys because they have iOS training centers and they are not just writing another cookbook. It is probably in their best interest to be accurate.

BNR example one: addObserver: in init:, removeObserver: in dealloc:

BNR example two: addObserver: in awakeFromNib:, removeObserver: in dealloc:

…when removing observer in dealloc: they don’t use [super dealloc];

I hope this helps the next person…

I am updating this post because Apple now has almost completely gone with Storyboards so the above mentioned may not apply to all situations. The important thing (and the reason I added this post in the first place) is to pay attention if your viewWillDisappear: is getting called. It wasn't for me when the application entered background.

Solution 11 - Objective C

The accepted answer is not safe and could cause a memory leak. Please do leave the unregister in dealloc but also deregister in viewWillDisappear (that is of course if you register in viewWillAppear)....THAT'S WHAT I DID ANYWAYS AND IT WORKS GREAT! :)

Solution 12 - Objective C

It is important to notice also that viewWillDisappear is called also when the view controller present a new UIView. This delegate simply indicate that the view controller main view is not visible on the display.

In this case, deallocating the notification in viewWillDisappear may be inconvenient if we are using the notification to allow the UIview to communicate with the parent view controller.

As a solution I usually remove the observer in one of these two methods:

- (void)viewWillDisappear:(BOOL)animated {
    NSLog(@"viewController will disappear");
    if ([self isBeingDismissed]) {
        NSLog(@"viewController is being dismissed");
        [[NSNotificationCenter defaultCenter] removeObserver:self name:@"actionCompleted" object:nil];
    }
}

-(void)dealloc {
    NSLog(@"viewController is being deallocated");
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"actionCompleted" object:nil];
}

For similar reasons, when I issue the notification the first time, I need to account for the fact that any time a view with appear above the controller then viewWillAppear method is fired. This will in turn generate multiple copy of the same notification. Since there isn't a way to check if a notification is already active, I obviate the problem by removing the notification before adding it:

- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"viewController will appear");
    // Add observers
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"imageGenerated" object:nil]; // This is added to avoid duplicate notifications when the view is presented again
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedImageFromCameraOrPhotolibraryMethodOnListener:) name:@"actionCompleted" object:nil];

}

Solution 13 - Objective C

override func viewDidLoad() {   //add observer
  super.viewDidLoad()
  NotificationCenter.default.addObserver(self, selector:#selector(Yourclassname.method), name: NSNotification.Name(rawValue: "NotificationIdentifier"), object: nil)
}

override func viewWillDisappear(_ animated: Bool) {    //remove observer
    super.viewWillDisappear(true)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "NotificationIdentifier"), object: nil)
}

Solution 14 - Objective C

This is the only correct answer (all other are mistakenly suggest to use deinit and dealloc - this is clear misunderstanding of class instance lifecycle and iOS system).

Swift 5

There are two cases of using notifications:

  • they are needed only when the view controller is on screen (viewWillAppear + viewWillDisappear)
  • they are needed always, even if user opened another screen over current (viewDidLoad + viewWillDisappear with "if").

For the first case the correct place to add and remove observer are:

    /// Add observers
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        NotificationCenter.default.addObserver(...)
    }

    /// Remove observers
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

// remove when screen dismissed
        NotificationCenter.default.removeObserver(self) 
    }

for the second case the correct way is:

    /// Add observers
    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(...)
    }

    /// Remove observers
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

// remove only when view controller is removed disappear forever
        if self.isBeingDismissed 
        || !(self.navigationController?.viewControllers.contains(self) ?? true) {
            NotificationCenter.default.removeObserver(self)
        }
    }

And never put removeObserver in deinit{ ... }, or dealloc - it's a MISTAKE!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionZhenView Question on Stackoverflow
Solution 1 - Objective CDirkView Answer on Stackoverflow
Solution 2 - Objective CParesh NavadiyaView Answer on Stackoverflow
Solution 3 - Objective CSebastianView Answer on Stackoverflow
Solution 4 - Objective CRickiGView Answer on Stackoverflow
Solution 5 - Objective CLegolasView Answer on Stackoverflow
Solution 6 - Objective CRaphael PetegrossoView Answer on Stackoverflow
Solution 7 - Objective CMorten HolmgaardView Answer on Stackoverflow
Solution 8 - Objective CEhrenView Answer on Stackoverflow
Solution 9 - Objective CkimimaroView Answer on Stackoverflow
Solution 10 - Objective CMurat ZaziView Answer on Stackoverflow
Solution 11 - Objective CMobileMonView Answer on Stackoverflow
Solution 12 - Objective CAlexView Answer on Stackoverflow
Solution 13 - Objective Curvashi bhagatView Answer on Stackoverflow
Solution 14 - Objective CAlexander VolkovView Answer on Stackoverflow