An -observeValueForKeyPath:ofObject:change:context: message was received but not handled

23,176

Solution 1

You have an object that got dealloc'ed and did not stop observing another object. Walk through all of your -addObserver... calls and make sure they are matched with -removeObserver... calls at least in the -dealloc and possibly in the -viewDidUnload depending on your application structure.

Solution 2

I saw this error when I sent the observeValueForKeyPath method to super, which I had not registered as an observer for the change. Apple docs say "Be sure to call the superclass's implementation [of observeValueForKeyPath] if it implements it."

My fix was to change:

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
  if ([keyPath isEqualToString:kPropertyThatChanges]) {
    ...
  }
  [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}

to:

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
  if ([keyPath isEqualToString:kPropertyThatChanges]) {
    ...
  }
}

In swift:

func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if (keyPath == kPropertyThatChanges) {
    }
    super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
To:
func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if (keyPath == kPropertyThatChanges) {
    }
}

Solution 3

I ran into this problem by accidentally passing the target in as the observer (instead of self) like so:

[self.someView addObserver:self.someView forKeyPath:@"key" options:0 context:nil];

The error message was not at all helpful in identifying this so just thought I'd post in case anyone else does the same thing.

Solution 4

I had the same problem, but in my case I was observing a different context. Then I put all in the same context and the crash was gone. I hope this helps someone.

Solution 5

For me I forgot to add in override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) to actually do something with the keyPath getting observed.

Here's an example of the Swift version. Add the observers in viewDidLoad and remove them in deinit:

lazy var firstNameTextField: UITextField = {
    let textField = UITextField()
    // configure your textField
    return textField
}()

lazy var lastNameTextField: UITextField = {
    let textField = UITextField()
    // configure your textField
    return textField
}()

override func viewDidLoad() {
    super.viewDidLoad()

    // 1. add your observers here
    firstNameTextField.addObserver(self, forKeyPath: "text", options: [.old, .new], context: nil)
    lastNameTextField.addObserver(self, forKeyPath: "text", options: [.old, .new], context: nil)
}

// 2. ***IMPORTANT you must add this function or it will crash***
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

    if keyPath == "text" {
        print("do something when the textField's text your observing changes")
    }
}

 // 3. remove them in deinit
deinit {
    firstNameTextField.removeObserver(self, forKeyPath: "text", context: nil)
    lastNameTextField.removeObserver(self, forKeyPath: "text", context: nil)
    print("DEINIT")
}
Share:
23,176
westsider
Author by

westsider

I have a passion for developing technology that empowers people with its simplicity and elegance. Across programming languages, operating systems and hardware I have been doing this for more than 30 years. I started in BASIC-Plus on PDP-11/34 running RSTS/E - continued through FORTRAN, Pascal, several dialects of Lisp and from Xerox Lisp Machines to Mac, SPARC, Newton, Linux and on to iOS and Android.

Updated on August 06, 2020

