Key Value Observing (KVO) is the sister API to KVC. KVO allows us to request notifications when an attribute has changed. By observing attributes on an object, we can react when those attributes are changed. KVO is also implemented via an informal protocol on the NSObject, and we register and remove observers using ‑addObserver:for-KeyPath:options:context: and ‑removeObserver:forKeyPath:. These are the two primary methods, although there are other methods involved in the protocol, just as with KVC.
If we wanted to observe the name value on a recipe, we’d add ourselves (or another object) as an observer for that value, like so:
| static NSString *kPragProgObserver = @"PragProgObserver" |
| id myRecipe = ... |
| [myRecipe addObserver:self |
| forKeyPath:@"name" |
| options:NSKeyValueObservingOptionNew |
| context:kPragProgObserver]; |
This snippet of code adds self as an observer to the myRecipe object and requests that when the name value changes, to notify self of that change and include both the old value and the new value in that notification. We pass along a context so we can ensure we’re acting on observations meant only for us and that they aren’t accidentally intercepted.
We do this because it’s possible that our code isn’t the only code in our application observing a value, and this method may be called with the intention of being received by our superclass. To ensure that the notification we receive is in fact intended for us, we check the context that’s passed in. After this code has been called, any time the name property is changed on that instance of Recipe, the ‑observeValueForKeyPath:ofObject:change:context: is called on self. We can then handle the change notification as appropriate.
| - (void)observeValueForKeyPath:(NSString*)keyPath |
| ofObject:(id)object |
| change:(NSDictionary*)change |
| context:(void*)context |
| { |
| if (context != kPragProgObserver) { |
| [super observeValueForKeyPath:keyPath |
| ofObject:object |
| change:change |
| context:context]; |
| return; |
| } |
| |
| NSLog(@"Attribute %@ changed from %@ to %@", keyPath, |
| [change valueForKey:NSKeyValueChangeOldKey], |
| [change valueForKey:NSKeyValueChangeNewKey]); |
| } |
When the variable is changed, we see output similar to the following:
| Attribute name changed from untitled to Beef Chili |
When we’re done observing a value, we can stop receiving messages by passing ‑removeObserver:forKeyPath: to the observed object.
| id myRecipe = ... |
| [myRecipe removeObserver:self |
| forKeyPath:@"name"]; |
KVO allows views to automatically refresh themselves from the model when the data has changed. When a view is initialized, it uses KVO to connect all its components to the underlying objects and then uses the notifications to refresh itself.