0
votes

I have a NSViewController subclass with a convenience method to return the window controller like this

@interface ILViewController : NSViewController

- (ILWindowController *)windowController;

@end

@implementation ILViewController

- (NSWindowController *)windowController {
    return self.view.window.windowController;
}

- (void)setView:(NSView *)view {
    [self willChangeValueForKey:@"windowController"];
    [super setView:view];
    [self didChangeValueForKey:@"windowController"];
}

@end

The window controller has a NSString * property named mainStatus which I'm trying to bind to through the view controller. However, I am getting this error at run time.

Cannot update for observer <NSAutounbinderObservance 0x1005cf990> for the key path 
"windowController.mainStatus" from <ILViewController 0x1001976b0>, most likely because
the value for the key "windowController" has changed without an appropriate KVO
notification being sent. Check the KVO-compliance of the ILViewController class.

Why is this happening? I have appropriately sent the KVO notifications.

The following code, which uses an ivar and is actually NOT KVO compliant turns out to NOT result in any error....

@interface ILViewController : NSViewController {
    ILWindowController *_windowController;
}

- (ILWindowController *)windowController;

@end

@implementation ILViewController

- (NSWindowController *)windowController {
    return _windowController;
}

- (void)setView:(NSView *)view {
    [super setView:view];
    _windowController = view.window.windowController;
}

@end

This is confusing the heck out of me... Can someone see why the first implementation is not KVO compliant?

EDIT

Got it, if the view is added to window after it is set for the ViewController then the view's window is indeed changed without appropriate KVO notification. However, when I try to observe the view's window's window controller, I'm getting errors like this

KVO autonotifying only supports -set<Key>: methods that return void. Autonotifying 
will not be done for invocations of -[NSView _setWindow:].
Cannot remove an observer <NSKeyValueObservance 0x102e17880> for the key path
"window.windowController.mainStatus" from <NSView 0x102e13ec0>, most likely because
the value for the key "window" has changed without an appropriate KVO notification
 being sent. Check the KVO-compliance of the NSView class.

So if you can't observe a view's window, surely there's gotta be some way to be notified when the view's window changes. Does anyone know how?

Also, these still doesn't explain why the second version of the code that uses an iVar without KVO notification actually work.

2
You don't need to send KVO notifications within a KVC-compliant setter. KVO will wrap any such setters and send notifications for you unless you explicitly tell it not to.Peter Hosey
I'm sending KVO notification for a different attribute that the one being set affects.Tony
You still don't need to send KVO notifications yourself. Instead, implement keyPathsForValuesAffectingWindowController, and return a set whose only object is @"view".Peter Hosey
yea, I tried that as well. In either case it still doesn't solve the problem of observing a view's window thoughTony
There's no reason why it would. What it does do is save you from having to implement -[ILViewController setView:] yourself.Peter Hosey

2 Answers

4
votes

If you set the view, and then add the view to a window, then windowController will have changed without KVO notifications being sent out. You need to observer your own view's window and send out will/did change notifications when that changes (It's possible the automatic key path dependence stuff does this for you, I don't remember if it actually works with paths or just values). Similarly if the window's windowController changes then you'll have the same problem.

Edit: The method is called -automaticallyNotifiesObserversForKey:. I'm inclined to say that it doesn't support key paths, so you'll have to do the manual observing/notifying yourself.

0
votes

KVO autonotifying only supports -set: methods that return void

some method isn't declared and your compiler may thinking its return id and continue.