Key Value Observing in Cocoa, introspecting the change property

12,196

Solution 1

Firstly, you specify NSKeyValueObservingOptionNew:

[theObject addObserver: self
            forKeyPath: @"theKey"
               options: NSKeyValueObservingOptionNew
               context: NULL];

…then, in your observer method:

-(void) observeValueForKeyPath: (NSString *)keyPath ofObject: (id) object
                        change: (NSDictionary *) change context: (void *) context
{
    BOOL newValue = [[change objectForKey: NSKeyValueChangeNewKey] boolValue];
}

Ideally you'd check whether value was nil (well, it might happen) before calling -boolValue, but that was omitted for clarity here.

Solution 2

As Jim Dovey says, except that the change dictionary does not bring nil, but null values, so that

NSLog(@"%@", [change description]); 

will result in something like:

{
    kind = 1;
    new = <null>;
    old = <null>;
}

As mentioned, calling boolValue on a null value will result in an error

[NSNull boolValue]: unrecognized selector sent to instance 0xa0147020

To avoid this, one has to check not for nil but for [NSNull null], like so:

if([change objectForKey:NSKeyValueChangeNewKey] != [NSNull null]) 
  BOOL newValue = [[change objectForKey: NSKeyValueChangeNewKey] boolValue];

or

id newValue;
if((newValue[change valueForKey: @"new"]) != [NSNull null]){
     BOOL newBOOL = [newValue boolValue];
}
Share:
12,196
Roman
Author by

Roman

Updated on June 05, 2022

Comments

  • Roman
    Roman about 2 years

    I'm using key value observing on a boolean property an NSObject method:

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

    The most interesting part of the value for this key path is a BOOL which is constantly flipping between YES/NO. The most I get out of the change dictionary is kind = 1. Is there anyway without probing the object I'm observing to see what the actual change value is?

    Thanks.

  • angelokh
    angelokh almost 12 years
    I searched and found out this post. I have same issue that new=null returned. stackoverflow.com/q/11835607/772481 Is this fixed?
  • Elise van Looij
    Elise van Looij almost 12 years
    Doubt that this will ever get fixed. In your question you run into a problem observing a relationship, which complicates things. As I quoted there from the docs "If the observed property is a to-many relationship, the NSKeyValueChangeKindKey entry also indicates whether objects in the relationship were inserted, removed, or replaced by returning NSKeyValueChangeInsertion, NSKeyValueChangeRemoval, or NSKeyValueChangeReplacement, respectively."
  • Adam
    Adam over 11 years
    How could you get "nil"? It could happen for anything of NSObject type, but I don't see how that could happen for a BOOL?
  • Jim Dovey
    Jim Dovey over 11 years
    I mean you would check whether the result of [change objectForKey: NSKeyValueChangeNewKey] was nil, because there's a difference between 'no result' and 'a result of NO'.
  • pulkitsinghal
    pulkitsinghal over 11 years
    @JimDovey - when I inspect the dictionary on the debug console for a non-BOOL property, I wonder if a string literal check isn't required as well new = "<null>"
  • pulkitsinghal
    pulkitsinghal over 11 years
    Actually I found out that when a method returns an NSNull object, the -description on that object returns the string <null> so my earlier introspection based comment to check for a string literal is unmerited. I now check for nil like so: if ([change objectForKey:NSKeyValueChangeNewKey] == nil || [change objectForKey:NSKeyValueChangeNewKey] == (id)[NSNull null]) {...}