在KVO中使用嵌套键路径

时间:2015-01-27 10:02:36

标签: ios objective-c key-value-observing

假设我想要观察一个名为' 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文档中找不到任何关于此类行为的提及,尽管其他一些人在论坛中提到了这一点。任何参考将很乐意接受。

感谢。

2 个答案:

答案 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