Crash with removeObserver:forKeyPath: in Foundation

Lorenzo B picture Lorenzo B · May 11, 2015 · Viewed 7.4k times · Source

I having some problems with the following crash logs retrieved from the "Crashes" section in Xcode. Only few devices are affected by this crash report.

I have analyzed the problem but I guess it's a bug on Apple framework. But I cannot find a way to replicate it.

Here a similar discussion: Help with crash in removeObserver:forKeyPath:.

Any hints?

Thread 0 name: Thread 0 Crashed:

0 Foundation
0x23507591 _NSKeyValueReplaceObservationInfoForObject + 69 (NSKeyValueObserving.m:1166)

1 Foundation
0x23506fe7 -[NSObject(NSKeyValueObserverRegistration) _removeObserver:forProperty:] + 327 (NSKeyValueObserving.m:1552)

2 Foundation
0x23506b03 -[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:] + 163 (NSKeyValueObserving.m:1696)

3 Foundation
0x235069a7 -[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:context:] + 219 (NSKeyValueObserving.m:1663)

4 ApplicationName 0x0002e233 -[Supervisor removeObjectObserver:forKeyPath:] + 115 (Supervisor.m:344)

where removeObjectObserver:forKeyPath: is

- (void) removeObjectObserver:(id)object forKeyPath:(NSString *)keyPath { 

    @try {        
        [object removeObserver:self forKeyPath:keyPath context:PrivateKVOContext];
    
    } @catch (NSException *exception) { }
}

Answer

Loegic picture Loegic · Jun 2, 2015

Observers in Objective-C must be used with extra attention: don't add the same observer multiples time to the same object's property, and wrap the removal if there is one :

  if ([self observationInfo]) {
        @try {
            [self removeObserver:self forKeyPath:keyPath];
        }
        @catch (NSException *exception) {}
    }

You are experiencing crashes because you try to remove twice the observer, or you are removing a non-existant observer.

You should add observers this way :

[yourObject addObserver:self forKeyPath:keypath options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionInitial context:nil/yourContext];

EDIT: You may remove an observer on an already deallocate object, resulting in this crash.

  if (object && [self observationInfo]) {
    @try {
                [self removeObserver:self forKeyPath:keyPath];
            }
            @catch (NSException *exception) {}
}