KVO: + keyPathsForValuesAffecting <Key> does not work with (subclass) NSObjectController
I have a class capable of KVO (call it Observee ), the affectedValue property is affectedValue by the affectingValue property. The relationship between properties is determined using the +keyPathsForValuesAffectingAffectedValue method.
Setting the affectingValue value notifies that the affectedValue has changed as I expected if Ovservee not a subclass of NSObjectController . Full example:
@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 is output like the following when Observee outputs an NSObject :
keyPathsForValuesAffectingAffectedValue called setting value to affectingValue affected key path = affectedValue but when Observee outputs an NSObjectController :
keyPathsForValuesAffectingAffectedValue called setting value to affectingValue (note that the "affected key path = affectedValue" is missing.)
It seems that keyPathsForValuesAffectingAffectedValue is called in both cases, but there is no-op in it.
In addition, any key paths associated with an instance (subclass) of NSObjectController will not 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 to declare a dependency between key paths in such cases? And why is all this happening?
(Yes, I know about will/didChangeValueForKey: and friends, but wrapping each affecting key path with an (nother) installer is horrible, and I would like to avoid it.)
NSController , and its subclasses, are full of KVO "black magic" and unexpected behavior. (For another example, they do not respect certain KVO options, such as NSKeyValueObservingOptionPrior ). You will be disappointed if you expect them to behave as βnormalβ objects with respect to KVO. They exist mainly to support Cocoa bindings. Although at first glance, bindings may look like simple syntactic sugar on top of KVOs, you can see (overriding the KVO support methods of the linked object and setting breakpoints in them) that actually happens quite a bit under the covers than just observing the KVO.
Download file errors from Apple to increase the likelihood that they will fix (or at least document) these kinds of problems / behavior.