104

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?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Zhen
  • 12,361
  • 38
  • 122
  • 199
  • I put it in the dealloc method. – onnoweb Jun 24 '11 at 14:28
  • 1
    The dealloc method was not automatically created for me when I created the objective c class, so it's ok for me to add it in? – Zhen Jun 24 '11 at 15:21
  • Yes, you can implement `-(void)dealloc` and then add `removeObserser:self` in it. This is the most recommended way to put `removeObservers:self` – petershine Jun 24 '11 at 15:42
  • Is it still ok to put in the `dealloc` method in iOS 6? – wcochran May 22 '13 at 22:05
  • 2
    Yes, it is ok to use dealloc in ARC projects as long as you do not call [super dealloc] (you will get a compiler error if you call [super dealloc]). And yes, you can definitely put your removeObserver in dealloc. – Phil May 24 '13 at 12:10
  • Dealloc still exists, but it is not the place for this, your dealloc will never be called because of the notificationcenter references to your object – mcfedr Oct 08 '13 at 07:14
  • @mcfedr what do you offer when NotificationCenter references to an object? – Shamsiddin Saidov Apr 24 '17 at 06:22
  • @Shamsiddin That comment of mine is actually wrong. NotificationCentre doesnt keep strong references to objects. – mcfedr Apr 25 '17 at 08:13

14 Answers14

112

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).

Dirk
  • 30,623
  • 8
  • 82
  • 102
  • 4
    This is not safe with ARC and could potentially cause a leak. See this discsussion: http://www.cocoabuilder.com/archive/cocoa/311831-arc-and-dealloc.html – MobileMon Jun 27 '13 at 19:54
  • 3
    @MobileMon The article you linked to seems to make my point. What am I missing ? – Dirk Jun 27 '13 at 22:58
  • I suppose it should be noted in that one should remove observer somewhere else other than dealloc. For example, viewwilldisappear – MobileMon Jul 01 '13 at 13:01
  • 1
    @MobileMon -- yes. I hope, that that's the point I am getting across with my answer. Removing the observer in `dealloc` is only a last line of defence against crashing the app due to a later access to a decallocated object. But the proper place to unregister an observer is usually somewhere else (and often, much earlier in the object's life-cycle). I am not trying to say here "Hey, just do it in `dealloc` and everything will be fine". – Dirk Jul 01 '13 at 13:15
  • @MobileMon "For example, `viewWillDisappear`" The problem with giving a concrete advise is, that it really depends on what kind of object you register as observer for what kind of event. It **may** be the right solution to unregister an observer in `viewWillDisappear` (or `viewDidUnload`) for `UIViewController`s, but that really depends on the use case. – Dirk Jul 01 '13 at 13:19
  • Yeah I think I misread your answer. I believe I meant to put it on the answer below, sorry about that – MobileMon Jul 01 '13 at 14:39
  • ARC forbits explicit calls to dealloc, eg [super dealloc]. This doesn't work for iOS 6. – quantumpotato Sep 13 '13 at 00:07
  • This will cause leaks! Dealloc will never be called because the notificationCenter is still holding references to your object – mcfedr Oct 08 '13 at 07:13
  • @mcfedr: no, the `NSNotificationCenter` does not retain objects added with `addObserver:selector:name:object:`. It doesn't specify this explicitly in the documentation, but you can infer it from sentences like "Be sure to invoke removeObserver: or removeObserver:name:object: before notificationObserver or any object specified in addObserver:selector:name:object: is deallocated." (or you can run a small test program and verify the behaviour, as I just did to be sure). – Dirk Oct 08 '13 at 14:22
  • @lekksi -- Unfortunately, the answer you favour will only make sense for `UIViewController`s. If we are talking about arbitrary application-defined classes (as the OP explicitly does), you simply cannot give general advice as to when it's best to remove an observer from the notification center. And even in the case of a `UIViewController` descendant, you do not know in general, whether `viewWillAppear:`/`viewWillDisappear:` is really appropriate. – Dirk Feb 26 '14 at 13:38
  • @Dirk You are right, my wording for the previous comment was not exact enough, sorry about that. :) What I tried to state is that: When working with ViewControllers, iOS7 and ARC (as many of us do nowadays), it might be better to do e.g. as suggested in RickiG's answer than use dealloc. – lekksi Feb 27 '14 at 12:25
  • which statement is right from below and why !!!! 1) [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(proximityChanged) name: ***@"UIDeviceProximityStateDidChangeNotification"*** object:[UIDevice currentDevice]]; 2) [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(proximityChanged) name: ***UIDeviceProximityStateDidChangeNotification*** object:[UIDevice currentDevice]]; – Shrikant Phadke Apr 08 '20 at 06:52
