Help in observing key values
I need a little help with KVO, I'm about halfway there. What I am trying to do is call a method when something in the tree controller changes.
Therefore, I use this code to register as a KVO.
[theObject addObserver: self
forKeyPath: @"myKeyPath"
options: NSKeyValueObservingOptionNew
context: NULL];
But how can I call the method when the Observable Key Path changes?
One additional question, when I add myself as an observer, I want the key-path to be a property in my underlying data model, did I do it right?
source to share
Override observeValueForKeyPath:ofObject:change:context:
to dispatch the method you want to call.
@interface Foo : NSObject {
NSDictionary *dispatch;
...
}
@end
@implementation Foo
-(id)init {
if (self = [super init]) {
dispatch = [[NSDictionary dictionaryWithObjectsAndKeys:NSStringFromSelector(@selector(somethingHappenedTo:with:)),@"myKeyPath",...,nil] retain];
...
}
}
...
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
SEL msg = NSSelectorFromString([dispatch objectForKey:keyPath]);
if (msg) {
[self performSelector:msg withObject:object withObject:keyPath];
}
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
...
See Receiving Change Notification for details .
source to share
I would recommend you take a look at the Google Toolbox for Mac category GTMNSObject + KeyValueObserving.h or at least Michael Ash's blog post that inspired it. Basically, manual KVO management is very subtle and the pattern suggested by the API is not perfect. Much better putting another layer in the API (like GTMNSObject + KeyValueObserving) makes it more API-like NSNotification
and hides some of the sources of subtle bugs.
Using GTMNSObject + KeyValueObserving you would do
[theObject gtm_addObserver:self
forKeyPath:@"myKeyPath"
selector:@selector(myCallbackSelector:)
userInfo:nil
options:NSKeyValueObservingOptionNew];
and yours -myCallbackSelector:
will be called when the value in @"myKeyPath"
changes with a type argument GTMKeyValueChangeNotification
that encapsulates all the information you need.
This way, you don't need to have a big mailing table in observeValueForKeyPath:ofObject:change:context
(actually the category is supported for you), or you need to worry about the correct way to use the pointer context
to avoid conflict with super / sub classes, etc.
source to share
(this is the technique I learned here: http://www.bit-101.com/blog/?p=1999 )
You can pass the method in a "context" like
[theObject addObserver:self
forKeyPath:@"myKeyPath"
options:NSKeyValueObservingOptionNew
context:@selector(doSomething)];
.. then in the observValueForKeyPath method, you pass it to the SEL SEL type and then execute it.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
SEL selector = (SEL)context;
[self performSelector:selector];
}
If you want to pass data to the doSomething method, you can use the "new" key in the "change" dictionary, for example:
[theObject addObserver:self
forKeyPath:@"myKeyPath"
options:NSKeyValueObservingOptionNew
context:@selector(doSomething:)]; // note the colon
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
SEL selector = (SEL)context;
// send the new value of observed keyPath to the method
[self performSelector:selector withObject:[change valueForKey:@"new"]];
}
-(void)doSomething:(NSString *)newString // assuming it a string
{
label.text = newString;
}
source to share