How to stop the Observer in NSNotification to called twice?
Asked Answered
A

9

64

I have an observer of NSNotification which is called twice. I do not know what to do with it.

I googled it but no solution found.

[[NSNotificationCenter defaultCenter] addObserver:self
     selector:@selector(connectedToServer:) name:@"ConnectedToServer" object:nil];

- (void)connectedToServer:(NSNotification*)notification {

    [[NSNotificationCenter defaultCenter] postNotificationName:@"SendMessageToServer" object:message];
}
Aboutface answered 13/10, 2011 at 8:11 Comment(3)
It could help to give us some code to look at.Diverticulosis
Please see this answer #10835500, regarding the "blue box" class instance in interface builder...Kettie
Maybe your viewDidLoad is called twice. Try to see if this answer can help you: https://mcmap.net/q/302854/-same-view-controller-loads-twiceExpel
U
146

Solution 1: The first thing is to check if the notification itself is posted twice.

Solution 2: Even if the notification is posted only once, the action will be called as many times you've added the observer for the notification (no matter the notification is same or not). For example, the following two lines will register the observer(self) for the same notification(aSelector) twice.

[[NSNotificationCenter defaultCenter] addObserver:self selector:aSelector name:aName object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:aSelector name:aName object:nil];

You have to find where you are adding observer for the second time, and remove it. And also make sure that the code where you are add the observer is not called twice.

Solution 3: If you are not sure whether you have already added the observer or not, you can simply do the following. This will make sure that the observer is added only once.

[[NSNotificationCenter defaultCenter] removeObserver:self name:aName object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:aSelector name:aName object:nil];
Unlettered answered 13/10, 2011 at 8:21 Comment(8)
Maybe you receive the notification two times. To check it set breakpoint in aSelector and look at calling stack.Jadeite
[[NSNotificationCenter defaultCenter] removeObserver:self name:aName object:nil]; is the best answerSusiesuslik
As with Azhar, I could not resolve this issue using the methods above. Not sure what was going on - but in my case I switched to using a delegate for my callback needs. This is not a solution for all cases, but a workaround that may help some future readers who are also stuck.Barrel
Wow I was debugging this for way too long... I was calling addObserver at the beginning of a function and not removing it correctly.. hence my function being called multiple times. Added the remove above it and works perfect.Jeanette
if you're registering the observer in one of your nib init methods then it's most likely being called (and registered) more than once.Cominform
I got solution from solution 3. I have implement this method [NSNotificationCenter defaultCenter] in view did load and view did load was called two times in class.Violetteviolin
Solution 2 rocks, because I add notification once in awakeFromNib, and awakeFromNib is called twice.Cirone
@Aboutface have you found any solution ?Sharie
C
17

If your addObserver method is run multiple times, it will create multiple observers. My issue was that I somehow placed my observer in viewWillAppear which appeared multiple times before I posted the notification and it resulted in my observer being called multiple times.

While EmptyStack's 3rd solution works, there is a reason your observer is being called twice, so by finding it, you can prevent needless lines of code instead of removing and adding the same observer.

I would suggest putting your observer in viewDidLoad to avoid simple errors like the one I experienced.

Cherri answered 5/2, 2014 at 0:16 Comment(9)
This is probably the solution. A breakpoint can be kept in the addObserver method to check how many times this same observer has been added.Pistoia
If i read this a few hours before i would have saved sooo much time. just couldn't figure out, why multiple observers where registered. Now moved the registration from viewWillAppear to viewDidLoad and it works like a charm. Thx a lot.Wixted
addObserver in viewWillAppear works fine, if you add the corresponding removeObserver in viewWillDisappear, as suggested by bhadresh! (Which you should do anyway - no reason to have unnecessary observers hanging around.) If you put addObserver in viewDidLoad, then where do you put removeObserver? Do you really want the observer hanging around after your view is not being seen?Idiot
use GCD and dispatch once to ensure observer is added only once. static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //add observer code });Medicable
In my case viewDidLoad was called twice, unexpectedly. Do not rely on viewDidLoad being called only once. I mean I do, but this was the culprit this time.Brigandine
@Brigandine How did you solve the problem? That's my case, viewDidLoad, ViewWillAppear are called twice and at the same time so delete the notification before adding it doesn't solve the problem...Hulky
@Hulky I don't remember what I was working on. But normally doing addObserver using NotificationCenter in viewDidLoad should be correct... For NC I no longer use removeObserver at all, since iOS 9 - see the docs under discussion: developer.apple.com/documentation/foundation/…Brigandine
Just to note that there are many answers elsewhere for questions like "Why was viewDidLoad() called twice?" – and they all emphasize that there is no guarantee that viewDidLoad will only be called once. It would seem that performing removeObserver before addObserver is the way to go.Wilcher
I also had viewdidLoad getting called repeatedly, occasionally. Makes sense. I put the addobserver call in init. If you can't do that, you can wrap it in a dispatch_once as suggested aboveRosio
O
6

