你能手动实现Cocoa绑定吗?

时间:2009-07-23 01:17:03

标签: objective-c cocoa cocoa-bindings

我在为自己的NSView子类实现绑定方面遇到了麻烦。它工作正常,但是当从nib文件绑定到File的Owner时,保留周期存在问题。在阅读了一下之后,我发现Apple几年前也遇到了同样的问题,但已经修复了一些魔法未记录的类(NSAutounbinder)。

这里对保留周期问题进行了长时间的讨论http://www.cocoabuilder.com/archive/message/cocoa/2004/6/12/109600。解决方法是在窗口控制器释放之前取消绑定所有绑定,而不是在取消分配之前,在windowWillClose:之类的位置取消绑定。这似乎是对我不必要的黑客攻击。

我的问题是:有没有办法让自定义绑定与Apple制作的绑定一样好,而不使用未记录的功能?我是以错误的方式解决这个问题吗?


更新2:我找到了一个解决方案,允许手动实现的绑定与Apple的绑定完全相同。它利用了未记录的NSAutounbinder类,而没有实际使用未记录的功能。我今天晚些时候会发布解决方案。


更新:我尝试使用exposeBinding:,但似乎没有任何区别。但是,NSObject一半的bind:toObject:withKeyPath:options:实施工作正常。它传播从bindee到binder的变化(即从模型/控制器到视图),但不会以相反的方式工作。此外,虽然明显观察到了bindee,但永远不会触发observeValueForKeyPath:ofObject:change:context:

此处的示例项目:http://www.tomdalling.com/wp-content/BindingsTest.zip

Apple的文档表明,事实上,您必须覆盖bind:toObject:withKeyPath:options:来实现手动绑定。见这里:http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaBindings/Concepts/HowDoBindingsWork.html


侧面注意:我已经调查了未记录的NSAutounbinder如何工作,这就是我所知道的。

当为NSWindowController创建绑定时,绑定对象实际上是一个NSAutounbinder,它是从NSWindowController获取的 - [NSWindowController _autounbinder]。 NSAutounbinder是NSWindowController对象的非保留代理。它是不保留的,以避免保留周期问题。

当调用 - [NSWindowController release]并且retainCount == 1时,NSAutounbinder会解除对所有绑定的绑定。这样可以确保在取消分配对象之前没有悬空指针。

4 个答案:

答案 0 :(得分:22)

这是我能找到的最佳解决方案。我在这里有一个更详细的讨论和演示代码:http://tomdalling.com/blog/cocoa/implementing-your-own-cocoa-bindings/

基本上,您不要覆盖bind:toObject:withKeyPath:options:unbind:NSObject上的默认实施将使用NSAutounbinder来避免保留周期。正如Louis Gerbarg所指出的那样,仍然存在NSAutounbinder没有启动的情况。但是,你可以使你的绑定工作至少与Apple的绑定一样好。

由于bind:toObject:withKeyPath:options:的默认实现在视图更改时不会更新模型,因此必须手动传播视图驱动的更改。您可以使用-[NSObject infoForBinding:]获取更新绑定对象所需的所有信息。我在NSObject上添加了我自己的方法,其类别为:

-(void)propagateValue:(id)value forBinding:(NSString*)binding;

它处理获取绑定对象,绑定键路径和应用值转换器。可以从顶部的链接获得实施。

答案 1 :(得分:3)

简短的回答是,在调用代码和笔尖中没有解决方法时,你不能让它工作。甚至NSAutounbinder也错过了NSDocument和NSWindowController的一些案例,如果Apple无法让它在2个班级正常工作,他们特意装备我们这些人而无法访问AppKit的内部基本上没有机会。

话虽如此,有两种解决方法可能比windowWillClose中的解绑更好:。

  1. 不要绑定到File的所有者,而是将NSObjectController作为根级对象拖动到nib中并绑定到它,然后在awakeFromNib期间在对象控制器上设置setContents:。
  2. 打开垃圾收集。如果这是一个选项,它解决了所有的对象周期问题;-)显然GC引入了自己的问题,如果你需要10.4兼容性,那么它是一个非首发。

答案 2 :(得分:3)

有关如何实现自己的绑定的一个很好的示例,请参阅mmalc的GraphicsBindings示例。您需要实施NSKeyValueBindingCreation非正式协议才能使其正常运行。要让控制器知道可以绑定的东西,请在视图的+(id)初始化方法中调用exposeBinding:

+ (void)initialize { [self exposeBinding:@"ILIKEBINDAGE"]; }

然后,您需要在NSKeyValueBindingCreation协议中实现每个绑定管理方法。您基本上需要为视图设置KVO,以便它根据应用程序的行为知道何时更新并处理清理(解除绑定:)。

这是一个额外的,相当丑陋的代码,因此可能使用传统的胶水代码更好,更容易阅读。

答案 3 :(得分:2)

您可能想查看NSKeyValueBindingCreation Protocol。它允许您通过代码以编程方式创建绑定。 (如果需要引用IBOutlet变量或者它们可能是nil,请记住在awakeFromNib方法中进行工作。)