NSInternalInconsistencyException when using KVO
I'm trying to use a KVO example I got from an iPhone tutorial book, but get this exception
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<UINavigationController: 0x139630>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: dataUpdated
Observed object: <FilterDetailsController: 0x1b9930>
Change: {
kind = 1;
}
Context: 0x0'
Call stack at first throw:
(
0 CoreFoundation 0x320d3987 __exceptionPreprocess + 114
1 libobjc.A.dylib 0x3271849d objc_exception_throw + 24
2 CoreFoundation 0x320d37c9 +[NSException raise:format:arguments:] + 68
3 CoreFoundation 0x320d3803 +[NSException raise:format:] + 34
4 Foundation 0x35c316e9 -[NSObject(NSKeyValueObserving) observeValueForKeyPath:ofObject:change:context:] + 60
5 Foundation 0x35bd4a3d NSKeyValueNotifyObserver + 216
6 Foundation 0x35bd46e5 NSKeyValueDidChange + 236
7 Foundation 0x35bcc3f5 -[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:] + 76
8 Foundation 0x35c30d87 _NSSetObjectValueAndNotify + 98
9 Lucid Dreaming App 0x000108e3 -[FilterDetailsController adjustFilterDetails:] + 138
10 CoreFoundation 0x3207afed -[NSObject(NSObject) performSelector:withObject:withObject:] + 24
11 UIKit 0x323b3ea5 -[UIApplication sendAction:to:from:forEvent:] + 84
12 UIKit 0x323b3e45 -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 32
13 UIKit 0x323b3e17 -[UIControl sendAction:to:forEvent:] + 38
14 UIKit 0x323b3b69 -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 356
15 UIKit 0x323b43c7 -[UIControl touchesEnded:withEvent:] + 342
16 UIKit 0x323a9d4d -[UIWindow _sendTouchesForEvent:] + 368
17 UIKit 0x323a96c7 -[UIWindow sendEvent:] + 262
18 UIKit 0x3239499f -[UIApplication sendEvent:] + 298
19 UIKit 0x323942df _UIApplicationHandleEvent + 5090
20 GraphicsServices 0x35472f03 PurpleEventCallback + 666
21 CoreFoundation 0x320686ff __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 26
22 CoreFoundation 0x320686c3 __CFRunLoopDoSource1 + 166
23 CoreFoundation 0x3205af7d __CFRunLoopRun + 520
24 CoreFoundation 0x3205ac87 CFRunLoopRunSpecific + 230
25 CoreFoundation 0x3205ab8f CFRunLoopRunInMode + 58
26 GraphicsServices 0x354724ab GSEventRunModal + 114
27 GraphicsServices 0x35472557 GSEventRun + 62
28 UIKit 0x323c7d21 -[UIApplication _run] + 412
29 UIKit ...
I start by
[advancedController addObserver:self.navigationController forKeyPath:@"dataUpdated" options:0 context:nil];
within self.navigation controller, I have the observation method defined:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"2 Observed change for: %@",keyPath);
// if (context == <#context#>) {
// <#code to be executed upon observing keypath#>
// } else {
[super observeValueForKeyP开发者_如何学运维ath:keyPath ofObject:object change:change context:context];
// }
}
within advancedController I have a NSNumber property
@property(nonatomic,retain)NSNumber* dataUpdated;
When I hit a button, I expect the observer to fire.
- (IBAction)adjustFilterDetails:(id)sender {
self.dataUpdated = [NSNumber numberWithInt:[self.dataUpdated intValue]+1];
}
Do I need to implement some protocol or explicitly state that I will update the value? I read that NSKeyValueObserving is an "informal" protocol. Thank you for your help!
I have figured out the answer to my own question. I had the KVO protocol method defined within a ViewController, however, I have accidentally registered the Navigation controller for the View controller to observe the value. The correct object is found by getting the view controller like this:
[advancedController addObserver:(RootViewController*)self.navigationController. topViewController forKeyPath:@"dataUpdated" options:0 context:nil];
Your problem is the call to super
. You shouldn't pass this method to super
for properties that you observed yourself. You commented out the code that was there to help you with this.
A possible correct observation would be (note the context):
[advancedController addObserver:self.navigationController forKeyPath:@"dataUpdated" options:0 context:self];
And then the correct observer would be:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == self) {
NSLog(@"2 Observed change for: %@",keyPath);
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
Dave Dribin provides another approach to using context
correctly, along with the backstory of why it's necessary, in Proper Key-Value Observer Usage.
精彩评论