假设我想要观察一个名为' isEnabled
'在名为' controller'的房产上在自我。 AFAIK我有两种选择来安装这种观察:
1. [self.controller addObserver:self forKeyPath:@"isEnabled" options:0 context:nil];
2. [self addObserver:self forKeyPath:@"controller.isEnabled" options:0 context:nil];
我注意到这两种方法之间存在实际差异 - 在第二种方法中,如果“控制器”按下,我会收到通知。自我对象被替换,而第一种方法我只会在isEnabled
'在我安装观察的同一个实例上更改了属性。
我的问题是,如果有的话,这到底是什么?我知道它有效,但我应该使用它吗?
我在Apple文档中找不到任何关于此类行为的提及,尽管其他一些人在论坛中提到了这一点。任何参考将很乐意接受。
感谢。
答案 0 :(得分:3)
如果controller
属性发生变化,您不仅会收到更改通知,而且KVO会切换为跟踪isEnabled
属性的isEnabled
属性。新控制器并停止跟踪旧控制器的address.street
属性。
这隐含在关键路径的概念中。键值观察建立在键值编码之上。键值编码编程指南说明Key-Value Coding Fundamentals中的关键路径:
键路径是一串点分隔键,用于指定要遍历的对象属性序列。序列中第一个键的属性是相对于接收者的,并且每个后续键相对于前一个属性的值进行评估。
例如,密钥路径
address
将从接收对象获取street
属性的值,然后确定相对于address
对象的-addObserver:forKeyPath:options:context:
属性
isEnabled
的含义不是"遵循最终元素的关键路径,并在倒数第二个对象上观察该属性"。它观察接收器对象的这个关键路径"。始终从接收器开始考虑关键路径。
换句话说,对于您的第二个代码示例,它不是"观察controller
的{{1}}的{{1}}属性" (这是你的第一个例子的含义)。意思是"观察self
的{{1}}。只要评估表达式controller.isEnabled
的结果已经或可能已更改,请通知我。"
我知道它有效,但我应该使用它吗?
是的,你应该使用它。这是API的预期含义。这就是为什么该方法将其参数描述为键路径而不仅仅是键。
答案 1 :(得分:0)
关于第二种方法如果“控制器”,我将收到通知 自我对象被替换
我要说的是,在第二种方法中,如果控制器对象被替换或者其isEnabled
属性发生变化,您将收到通知。换句话说,当controller.isEnabled
发生变化时(正如Ken的回答所解释的那样)。
检查此示例:
- (void)viewDidLoad {
[super viewDidLoad];
self.controller = [[ViewController2 alloc] init];
[self.controller addObserver:self forKeyPath:@"isEnabled" options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:self forKeyPath:@"controller.isEnabled" options:NSKeyValueObservingOptionNew context:nil];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.controller.isEnabled = !self.controller.isEnabled;
// replace controller
[self.controller removeObserver:self forKeyPath:@"isEnabled"];
self.controller = [[ViewController2 alloc] init];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.controller.isEnabled = !self.controller.isEnabled;
});
});
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"KVO %d %@ %p", self.controller.isEnabled, keyPath, self.controller);
}
我们会收到4个KVO通知:
KVO 1 controller.isEnabled 0x7fbbc2e4b4e0 <-- These 2 fire together when we toggle isEnbled
KVO 1 isEnabled 0x7fbbc2e4b4e0 <-- So basically 1. and 2. behave the same
KVO 0 controller.isEnabled 0x7fbbc2e58d30 <---- controller was replaced
KVO 1 controller.isEnabled 0x7fbbc2e58d30 <---- Toggle on the new instance