Ein -observeValueForKeyPath: ofObject: Änderung: Kontext: Nachricht wurde empfangen, aber nicht behandelt
-
29-09-2019 - |
Frage
Ich bin relativ neu in KVO, so dass es eine gute Chance, dass ich einige grundlegende Regel bin zu verletzen. Ich bin mit Core Data.
Meine app stürzt mit folgenden Meldung:. Und was ich nicht verstehe ist, warum ein CGImage wird bei der Beobachtung Wert einzulassen, die auf einem MeasurementPointer Objekt festgelegt ist
*** 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”.
Alles, was diese auszulösen geschieht, ist:
[[self measurementPointer] setMeasurementDescriptor:descriptor];
Unter dieser Voraussetzung
[[meterDisplay measurementPointer] addObserver:self
forKeyPath:@"measurementDescriptor"
options:NSKeyValueObservingOptionNew
context:nil];
Grundsätzlich Objekte MeasurementPointer Punkt MeasurementDescriptor Objekte - und beide sind NSManagedObject Subklassen. MeasurementDescriptor Objekte beschreiben eine bestimmte ‚Messung‘ und ‚Einheit‘ Kombination (z.B. „Temperatur (° C)“ oder „Windgeschwindigkeit (mph)“). MeasurementDescriptors sind so etwas wie Singletons in dem Maße, dass es nur eine für jede einzelne Messeinheit Combo ist.
MeasurementPointers werden durch andere Objekte referenziert - beide Modellobjekte und Controller-Objekte. Ein MeasurementPointer verweist auf eine MeasurementDescriptor. Viele Objekte sind zu wissen, interessiert, wenn ein MeasurementPointer beginnt Bezugnahme auf eine neue / andere MeasurementDescriptor. Eine solche Änderung eine Diagrammanzeige der Achse zu ändern, zum Beispiel verursachen könnte. Oder in dem obigen Code, könnte eine Messanzeige verursacht eine andere Probe zeigen (von einer ausgewählten Gruppe von Proben).
Ich denke, das grundlegende Problem ist, dass ein CGImage eine Nachricht empfängt, die nicht für ihn bestimmt ist ... leider ist diese intermittierend, also habe ich nicht in der Lage gewesen, ein Muster, dass Trigger, es zu finden.
Lösung
Sie haben ein Objekt, das dealloc'ed habe und nicht stoppen ein anderes Objekt zu beobachten. Gehen Sie durch alle Ihre -addObserver...
Anrufe und stellen Sie sicher, dass sie mit -removeObserver...
Anrufe abgestimmt sind zumindest in der -dealloc
und möglicherweise in der -viewDidUnload
je nach Anwendungsstruktur.
Andere Tipps
habe ich diesen Fehler, wenn ich die observeValueForKeyPath
Methode super
geschickt, die ich nicht als Beobachter für die Änderung registriert hatte. Apple- docs sagen: „Seien Sie sicher, die übergeordnete Klasse Implementierung [von observeValueForKeyPath
], um , wenn sie implementiert es . "
Meine Verlegenheit zu ändern war:
- (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];
}
zu:
- (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) {
}
}
Ich lief in dieses Problem, indem sie zufällig das Ziel in als Beobachter vorbei (statt Selbst) wie folgt:
[self.someView addObserver:self.someView forKeyPath:@"key" options:0 context:nil];
Die Fehlermeldung war gar nicht hilfreich diese zu identifizieren, so dachte nur, ich für den Fall von jemandem würde sonst tut das gleiche.
Ich hatte das gleiche Problem, aber in meinem Fall ich einen anderen Kontext zu beobachten. Dann habe ich alle im gleichen Kontext und der Absturz war verschwunden. Ich hoffe, das hilft jemand.
Für mich habe ich vergessen, in override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
hinzufügen, um tatsächlich etwas zu tun mit der keyPath beobachtet zu werden.
Hier ist ein Beispiel für die Swift-Version. Fügen Sie die Beobachter in viewDidLoad
und entfernen Sie sie 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")
}