Is it safe to implement KVO as such:
[self addObserver:self
forKeyPath:@"restaurant.oldestOrder.patron.frustrationLevel"
options:0 context:nil];
…when you know that oldestOrder
might change or become nil, and that previous oldestOrders
might become deallocated?
Executive summary:
You never need to add an observer twice for the same key just because keys in the middle changed, NSKeyValueObserving
does this for you.
As long as your -oldestOrder
method doesn't return a dangling pointer, nothing should break.
The way KVO works is when you add yourself as an observer, the observer is added to a list on the observed object, in the form of an NSObservationInfo
object. Easy. An NSObservationInfo
object is just a raw key (not a path), an object to watch, an observer to call, a pointer to the context you passed, and some other bookkeeping stuff.
When you observe a path of keys, the observer relationships are observed through respective NSObservationInfo
objects, which are held in the list on the called object, as if the whole path was a property of the object you called -addObserver
on.
In your example self
observes restaurant
, but the addObserver:
method then creates NSObservationInfo
objects in the background that are added as observers for each respective object down the path, and these are also added to your self's list of observers.
When you observe @"A.B.C.D"
of an object, it creates four NSObservationInfo
objects, one for each key relation, and these are added to each key on the path as an observer, so you get an NSObservationInfo
object watching A
of the called, one watching B
of A
, one watching C
of B
, etc. When one of these NSObservationInfo objects observes a change, it passes this along to the original -addObserver
caller as a change to the entire keypath.
(This is an implementation detail but it should help the explanation.)
When an object in the middle of the path is nil'd or removed, -willChangeValueForKey
notices this and dutifully removes the NSObservationInfo
objects as observers, along the path after the nil'd object. These NSObservationInfo
objects still exist though, and they still know what keys they want to see, and when -didChangeValueForKey
is called, it will look at the new value for the key and if it has key names matching the ones that it's chain of NSObservationInfo
objects is looking for, it will attach them as the new observers.
So, when any element along the path @"restaurant.oldestOrder.patron.frustrationLevel"
changes, you will get a change notification, but the NSKeyValueObserving
machinery will attempt to reestablish a relationship with the key at the end of that path after the change, and any subsequent change.
Here's Cocoatron's implementation of this, you can see how it works yourself.