Objective-C: Where to remove observer for NSNotification?

85,497

Solution 1

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

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 3

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 4

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

Solution 5

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

Related videos on Youtube

Zhen
Author by

Zhen

Updated on December 04, 2020

Comments

  • Zhen
    Zhen over 3 years

    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?

    • onnoweb
      onnoweb about 13 years
      I put it in the dealloc method.
    • Zhen
      Zhen about 13 years
      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?
    • petershine
      petershine about 13 years
      Yes, you can implement -(void)dealloc and then add removeObserser:self in it. This is the most recommended way to put removeObservers:self
    • wcochran
      wcochran about 11 years
      Is it still ok to put in the dealloc method in iOS 6?
    • Phil
      Phil about 11 years
      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.
    • mcfedr
      mcfedr over 10 years
      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
    • Shamsiddin Saidov
      Shamsiddin Saidov about 7 years
      @mcfedr what do you offer when NotificationCenter references to an object?
    • mcfedr
      mcfedr about 7 years
      @Shamsiddin That comment of mine is actually wrong. NotificationCentre doesnt keep strong references to objects.
  • Dirk
    Dirk about 13 years
    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)
  • Legolas
    Legolas about 13 years
    Hm. it has worked for me. Have you noticed any unusual behavior ?
  • Dirk
    Dirk about 13 years
    Nope. This was just a general remark. It just feels like using a malloc ed pointer after the call to free to me...
  • jscs
    jscs about 13 years
    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
  • Legolas
    Legolas about 13 years
    @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 ?
  • jscs
    jscs about 13 years
    @Legolas: They may work most of the time, but they're not safe.
  • Legolas
    Legolas about 13 years
    @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
  • Deamon
    Deamon about 12 years
    If using ARC on iOS 5+, I think [super dealloc] is not needed anymore
  • marko
    marko over 11 years
    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.
  • Isaac Overacker
    Isaac Overacker over 11 years
    I'm curious, @RickiG: why do you recommend using viewWillAppear and viewWillDisappear for viewControllers?
  • RickiG
    RickiG over 11 years
    @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
    RickiG over 11 years
    @IsaacOveracker also with ARC it would be weird to implement dealloc to unsubscribe to notifications.
  • lnafziger
    lnafziger over 11 years
    Lots of people are still writing code for older versions of iOS than iOS6.... :-)
  • Alex
    Alex over 11 years
    In ARC you can use this code but without the line [super dealloc]; You can see more here: developer.apple.com/library/ios/#releasenotes/ObjectiveC/…
  • tapmonkey
    tapmonkey over 11 years
    @pixelfreak stronger, it is not allowed under ARC to call [super dealloc]
  • wcochran
    wcochran about 11 years
    Except a controller may still want notifications when its view is not showing (e.g. to reload a tableView).
  • qix
    qix about 11 years
    What if you had a regular NSObject be the observer of a notification? Would you use dealloc in this case?
  • MobileMon
    MobileMon about 11 years
    This is not safe with ARC and could potentially cause a leak. See this discsussion: cocoabuilder.com/archive/cocoa/311831-arc-and-dealloc.html
  • Dirk
    Dirk about 11 years
    @MobileMon The article you linked to seems to make my point. What am I missing ?
  • MobileMon
    MobileMon almost 11 years
    I suppose it should be noted in that one should remove observer somewhere else other than dealloc. For example, viewwilldisappear
  • Dirk
    Dirk almost 11 years
    @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
    Dirk almost 11 years
    @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 UIViewControllers, but that really depends on the use case.
  • MobileMon
    MobileMon almost 11 years
    Yeah I think I misread your answer. I believe I meant to put it on the answer below, sorry about that
  • Richard
    Richard almost 11 years
    @wcochran automatically reload/refresh in viewWillAppear:
  • Alexandre
    Alexandre almost 11 years
    So is it still needed to removeObserver in dealloc using ARC ?
  • quantumpotato
    quantumpotato almost 11 years
    ARC forbits explicit calls to dealloc, eg [super dealloc]. This doesn't work for iOS 6.
  • Legolas
    Legolas almost 11 years
    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.
  • mcfedr
    mcfedr over 10 years
    This will cause leaks! Dealloc will never be called because the notificationCenter is still holding references to your object
  • Dirk
    Dirk over 10 years
    @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).
  • lekksi
    lekksi over 10 years
    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].
  • Dirk
    Dirk over 10 years
    @lekksi -- Unfortunately, the answer you favour will only make sense for UIViewControllers. 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.
  • lekksi
    lekksi over 10 years
    @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.
  • Matrosov Oleksandr
    Matrosov Oleksandr about 10 years
    @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.
  • Jonathan Lin
    Jonathan Lin about 10 years
    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.
  • Ans
    Ans almost 10 years
    its up to you .. you can remove them in viewWillDisapper or in viewDidDisapper methods.
  • SarpErdag
    SarpErdag over 9 years
    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.
  • cbowns
    cbowns over 9 years
    Calling removeObserver:self in any of the UIViewController lifecycle events is almost guaranteed to ruin your week. More reading: subjective-objective-c.blogspot.com/2011/04/…
  • Christopher King
    Christopher King over 9 years
    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...
  • raw3d
    raw3d over 9 years
    how to do the same in swift without type casting [self.navigationController viewControllers] to NSArray ??
  • wuf810
    wuf810 about 9 years
    The EDIT suggesting checking the navigation hierarchy for the controller is the perfect solution for @wcochran's point. Very helpful
  • RickiG
    RickiG almost 9 years
    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.
  • Borzh
    Borzh almost 9 years
    It is better still to remove observer in dealloc, but without the call to super obviously (it is done automatically by ARC).
  • fir
    fir over 7 years
    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.
  • Sebastian
    Sebastian over 7 years
    The linked documentation goes into detail about that. TL;DR: it's a weak reference.
  • TheEye
    TheEye over 6 years
    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
  • Shrikant Phadke
    Shrikant Phadke about 4 years
    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]];
  • Alexander Volkov
    Alexander Volkov over 3 years
    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
    Alexander Volkov over 3 years
    This is wrong answer. If dealloc is called, then instance is already unsubscribed (by the system).
  • Morten Holmgaard
    Morten Holmgaard over 3 years
    @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.
  • Alexander Volkov
    Alexander Volkov over 3 years
    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.