Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the context parameter used for in Key value observing

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

like image 618
rustylepord Avatar asked Aug 11 '12 18:08

rustylepord


People also ask

What is key-value observing?

Key-value observing is a Cocoa programming pattern you use to notify objects about changes to properties of other objects. It's useful for communicating changes between logically separated parts of your app—such as between models and views. You can only use key-value observing with classes that inherit from NSObject .

What is key-value Coding and key-value observing?

Using a mechanism called key-value coding (KVC), you can manipulate object properties indirectly. With KVC comes the ability to observe changes to a particular key value, which is known as key-value observing (KVO).

What framework is KVO key-value observing a part of?

Key-Value Observing, KVO for short, is an important concept of the Cocoa API. It allows objects to be notified when the state of another object changes.

What is KVO and KVC in IOS Swift?

KVO and KVC or Key-Value Observing and Key-Value Coding are mechanisms originally built and provided by Objective-C that allows us to locate and interact with the underlying properties of a class that inherits NSObject at runtime.


1 Answers

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:

  1. You might be called twice when the view's center changes.
  2. 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];     } } 
like image 110
rob mayoff Avatar answered Oct 13 '22 15:10

rob mayoff