Can you manually implement Cocoa bindings?
The short answer is, no you can't get it to work with no workaround in the calling code and nibs. Even NSAutounbinder misses some cases for the NSDocument and NSWindowController, if Apple can't get it working correctly for 2 classes they specially rig up those of us without access to innards of AppKit have basically no chance.
Having said that, there are two workaround that are maybe a bit nicer than unbinding in windowWillClose:.
- Do not bind to File's Owner, but instead to drag an NSObjectController as root level object into the nib and bind to that, then setContents: on the object controller during awakeFromNib.
- Turn on Garbage Collection. If that is an option it solves all the object cycle issues ;-) Obviously GC introduces its own issues, and if you need 10.4 compatibility it is a non-starter.
See mmalc's GraphicsBindings example for a good example of how to implement your own bindings. You need to implement the NSKeyValueBindingCreation informal protocol to get it working. To let your controllers know there are things that can be bound, call exposeBinding in the + (id)initialize method of your view:
+ (void)initialize { [self exposeBinding:@"ILIKEBINDAGE"]; }
You'll then need to implement each of the bindings managing methods in the NSKeyValueBindingCreation protocol. You basically need to setup KVO for the view so that it knows when to update based on the application's behaviors and handle the cleanup (unbind:).
It's a lot of extra, fairly ugly code so it may be that using traditional glue code works better and is easier to read.
Here is the best solution I can find. I've got a more detailed discussion and demo code here: http://tomdalling.com/blog/cocoa/implementing-your-own-cocoa-bindings/
Basically, you DO NOT override bind:toObject:withKeyPath:options:
or unbind:
. The default implementation on NSObject
will use NSAutounbinder
to avoid retain cycles. As Louis Gerbarg pointed out, there are still situations where NSAutounbinder
doesn't kick in. However, you can get your bindings working at least as well as Apple's bindings.
Because the default implementation of bind:toObject:withKeyPath:options:
doesn't update the model when the view changes, view-driven changes must be propagated manually. You can use -[NSObject infoForBinding:]
to get all the information necessary to update the bound object. I've added my own method on NSObject with a category:
-(void)propagateValue:(id)value forBinding:(NSString*)binding;
It handles getting the bound object, the bound key path, and applying the value transformer. The implementation is available from the link at the top.