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?

+2


source to share


4 answers


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 .

+6


source


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.

+5


source


You have to implement this and it will be called when the path to the key changes:

 (void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary *)change
                      context:(void *)context;

      

More details here .

+4


source


(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;
}

      

+3


source







All Articles