'Cannot remove observer' crash on iOS 10 devices

My app started to crash on several devices running iOS 10.x. My app is partly made of Objective-C and Swift and the culprit is:


*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <CameraViewController 0x115e0a5f0> for the key path "queueLength" from <Connectivity 0x170469f00> because it is not registered as an observer.'


Connectivity is an Objective-C based singleton NSObject derived class which I observe from CameraViewController. I've beed adding the observer in CameraViewController viewWillAppear and remove it in CameraViewController viewWillDisappear.


App has been running fine in hundreds of devices and users but lately it has started to crash on small percentage of devices and users.


It seems that for some unknown reason either viewWillDisappear is called twice or viewWillAppear is not called at all. I'm not adding or removing observers in any place other than those two functions.


I add the observer in viewWillAppear with


[[Connectivity sharedConnectivity] addObserver:self forKeyPath:@"queueLength" options:NSKeyValueObservingOptionNew context:nil];


and remove it in viewWillDisappear with


[[Connectivity sharedConnectivity] removeObserver:self forKeyPath:@"queueLength"];


Been testing this on my device and simulator but can't make it crash.


Any ideas

Why do you remove the observer in viewWillDisappear and not at deinit ?


If you want to do it in viewWillDisappear, you could manage a Bool flag to keep track of wether it exists or not. And call addObserver and removeObserver accordingly.

I'm only interested in the notification while my ViewController is visible so that is why I bind it in the appear / disappear and not in init / deinit. As said it has been working fine in pre iOS 10 devices. In my case it seems that observer is being removed without it being added first. Somehow the viewWillAppear and viewWillDisappear calls are not balanced.

According to this http://useyourloaf.com/blog/unregistering-nsnotificationcenter-observers-in-ios-9/ one does not need to remove observers anymore if targetting iOS 9 and later.


So one can simply register observer in viewDidLoad and simply leave it there. No need to remove it ever thus this bug should not happen anymore.


Still curious why this bug started to manifest though...

Since it's very easy to keep track of when you have an observation registered, it's worth doing that to ensure that your app doesn't crash. Also, since you've seen a problem, you should probably add code to keep track of whether viewWillAppear isn't called or viewWillDisappear is called twice. (The difference matters, because if viewWillAppear isn't called, then you're not registered at all.)


However, your code is incorrect. You should never register an observation with a context of nil. Always use a unique context pointer value, and in the "observeValueForKeyPath:…" method always check that you've been given the same context. (If not, invoke the super method and do nothing else.) In Obj-C, the easiest way to get a unique context is to create a static variable:


     static void* myContext = &myContext;


Initializing a variable to its own address is a (valid) C quirk that gives you a guaranteed unique pointer.


Also, you should never de-register an observation using the method that has no context pointer. Use the newer method that takes has the context pointer. There is a longstanding bug in KVO where failing to specify the context can result in the wrong observation being removed.


The correct approach is documented here:


developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOBasics.html

Your problem is with key-value observing. NSNotificationCenter (in the blog post) is entirely separate.


With key-value observing, you still must remove the observer if and only if you added the observer. See @QuincyMorris's post above for a good way to do this. The link posted above is very useful: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOBasics.html


Thanks for the info about removing observers with NSNotificationCenter, though. 🙂 I filed bug #29860777 since the NSNotificationCenter documentation does not make this detail clear.

'Cannot remove observer' crash on iOS 10 devices
 
 
Q