Search code examples
iosobjective-ckey-value-observing

Using nested key paths in KVO


Suppose i want to observe a property named 'isEnabled' on a property named 'controller' on self. AFAIK i have two options for installing such kind of observation:

1. [self.controller addObserver:self forKeyPath:@"isEnabled" options:0 context:nil];
2. [self addObserver:self forKeyPath:@"controller.isEnabled" options:0 context:nil];

I noticed the practical difference between the two approaches - on the second approach i will get a notification if the 'controller' object on self was replaced while with the first approach I will be notified only when 'isEnabled' property is changed on the same instance on which I installed the observation.

My question is where the hell is this documented if at all? I know it works but should I use it?
I could not find any mention of such behavior in Apple docs, though some other dudes mentioned it in forums. Any reference will be gladly accepted.

Thanks.


Solution

  • It's not just that you'll get a change notification if the controller property changes, it's that KVO will switch to tracking the isEnabled property of the new controller and stop tracking the isEnabled property of the old controller.

    This is implicit in the notion of a key path. Key-Value Observing is built on top of Key-Value Coding. The Key-Value Coding Programming Guide says this about key paths in Key-Value Coding Fundamentals:

    A key path is a string of dot separated keys that is used to specify a sequence of object properties to traverse. The property of the first key in the sequence is relative to the receiver, and each subsequent key is evaluated relative to the value of the previous property.

    For example, the key path address.street would get the value of the address property from the receiving object, and then determine the street property relative to the address object.

    The meaning of -addObserver:forKeyPath:options:context: is not "follow the key path to the final element and observe that property on the second-to-last object". It's "observe this key path of the receiver object". The key path is always considered starting from the receiver.

    Put another way, for your second code example, it's not "observe the isEnabled property of the controller of self" (that's the meaning of your first example). The meaning is "observe the controller.isEnabled of self. Any time that the result of evaluating the expression [self valueForKeyPath:@"controller.isEnabled"] has or might have changed, notify me."

    I know it works but should I use it?

    Yes, you should use it. It's the intended meaning of the API. It's why the method describes its parameter as a key path rather than just a key.