Try to removeObserver in viewWillDisappear method :

-(void)viewWillDisappear:(BOOL)animated{

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"startAnimating" object:nil]; }
Officiary answered 15/2, 2016 at 8:29 Comment(0)
B
2

Try set a breakpoint on [[NSNotificationCenter defaultCenter] addObserver:self selector:aSelector name:aName object:nil]; and check if it is called more than once.

Baghdad answered 9/5, 2013 at 3:6 Comment(0)
S
2

For those looking for a solution in Swift 2.2 and above and who have reached this question like me you can create an extension as follows :

import Foundation

extension NSNotificationCenter {
  func setObserver(observer: AnyObject, selector: Selector, name: String?, object: AnyObject?) {
    NSNotificationCenter.defaultCenter().removeObserver(observer, name: name, object: object)
    NSNotificationCenter.defaultCenter().addObserver(observer, selector: selector, name: name, object: object)
  }
}

You can call this method as follows :

NSNotificationCenter.defaultCenter().setObserver(self, selector: #selector(methodName), name: "name", object: nil)

The extension will handle the removal of previous observer if it exists. Even if there was no previous observer present this code won't crash.

Sassoon answered 1/6, 2016 at 5:52 Comment(1)
Using this way, if the self(i.e. a UIViewcontroller) is allocated again and again, every new object of self would create an observer. Using above code would only remove the current self's observer but not the old self's observer. Have faced this issue before.Ishmaelite
S
0

I had two instances of the same class and it took me some time before I have realised that the notification is not running twice in one instance of that class but twice in two instances.

Spondee answered 5/4, 2019 at 14:36 Comment(0)
C
0

I use notifications in a document-based app. Every document's observer class (a ViewController) caught the notification. Two documents open, function was called twice. Three documents open... you get the drift.

To limit who receives the notification, you can specify that you're interested in a particular object, but this has a twist: the object needs to be the same instance of a class object; you cannot simply compare a value; so UUID does not get matched, but an instance of

class DocumentID {
    var id = UUID()
}

works fine. Inject this from your Document to every class that sends or receives notifications, and you've limited notifications to your document.

Com answered 28/8, 2020 at 8:51 Comment(0)
A
0

Swift 5+ Solution

NotificationCenter.default.removeObserver(self, name: aName, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(aSelector), name: aName, object: nil)

A couple of tips (and a couple ALWAYS means 2):

  1. Selector method must be exposed to @objc to work, so per our example:

    @objc func aSelector() { //do work here }
    
  1. I put the notifications in the init() and deinit() and use singletons to avoid repeating them, like this:

    init() {
        NotificationCenter.default.addObserver(self, selector: #selector(aSelector), name: aName, object: nil)
        //add any other notifications here
    }
    
    deinit() {
        NotificationCenter.default.removeObserver(self)
    }
    
Abreact answered 23/12, 2020 at 14:5 Comment(1)
If you are using combine way use .sink(receiveValue:) instead of .sink()Opinion
S
0

In my case Notification is calling the number of time i'm appearing in same screen and which is caused to triggered same action X numberofTime i'm entered to screen. So I've removed Notification observer in viewWillDisappear which is actually worked and stopped the multiple time triggered action/methods in same screen.

Thanks to Himanth's answer i've figured it out

  • Swift4

addObserver

 override func viewDidLoad(){
       super.viewDidLoad()

      NotificationCenter.default.addObserver(self, selector: #selector(self.yourNotificationAction(notification:)), name: Notification.Name("yourNotificationName"), object: nil)

}

removeObserver when screen is disappear

 override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        NotificationCenter.default.removeObserver(self, name: Notification.Name("yourNotificationName"), object: nil)
      
    }
Stepha answered 22/7, 2021 at 6:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.