What is the context parameter used for in Key value observing
cocoa, cocoa-touch, key-value-observing, objective-c
Solution
I hope this explanation isn't too abstract to understand.
Suppose you create a class `MyViewController`, which is a subclass of `UIViewController`. You don't have the source code of `UIViewController`.
Now you decide to make `MyViewController` use KVO to observe changes to the `center` property of `self.view`. So you duly add yourself as an observer:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.view addObserver:self forKeyPath:@"center" options:0 context:NULL];
}
- (void)viewDidDisappear:(BOOL)animated {
[self.view removeObserver:self forKeyPath:@"center"];
[super viewDidDisappear:animated];
}
The problem here is that you don't know if `UIViewController` also registers itself as an observer of `self.view`'s `center`. If it does, then you might have two problems:
- You might be called twice when the view's center changes.
- When you remove yourself as an observer, you might also remove `UIViewController`'s KVO registration.
You need a way to register yourself as an observer that is distinguishable from `UIViewController`'s KVO registration. That's where the `context` argument comes in. You need to pass a value for `context` that you are absolutely sure `UIViewController` is not using as the `context` argument. When you unregister, you use the same `context` again so that you only remove your registration, not `UIViewController`'s registration. And in your `observeValueForKeyPath:ofObject:change:context:` method, you need to check the `context` to see if the message is for you, or for your superclass.
One way to be sure you use a `context` that nothing else uses is to create a `static` variable in `MyViewController.m`. Use it when you register and unregister, like this:
static int kCenterContext;
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.view addObserver:self forKeyPath:@"center" options:0 context:&kCenterContext];
}
- (void)viewDidDisappear:(BOOL)animated {
[self.view removeObserver:self forKeyPath:@"center" context:&kCenterContext];
[super viewDidDisappear:animated];
}
Then in your `observeValueForKeyPath:...` method, check it like this:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if (context == &kCenterContext) {
// This message is for me. Handle it.
[self viewCenterDidChange];
// Do not pass it on to super!
} else {
// This message is not for me; pass it on to super.
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
}
}
Now you're guaranteed not to interfere with your superclass's KVO, if it does any. And if somebody makes a subclass of `MyViewController` that also uses KVO, it won't interfere with your KVO.
Note too that you can use a different context for each key path you observe. Then, when the system notifies you of a change, you can check the context instead of checking the key path. Testing for pointer equality is a little faster than checking for string equality. Example:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if (context == &kCenterContext) {
[self viewCenterDidChange];
// Do not pass it on to super!
} else if (context == &kBackgroundColorContext) {
[self viewBackgroundDidChange];
// Do not pass it on to super!
} else if (context == &kAlphaContext) {
[self viewAlphaDidChange];
// Do not pass it on to super!
} else {
// This message is not for me; pass it on to super.
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
}
}
Problem
What's the use of context parameter in following method which is used to register for key value notifications. The documentations just denotes it as arbitrary set of data. ``` addObserver:self forKeyPath:@"selectedIndex" options:NSKeyValueObservingOptionNew context:nil ``` Can somebody shed some light what's the purpose behind it ...