Search code examples
objective-ccore-datakey-value-observing

incomprehensible bug when adding an observer using NSKeyValueObservingOptionInitial


I have a UI object (a subclass of NSObject called Label) that observes a property called region (subclass of NSManagedObject called Region) for three core data attributes:

  • start (float)
  • end (float)
  • name (NSString)

Label.m, contains:

-(void) setRegion:(Region *)region {
   _region = region;
   [region addObserver:self forKeyPath:@"start"
                    options:NSKeyValueObservingOptionNew
                    context:nil];

   [region addObserver:self forKeyPath:@"end"
                    options:NSKeyValueObservingOptionNew
                    context:nil];

   [region addObserver:self forKeyPath:@"name"
                        options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial
                        context:nil];  // problematic
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
   NSLog(@"%@", keyPath);
}

(I also remove observation as required, in another method)

All this seems pretty standard. The problem is that when the instruction that is commented with //problematic is executed, observeValueForKeyPath: is called 3 times, one for each observed key (start, end, name), even though the problematic instruction only observes the name key (and should send the notification only once for this key).

Using a specific pointer for the context of each observation does not change this.

This issue doesn't not occur if the problematic instruction is placed at the start of the method, nor when NSKeyValueObservingOptionInitial is added as an option for the observation of start and end.

Of note: Region is a subclass of an abstract class that defines the start, end and name attributes, and the issue does not occur if the observed object is from another concrete subclass, even though neither subclass do anything special with these attributes (they use default accessors, they don't implement custom setters or whatnot).


Solution

  • Ok I found the root cause. The problem what that the observed object was a fault.

    The observation instruction using NSKeyValueObservingOptionInitial causes the fault to fire, which results in changes of the other (observed) attributes (fetched from the store), triggering change notifications.

    Move along, nothing to see here.