I have a KVO-able class (call it Observee
), which affectedValue
dynamic property is affected by affectingValue
property. The dependency between the properties is defined by implementing +keyPathsForValuesAffectingAffectedValue
method.
Setting a value to affectingValue
notifies that affectedValue
has changed as I expected, unless Ovservee
is a subclass of NSObjectController
. Full example follows:
@interface Observee : NSObject // or NSObjectController
@property (readonly, strong, nonatomic) id affectedValue;
@property (strong, nonatomic) id affectingValue;
@property (strong, nonatomic) NSArrayController *arrayController;
@end
@implementation Observee
@dynamic affectedValue;
- (id)affectedValue { return nil; }
+ (NSSet *)keyPathsForValuesAffectingAffectedValue {
NSLog(@"keyPathsForValuesAffectingAffectedValue called");
return [NSSet setWithObject:@"affectingValue"];
}
@end
@interface AppDelegate : NSObject <NSApplicationDelegate>
@property (strong, nonatomic) Observee *observee;
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
self.observee = [[Observee alloc] init];
[self.observee addObserver:self
forKeyPath:@"affectedValue"
options:NSKeyValueObservingOptionNew
context:NULL];
NSLog(@"setting value to affectingValue");
self.observee.affectingValue = @42;
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
NSLog(@"affected key path = %@", keyPath);
}
@end
The example works fine and outputs as the following when Observee
derives NSObject
:
keyPathsForValuesAffectingAffectedValue called
setting value to affectingValue
affected key path = affectedValue
but when Observee
derives NSObjectController
:
keyPathsForValuesAffectingAffectedValue called
setting value to affectingValue
(note that "affected key path = affectedValue" is absent.)
It seems that keyPathsForValuesAffectingAffectedValue
is called in both cases but it is no-op in the latter.
Also, any key paths involving an instance of (subclass of) NSObjectController
won't affect other key paths, such as:
@implementation SomeObject
// `someValue` won't be affected by `key.path.(snip).arrangedObjects`
+ (NSSet *)keyPathsForValuesAffectingSomeValue {
return [NSSet setWithObject:@"key.path.involving.anNSArrayController.arrangedObjects"];
}
@end
How do I declare dependency between key paths in such cases? And, why is this whole thing happening?
(Yes, I know about will/didChangeValueForKey:
and friends, but wrapping up every affecting key path with a(nother) setter is terrible and I'd like to avoid it.)
NSController
and its subclasses are full of KVO "black magic" and unexpected behaviors. (For another example, they don't respect certain KVO options like NSKeyValueObservingOptionPrior
) You will be disappointed if you expect them to behave like "normal" objects with respect to KVO. They exist primarily to support Cocoa bindings. Although at first glance bindings may look like mere syntactic sugar on top of KVO, you can see (by overriding the KVO supporting methods of a bound-to object and setting breakpoints in them) that there's actually quite a bit more going on underneath the covers than a simple KVO observation.
Please file bugs with Apple to increase the likelihood that they'll fix (or at least document) these sorts of issues/behavior.
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