1

The Setup

I have an NSTreeController subclass. It has a property called selectedProject which is custom and declared like this in the header:

@property (nonatomic, readonly) LPProject *selectedProject;

And like this in the private implementation:

@property (nonatomic, readwrite, assign) LPProject *selectedProject;

Critically, at EVERY point where I modify selectedProject in my TreeController, I go through the generated setter/getters. I never modify the iVar directly.

Basically, the reason this property exists is because the normal selectedObjects and selection properties of NSTreeController aren't typed and since I know this TreeController will ALWAYS manage only LPProject objects AND that only a single project can ever be selected at once, I wanted a nice clean iVar to use, rather than constantly getting the selectedObjects, checking if there is one, taking the first object in the array, etc.


LPProject

LPProject is a custom NSManagedObject subclass from Core Data. It has a property named enabled. This property is a BOOL, represented by an NSNumber. The getters/setters for it are generated by Xcode.


The Issue

In several controllers, I attempt to watch the enabled property of the selectedProject object, like this:

[_myTreeController addObserver:self 
                    forKeyPath:@"selectedProject.enabled" 
                       options:0 
                       context:NULL];

I have verified that the KVO notification arrives when selectedProject changes (and, as a result, selectedProject.enabled changes). HOWEVER, when only the enabled property of selectedProject changes (that is, the selected project itself remains the same), the KVO notification does not arrive.

HOWEVER, if I instead subscribe this way:

[_myTreeController addObserver:self 
                    forKeyPath:@"selection.enabled" 
                       options:0 
                       context:NULL];

It works perfectly. When enabled changes and selectedProject remains the same, the KVO notification arrives.

I am confused by this, because selectedProject is KVO-compliant and I feel like the keypath to .enabled should work.

Can anyone shed light on this? What have I missed?

Bryan
  • 4,628
  • 3
  • 36
  • 62

1 Answers1

1

I don't know how and when are you updating the selectedProject property, however since the selection-based approach works as expected (and not only for this), I recommend you make the selectedProject a derived property from selectedObjects (as selection is a proxy, and selectedObjects is KVO too):

// tell Cocoa which property changes directly affect selectedProject
+ (NSSet*)keyPathsForValuesAffectingSelectedProject {
    return [NSSet setWithObject:@"selectedObjects"];
}

- (LPProject*)selectedProject {
    // you might need to do some isKindOfClass checks if not all nodes are LPProject
    return self.selectedObjects.first;
}

The header property declaration stays the same, the implementation declaration is not needed if you provide the setter.

The beauty of this is that the runtime will automatically send KVO notifications for the selectedProject property when selectedObjects change, with minimum of glue code.

Cristik
  • 30,989
  • 25
  • 91
  • 127