KVO: +keyPathsForValuesAffecting<Key> doesn't work with (subclass of) NSObjectController

为君一笑 提交于 2019-12-07 05:28:35

问题


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.)


回答1:


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.



来源:https://stackoverflow.com/questions/15966158/kvo-keypathsforvaluesaffectingkey-doesnt-work-with-subclass-of-nsobjectco

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!