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];
}
Josh Brown
  • 52,385
  • 10
  • 54
  • 80
Azhar
  • 20,500
  • 38
  • 146
  • 211
  • 1
    It could help to give us some code to look at. – Totumus Maximus Oct 13 '11 at 08:13
  • Please see this answer http://stackoverflow.com/questions/10835000/initwithnibname-either-called-twice-or-wrong-xib-loaded, regarding the "blue box" class instance in interface builder... – M Jesse May 16 '14 at 03:32
  • Maybe your viewDidLoad is called twice. Try to see if this answer can help you: https://stackoverflow.com/a/46018292/2480458 – Cue May 04 '19 at 23:17

9 Answers9

145

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];
EmptyStack
  • 51,274
  • 23
  • 147
  • 178
  • 2
    Maybe you receive the notification two times. To check it set breakpoint in `aSelector` and look at calling stack. – brigadir Oct 13 '11 at 10:30
  • 10
    [[NSNotificationCenter defaultCenter] removeObserver:self name:aName object:nil]; is the best answer – SamehDos Feb 21 '12 at 08:16
  • 3
    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. – toblerpwn Jan 29 '13 at 21:52
  • 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. – RyanG Jun 14 '13 at 17:38
  • 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. – H.Rabiee Dec 29 '14 at 23:46
  • 2
    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. – Bhavin Ramani Oct 07 '15 at 14:10
  • Solution 2 rocks, because I add notification once in `awakeFromNib`, and `awakeFromNib` is called twice. – olha Aug 22 '16 at 15:05
  • @Azhar have you found any solution ? – Jack Jun 21 '18 at 09:48
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.

tfrank377
  • 1,858
  • 2
  • 22
  • 34
  • 1
    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. – Ashwin G Jun 04 '14 at 09:08
  • 1
    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. – Maverick1st May 12 '15 at 16:53
  • 1
    `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? – ToolmakerSteve Sep 07 '16 at 08:47
  • 1
    use GCD and dispatch once to ensure observer is added only once. static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //add observer code }); – RamaKrishna Chunduri Sep 08 '16 at 09:42
  • 1
    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. – Jonny May 06 '17 at 04:56
  • @Jonny 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... – OxyFlax Dec 12 '17 at 15:42
  • @OxyFlax 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: https://developer.apple.com/documentation/foundation/nsnotificationcenter/1415360-addobserver – Jonny Dec 12 '17 at 17:19
  • 1
    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. – bruce1337 Mar 17 '18 at 06:11
  • 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 above – Paul Bruneau Feb 24 '22 at 19:43
6

Try to removeObserver in viewWillDisappear method :

-(void)viewWillDisappear:(BOOL)animated{

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"startAnimating" object:nil]; }
bhadresh
  • 445
  • 5
  • 15
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.

IronManGill
  • 7,222
  • 2
  • 31
  • 52
stcui
  • 106
  • 4
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.

Joyson
  • 3,025
  • 1
  • 20
  • 34
  • 4
    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. – Cian Dec 14 '16 at 04:33
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.

Vladimír Slavík
  • 1,727
  • 1
  • 21
  • 31
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.

green_knight
  • 1,319
  • 14
  • 26
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)
    }
    
Michael
  • 748
  • 8
  • 11
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)
      
    }
steveSarsawa
  • 1,559
  • 2
  • 14
  • 31