objective-ccocoa-bindingskey-value-observingnsarraycontrollerkey-value-coding

KVO: Cannot remove an observer


In my interface I have an NSTextField who's value is bound to an NSArrayController's selection.selectedType.title. All of the NSArrayController's objects are custom objects each with two methods:

- (MYType *)selectedType;
- (void)setSelectedType: (MYType *)type;

The don't have an iVar selectedType. However, they do have an iVar holding all MYType objects. The code boils down to this:

- (MYType *)selectedType
{
   if (someIndex == 0)
        return [types objectAtIndex: 0];
    else
        return [self typeWithIndex: someIndex];
}

- (void)setSelectedType: (MYType *)type
{
     someIndex = [type index];  
}

MYType objects got a NSString *title iVar with a corresponding @property and synthesize.

Whenever I call setSelectedType:, the changes are immediately visible in the NSTextField and everything seems to work but I get a log message saying:

Cannot remove an observer NSArrayController 0x141160 for the key path "selectedType.title" from MYType 0x1a4830, most likely because the value for the key "selectedType" has changed without an appropriate KVO notification being sent. Check the KVO-compliance of the MYType class.

I tried encapsulating the setSelectedType: method with willChangeValueForKey: and didChangeValueForKey: and then I still got a log message but a different one:

Cannot remove an observer NSKeyValueObservance 0x1c7570 for the key path "title" from MYType 0x1a4be0 because it is not registered as an observer.


Solution

  • First, use accessors. Don't access your ivars directly. You're bypassing the KVO for someIndex because you modify the ivar directly. Don't touch ivars directly unless you have to.

    You also need to let KVO know that selectedType depends on someIndex (and/or someStuff; it's not clear from your distilled code).

    + (NSSet *)keyPathsForValuesAffectingSelectedType
    {
      return [NSSet setWithObjects:@"someIndex", nil];
    }
    

    This tells the KVO system that whenever someIndex changes, it causes an implicit change in selectedType. See Registering Dependent Keys.