私はKVOに比較的慣れていないので、いくつかの基本的なルールに違反している可能性があります。 CoreDataを使用しています。
私のアプリは次のメッセージでクラッシュします:そして私が理解できないのは、CGImageが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”.
これをトリガーするために起こっているのは次のとおりです。
[[self measurementPointer] setMeasurementDescriptor:descriptor];
これを考えると、
[[meterDisplay measurementPointer] addObserver:self
forKeyPath:@"measurementDescriptor"
options:NSKeyValueObservingOptionNew
context:nil];
基本的に、MeasurementPointerオブジェクトはMeasurementDescriptorオブジェクトを指し、どちらもNSManagedObjectサブクラスです。 MeasurementDescriptorオブジェクトは、特定の「測定」と「単位」の組み合わせを記述します(たとえば、「温度(°C)」または「風速(mph)」)。 MeasurementDescriptorは、一意の測定単位の組み合わせごとに1つしかないという点で、シングルトンのようなものです。
MeasurementPointerは、他のオブジェクト(ModelオブジェクトとControllerオブジェクトの両方)によって参照されます。 MeasurementPointerはMeasurementDescriptorを参照します。多くのオブジェクトは、MeasurementPointerが新しい/異なるMeasurementDescriptorの参照を開始するタイミングを知ることに関心があります。このような変更により、たとえば、グラフ表示の軸が変更される可能性があります。または、上記のコードで、メーターディスプレイに(選択したサンプルのセットからの)異なるサンプルが表示される場合があります。
根本的な問題は、CGImageが意図しないメッセージを受信していることだと思います...残念ながら、これは断続的であるため、それをトリガーするパターンを見つけることができませんでした。
割り当てが解除され、別のオブジェクトの監視を停止しなかったオブジェクトがあります。すべての-addObserver...
呼び出しをウォークスルーし、少なくとも-removeObserver...
で、場合によっては-dealloc
で、アプリケーション構造に応じて-viewDidUnload
呼び出しと一致することを確認します。
変更のオブザーバーとして登録していなかったobserveValueForKeyPath
メソッドをsuper
に送信したときに、このエラーが発生しました。 Apple docs say "スーパークラスの実装[observeValueForKeyPath
]を実装している場合は、必ず呼び出してください。 「」
私の修正は変更することでした:
- (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]) {
...
}
}
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) {
}
}
私は次のように(自己ではなく)オブザーバーとしてターゲットを誤って渡してこの問題に遭遇しました:
[self.someView addObserver:self.someView forKeyPath:@"key" options:0 context:nil];
エラーメッセージはこれを特定するのにまったく役に立たなかったので、他の誰かが同じことをした場合に備えて投稿したいと思いました。
私にとっては、override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
を追加して、keyPathが監視されることで実際に何かを行うのを忘れていました。
Swiftバージョンの例を次に示します。オブザーバーをviewDidLoad
に追加し、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")
}
私も同じ問題を抱えていましたが、私の場合は別の状況を観察していました。次に、すべてを同じコンテキストに置くと、クラッシュはなくなりました。これが誰かに役立つことを願っています。