Un -observeValueForKeyPath: ofObject: changement: contexte: le message a été reçu, mais pas manipulé
-
29-09-2019 - |
Question
Je suis relativement nouveau à KVO, donc il y a une bonne chance que je viole une règle fondamentale. J'utilise Core Data.
Mon application se bloque avec le message suivant:. Ce que je ne comprends pas pourquoi un CGImage s'implique à observer une valeur qui est définie sur un objet MeasurementPointer
*** 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”.
Tout ce qui se passe pour déclencher c'est:
[[self measurementPointer] setMeasurementDescriptor:descriptor];
Dans ce contexte,
[[meterDisplay measurementPointer] addObserver:self
forKeyPath:@"measurementDescriptor"
options:NSKeyValueObservingOptionNew
context:nil];
En gros, les objets MeasurementPointer pointez sur les objets MeasurementDescriptor - et les deux sont des sous-classes NSManagedObject. MeasurementDescriptor objets décrivent une combinaison spécifique de « mesure » et « unité » (par exemple, « Température (° C) » ou « Vitesse du vent (mph) »). MeasurementDescriptors sont quelque chose comme singletons dans la mesure où il n'y a qu'une seule pour chaque combo unique, unité de mesure.
MeasurementPointers sont référencées par d'autres objets - les deux objets modèle et les objets du contrôleur. Un MeasurementPointer fait référence à un MeasurementDescriptor. De nombreux objets sont intéressés à savoir quand un MeasurementPointer commence référence à une nouvelle / différente MeasurementDescriptor. Un tel changement pourrait provoquer l'axe d'un affichage graphique au changement, par exemple. Ou, dans le code ci-dessus, pourrait provoquer un affichage de compteur pour afficher un échantillon différent (à partir d'un ensemble d'échantillons sélectionnés).
Je pense que problème fondamental est qu'un CGImage reçoit un message qui ne vise pas pour elle ... malheureusement, cela est intermittent, donc je ne l'ai pas été en mesure de trouver un modèle qui déclenche.
La solution
Vous avez un objet qui a obtenu dealloc'ed et n'a pas cesser d'observer un autre objet. Marche à travers tous vos appels -addObserver...
et assurez-vous qu'ils sont jumelés à des appels -removeObserver...
au moins dans la -dealloc
et peut-être dans la -viewDidUnload
en fonction de la structure de votre application.
Autres conseils
J'ai vu cette erreur quand j'ai envoyé la méthode observeValueForKeyPath
à super
, que je ne l'avais pas enregistré à titre d'observateur pour le changement. docs d'Apple disent « Assurez-vous d'appeler la mise en œuvre de la superclasse [de observeValueForKeyPath
] si elle met en œuvre . "
Mon solution était de changer:
- (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];
}
à:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([keyPath isEqualToString:kPropertyThatChanges]) {
...
}
}
rapide:
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) {
}
}
je suis tombé sur ce problème en passant accidentellement la cible en tant que l'observateur (au lieu de l'auto) comme ceci:
[self.someView addObserver:self.someView forKeyPath:@"key" options:0 context:nil];
Le message d'erreur n'a pas été du tout utile pour identifier ce si juste pensé que je poste au cas où quelqu'un d'autre fait la même chose.
J'ai eu le même problème, mais dans mon cas, j'observais un contexte différent. Ensuite, je mets tous dans le même contexte et l'accident avait disparu. J'espère que cela aide quelqu'un.
Pour moi, j'oublié d'ajouter à override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
de faire quelque chose avec le KeyPath s'observe.
Voici un exemple de la version Swift. Ajouter les observateurs viewDidLoad
et de les supprimer dans 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")
}