40

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

Sebastian
  • 2,109
  • 1
  • 20
  • 15
  • 2
    May be they will not send messages to observers, but I believe they will keep a strong reference to them as I understand. In that case all observers will stay in memory and produce a leak. Сorrect me if I am wrong. – fir Oct 21 '16 at 15:44
  • 6
    The linked documentation goes into detail about that. TL;DR: it's a weak reference. – Sebastian Oct 21 '16 at 16:17
  • but of course it's still necessary in case you keep the object referencing them around and just don't want to listen to the notifications anymore – TheEye Mar 14 '18 at 12:29
39

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];
    }
}
Alexander Volkov
  • 7,904
  • 1
  • 47
  • 44
Paresh Navadiya
  • 38,095
  • 11
  • 81
  • 132
  • 8
    Except a controller may still want notifications when its view is *not* showing (e.g. to reload a tableView). – wcochran May 22 '13 at 22:09
  • 2
    @wcochran automatically reload/refresh in `viewWillAppear:` – Richard Aug 15 '13 at 20:57
  • @Prince can you explain why viewWillDisapper better then dealloc? so we have add observer to self, so when the self will be dropped from memory it will call dealloc and then all observers will be deleted, is this not a good logic. – Matrosov Oleksandr Apr 22 '14 at 15:53
  • Calling `removeObserver:self` in any of the `UIViewController` lifecycle events is almost guaranteed to ruin your week. More reading: http://subjective-objective-c.blogspot.com/2011/04/avoid-nsnotification-removeobserver.html – cbowns Nov 07 '14 at 22:18
  • 1
    Putting the `removeObserver` calls in `viewWillDisappear` as indicated is definitely the right way to go if the controller is being presented via `pushViewController`. If you put them in `dealloc` instead then `dealloc` will never be called -- in my experience at least... – Christopher King Nov 11 '14 at 15:46
  • how to do the same in swift without type casting [self.navigationController viewControllers] to NSArray ?? – raw3d Jan 07 '15 at 10:13
  • The EDIT suggesting checking the navigation hierarchy for the controller is the perfect solution for @wcochran's point. Very helpful – wuf810 Apr 29 '15 at 16:30
25

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

Abdurrahman Mubeen Ali
  • 1,331
  • 1
  • 13
  • 19
RickiG
  • 11,380
  • 13
  • 81
  • 120
  • I'm curious, @RickiG: why do you recommend using `viewWillAppear` and `viewWillDisappear` for viewControllers? – Isaac Overacker Dec 12 '12 at 02:40
  • 2
    @IsaacOveracker a few reasons: your setup up code (eg. loadView and viewDidLoad) could potentially cause the notifications to be fired and your controller needs to reflect that before it shows. If you do it like this there are a few benefits. At the moment you decided to "leave" the controller you don't care about the notifications and they wont cause you to do logic while the controller is being pushed off screen etc. There are special cases where the controller should receive notifications when it is off-screen I guess you can't do this. But events like that should probably be in your model. – RickiG Dec 12 '12 at 15:20
  • 1
    @IsaacOveracker also with ARC it would be weird to implement dealloc to unsubscribe to notifications. – RickiG Dec 12 '12 at 15:23
  • 4
    Of those what I've tried, with iOS7 this is the best way to register/remove observers when working with UIViewControllers. The only catch is that, in many cases you don't want the observer to be removed when using UINavigationController and pushing another UIViewController to the stack. Solution: You can check if the VC is being popped in viewWillDisappear by calling [self isBeingDismissed]. – lekksi Feb 24 '14 at 12:49
  • Popping the view controller from navigation controller may not cause `dealloc` to be called immediately. Going back into the view controller may then cause multiple notifications if observer is added in initialization commands. – Jonathan Lin Jun 03 '14 at 10:28
