KVO: +keyPathsForValuesAffecting<Key>无法与(NSObjectController的子类)一起使用

5
我有一个可进行KVO的类(称之为Observee),其affectedValue动态属性受到affectingValue属性的影响。属性之间的依赖关系通过实现+keyPathsForValuesAffectingAffectedValue方法来定义。
设置affectingValue的值会通知affectedValue已经被更改,就像我所期望的那样,除非ObserveeNSObjectController的子类。以下是完整的示例:
@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

这个示例在 Observee 派生自 NSObject 时可以正常运行,并输出以下内容:
keyPathsForValuesAffectingAffectedValue called
setting value to affectingValue
affected key path = affectedValue

ObserveeNSObjectController继承时:

keyPathsForValuesAffectingAffectedValue called
setting value to affectingValue

(请注意,“受影响的关键路径 = 受影响的值”不在其中。)
似乎 keyPathsForValuesAffectingAffectedValue 在两种情况下都被调用,但后者是无操作的。
此外,涉及 (NSObjectController 的子类的) 实例的任何关键路径都不会影响其他关键路径,例如:
@implementation SomeObject

// `someValue` won't be affected by `key.path.(snip).arrangedObjects`
+ (NSSet *)keyPathsForValuesAffectingSomeValue {
  return [NSSet setWithObject:@"key.path.involving.anNSArrayController.arrangedObjects"];
}

@end

我该如何在这种情况下声明关键路径之间的依赖关系?为什么会发生这样的事情?
(是的,我知道有will/didChangeValueForKey:和相关的内容,但是用另一个setter来包装每个影响到的关键路径很糟糕,我想避免它。)

如果您找到原因,请分享;)我现在观察到使用从“Core Data”实体生成的类的KVO的奇怪行为。 - Colas
你尝试过使用+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key吗? - Colas
+keyPathsForValuesAffectingValueForKey: 的默认实现只是搜索 +keyPathsForValuesAffecting<Key> 并返回其结果,覆盖它在这里没有任何区别。 - uasi
1个回答

6
NSController及其子类充斥着KVO的"黑魔法"和意想不到的行为。例如,它们不遵循某些KVO选项,比如NSKeyValueObservingOptionPrior。如果您期望它们在KVO方面像“普通”对象一样行为,那么您将感到失望。它们主要存在是为了支持Cocoa绑定。虽然乍一看绑定可能只是在KVO之上的纯句法糖果,但是(通过重写绑定对象的支持KVO方法并在其中设置断点)您会发现底层有更多的工作正在进行。

向苹果报告问题,以增加他们修复(或至少记录)这些问题/行为的可能性。


叹气。这肯定可以解释为什么我无法使依赖于我的NSArrayController属性的selectionIndexes(或selection)属性起作用..非常烦人 - 感谢您提供的信息! - Jay
@ipmcc:你知道缺少对NSKeyValueObservingOptionPrior(以及New和Old选项)的支持是否有文档记录吗?因为有趣的是,苹果文档中声明“此对象完全符合键值编码规范”。 - Mojo66
@Mojo66 这个像 Cocoa Bindings 中许多其他部分一样,似乎没有文档记录。不过相信我…… ;) - ipmcc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接