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 ...

Original source

Related problems