20
-(void) dealloc {
      [[NSNotificationCenter defaultCenter] removeObserver:self];
      [super dealloc];
}
Legolas
  • 12,145
  • 12
  • 79
  • 132
  • 4
    I'd turn the order of these instructions around... Using `self` after `[super dealloc]` makes me nervous... (even if the receiver is unlikely to actually dereference the pointer in any way, well, you never know, how they implemented `NSNotificationCenter`) – Dirk Jun 24 '11 at 14:42
  • Hm. it has worked for me. Have you noticed any unusual behavior ? – Legolas Jun 24 '11 at 14:47
  • Nope. This was just a general remark. It just feels like using a `malloc` ed pointer after the call to `free` to me... – Dirk Jun 24 '11 at 14:52
  • 1
    Dirk is right -- this is incorrect. `[super dealloc]` must always be the **last** statement of your `dealloc` method. It destroys your object; after it runs, you don't have a valid `self` anymore. /cc @Dirk – jscs Jun 24 '11 at 16:22
  • @Josh : Hey dude. So are you sure about this ? So you are saying that all `dealloc` statements do not work (or) do not mean anything if I do this way ? – Legolas Jun 24 '11 at 16:28
  • @Legolas: They may work most of the time, but they're not safe. – jscs Jun 24 '11 at 16:36
  • @Josh: Well yeah. They have worked for me so far. Let me try switching things in your way. May be these were the reasons for the `memory leaks` – Legolas Jun 24 '11 at 16:40
  • 39
    If using ARC on iOS 5+, I think `[super dealloc]` is not needed anymore – pixelfreak Apr 16 '12 at 06:25
  • 3
    @pixelfreak stronger, it is not allowed under ARC to call [super dealloc] – tapmonkey Mar 26 '13 at 18:48
  • So is it still needed to removeObserver in dealloc using ARC ? – Alexandre Sep 04 '13 at 08:25
  • Its ok to put there. You need to basically place in that based on the life cycle of the VC. `viewDidDisappear` is a good place. – Legolas Sep 16 '13 at 20:27
  • its up to you .. you can remove them in `viewWillDisapper` or in `viewDidDisapper` methods. – Ans Jul 14 '14 at 08:37
  • It is better still to remove observer in dealloc, but without the call to super obviously (it is done automatically by ARC). – Borzh Sep 25 '15 at 21:29
  • This is wrong answer. If dealloc is called, then instance is already unsubscribed (by the system). – Alexander Volkov Dec 03 '20 at 14:18
7

In general I put it into the dealloc method.

Raphael Petegrosso
  • 3,870
  • 2
  • 24
  • 27
6

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.

Morten Holmgaard
  • 7,484
  • 8
  • 63
  • 85
  • Wrong! You should never unsubscribe in `deinit` because it's useless. When instance is clean from memory iOS unsubscribes it automatically, then it calls `deinit`, so it's useless. – Alexander Volkov Dec 03 '20 at 14:17
  • @AlexanderVolkov Please remember this answer is 5 years old, when swift was brand new. At that time it was the solution but it has changed since. – Morten Holmgaard Dec 04 '20 at 10:51
  • Nope. If deinit is called, then iOS automatically unsubscribed the observer. This is equal in ObjC and in Swift, it's system dependent, not language. Probably I wrong, but then provide a reference to documentation. – Alexander Volkov Dec 04 '20 at 21:39
5

*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).

Ehren
  • 504
  • 5
  • 13
  • 2
    Welcome to StackOverflow. Please checkout the MarkDown FAQ (question-mark icon next to the question/answer edit box). Using Markdwon will improve the usability of your answer. – marko Nov 25 '12 at 12:37
5

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.

Abdurrahman Mubeen Ali
  • 1,331
  • 1
  • 13
  • 19
kimimaro
  • 1,263
  • 2
  • 12
  • 21
  • Lots of people are still writing code for older versions of iOS than iOS6.... :-) – lnafziger Jan 16 '13 at 06:03
  • In ARC you can use this code but without the line [super dealloc]; You can see more here: http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226-CH1-SW14 – Alex Mar 07 '13 at 10:33
  • 1
    What if you had a regular NSObject be the observer of a notification? Would you use dealloc in this case? – qix May 23 '13 at 22:27
4

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.

Murat Zazi
  • 347
  • 4
  • 10
  • It is hard to say if this is right as the context is important. It is mentioned a few times already, but dealloc makes little sense in an ARC context (which is the only context by now). It is also not predictable when dealloc is called - viewWillDisappear is easier to control. A side note: If your child needs to communicate something to it's parent the delegate pattern sounds like a better choice. – RickiG Aug 12 '15 at 11:01
2

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! :)

MobileMon
  • 8,341
  • 5
  • 56
  • 75
  • 1
    I agree with this answer. I experience memory warnings and leaks leading to crashes after intensive use of the app if I don't remove the observers in viewWillDisappear. – SarpErdag Oct 11 '14 at 12:34
1

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];

}
Alex
  • 995
  • 12
  • 25
0
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)
}
urvashi bhagat
  • 1,123
  • 12
  • 15
-1

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!

Alexander Volkov
  • 7,904
  • 1
  • 47
  • 44