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.
When you create an observer, you start observation by calling the observe(_:options:changeHandler:) method with a key path that refers to the property you want to observe. You use the oldValue and newValue properties of the NSKeyValueObservedChange instance to see what's changed about the property you're observing.
KVO, which stands for Key-Value Observing, is one of the techniques for observing the program state changes available in Objective-C and Swift. The concept is simple: when we have an object with some instance variables, KVO allows other objects to establish surveillance on changes for any of those instance variables.
Property observers observe and respond to changes in a property's value. Property observers are called every time a property's value is set, even if the new value is the same as the property's current value. You can add property observers in the following places: Stored properties that you define.
KVO and KVC or Key-Value Observing and Key-Value Coding are mechanisms originally built and provided by Objective-C that allows us to locate and interact with the underlying properties of a class that inherits NSObject at runtime.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With