Comments

  • westsider
    westsider almost 4 years

    I am relatively new to KVO, so there is a good chance that I am violating some fundamental rule. I am using Core Data.

    My app crashes with the following message: And what I can't understand is why a CGImage is getting involved in observing a value that is set on a MeasurementPointer object.

            *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<CGImage 0x276fc0>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
    Key path: measurementDescriptor
    Observed object: <MeasurementPointer: 0x8201640> (entity: MeasurementPointer; id: 0x8200410 <x-coredata://EBEE0687-D67D-4B03-8C95-F4C60CFDC20F/MeasurementPointer/p75> ; data: {
        measurementDescriptor = "0x260fd0 <x-coredata://EBEE0687-D67D-4B03-8C95-F4C60CFDC20F/MeasurementDescriptor/p22>";
    })
    Change: {
        kind = 1;
        new = "<MeasurementDescriptor: 0x262530> (entity: MeasurementDescriptor; id: 0x260fd0 <x-coredata://EBEE0687-D67D-4B03-8C95-F4C60CFDC20F/MeasurementDescriptor/p22> ; data: {\n    measurementName = Temperature;\n    measurementUnits = \"\\U00b0C\";\n    sortString = nil;\n})";
    }
    Context: 0x0'
    *** Call stack at first throw:
    (
        0   CoreFoundation                      0x30897ed3 __exceptionPreprocess + 114
        1   libobjc.A.dylib                     0x3002f811 objc_exception_throw + 24
        2   CoreFoundation                      0x30897d15 +[NSException raise:format:arguments:] + 68
        3   CoreFoundation                      0x30897d4f +[NSException raise:format:] + 34
        4   Foundation                          0x34a13779 -[NSObject(NSKeyValueObserving) observeValueForKeyPath:ofObject:change:context:] + 60
        5   Foundation                          0x349b6acd NSKeyValueNotifyObserver + 216
        6   Foundation                          0x349b6775 NSKeyValueDidChange + 236
        7   Foundation                          0x349ae489 -[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:] + 76
        8   CoreData                            0x3165b577 _PF_ManagedObject_DidChangeValueForKeyIndex + 102
        9   CoreData                            0x3165ac51 _sharedIMPL_setvfk_core + 184
        10  CoreData                            0x3165dc83 _svfk_0 + 10
        11  SPARKvue                            0x000479f1 -[MeasurementViewController doneAction:] + 152
        12  CoreFoundation                      0x3083f719 -[NSObject(NSObject) performSelector:withObject:withObject:] + 24
        13  UIKit                               0x31eb1141 -[UIApplication sendAction:to:from:forEvent:] + 84
        14  UIKit                               0x31f08315 -[UIBarButtonItem(UIInternal) _sendAction:withEvent:] + 92
        15  CoreFoundation                      0x3083f719 -[NSObject(NSObject) performSelector:withObject:withObject:] + 24
        16  UIKit                               0x31eb1141 -[UIApplication sendAction:to:from:forEvent:] + 84
        17  UIKit                               0x31eb10e1 -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 32
        18  UIKit                               0x31eb10b3 -[UIControl sendAction:to:forEvent:] + 38
        19  UIKit                               0x31eb0e05 -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 356
        20  UIKit                               0x31eb1453 -[UIControl touchesEnded:withEvent:] + 342
        21  UIKit                               0x31eafddd -[UIWindow _sendTouchesForEvent:] + 368
        22  UIKit                               0x31eaf757 -[UIWindow sendEvent:] + 262
        23  UIKit                               0x31eaa9ff -[UIApplication sendEvent:] + 298
        24  UIKit                               0x31eaa337 _UIApplicationHandleEvent + 5110
        25  GraphicsServices                    0x31e4504b PurpleEventCallback + 666
        26  CoreFoundation                      0x3082cce3 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 26
        27  CoreFoundation                      0x3082cca7 __CFRunLoopDoSource1 + 166
        28  CoreFoundation                      0x3081f56d __CFRunLoopRun + 520
        29  CoreFoundation                      0x3081f277 CFRunLoopRunSpecific + 230
        30  CoreFoundation                      0x3081f17f CFRunLoopRunInMode + 58
        31  GraphicsServices                    0x31e445f3 GSEventRunModal + 114
        32  GraphicsServices                    0x31e4469f GSEventRun + 62
        33  UIKit                               0x31e51123 -[UIApplication _run] + 402
        34  UIKit                               0x31e4f12f UIApplicationMain + 670
        35  SPARKvue                            0x000031ff main + 70
        36  SPARKvue                            0x000031b4 start + 40
    )
    terminate called after throwing an instance of 'NSException'
    Program received signal:  “SIGABRT”.
    

    All that is happening to trigger this is:

    [[self measurementPointer] setMeasurementDescriptor:descriptor];
    

    Given this,

    [[meterDisplay measurementPointer] addObserver:self 
                forKeyPath:@"measurementDescriptor"
                options:NSKeyValueObservingOptionNew
                context:nil];
    

    Basically, MeasurementPointer objects point to MeasurementDescriptor objects - and both are NSManagedObject subclasses. MeasurementDescriptor objects describe a specific 'measurement' and 'unit' combination (e.g., "Temperature (°C)" or "Wind Speed (mph)"). MeasurementDescriptors are something like singletons to the extent that there is only one for each unique measurement-unit combo.

    MeasurementPointers are referenced by other objects - both Model objects and Controller objects. A MeasurementPointer references a MeasurementDescriptor. Many objects are interested in knowing when a MeasurementPointer starts referencing a new/different MeasurementDescriptor. Such a change might cause a graph display's axis to change, for example. Or, in the code above, might cause a meter display to show a different sample (from a selected set of samples).

    I think that fundamental problem is that a CGImage is receiving a message that is not intended for it... unfortunately, this is intermittent, so I have not been able to find a pattern that triggers it.