你可以手动实现Cocoa绑定吗?

38
我尝试为我的自定义NSView子类实现绑定。它可以工作,但是当从nib文件绑定到File's Owner时会出现保留周期问题。在稍微了解一下后,我发现苹果几年前也遇到了同样的问题,但是用一些神奇的未记录类(NSAutounbinder)解决了它。
这里有一个关于保留周期问题的详细讨论 http://www.cocoabuilder.com/archive/message/cocoa/2004/6/12/109600。解决方法是在窗口控制器被“释放”之前而不是在其“dealloc”之前取消所有绑定,比如在windowWillClose:中。对我来说,这似乎是一个不必要的hack。
我的问题是:是否有任何方法可以制作与苹果制作的一样好的自定义绑定,而不使用未记录的功能?我是不是走错了路?

更新2:我找到了一个解决方案,可以让手动实现的绑定与苹果的绑定完全相同。它利用了未公开的NSAutounbinder类,而不实际使用未公开的功能。我稍后会发布解决方案。


更新:我已尝试使用“exposeBinding:”,但似乎没有任何区别。然而,“bind:toObject:withKeyPath:options:”的NSObject实现半工作。它将变化从bindee传播到binder(即从模型/控制器到视图),但不起作用相反的方式。此外,虽然bindee显然正在被观察,但“observeValueForKeyPath:ofObject:change:context:”从未触发。
示例项目在这里:http://www.tomdalling.com/wp-content/BindingsTest.zip 苹果的文档表明,事实上,您必须覆盖“bind:toObject:withKeyPath:options:”才能实现手动绑定。请参见这里:http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaBindings/Concepts/HowDoBindingsWork.html

顺便提一下:我调查了未经记录的NSAutounbinder的工作原理,以下是我所知道的。

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

当调用-[NSWindowController release]并且retainCount == 1时,NSAutounbinder取消所有绑定到自身的绑定。这确保在对象被释放之前没有悬空指针。


当CocoaBuilder不可用时,您可以在http://lists.apple.com/archives/cocoa-dev/2004//Jun/msg00835.html找到相关的讨论主题。 - s4y
4个回答

22

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

基本上,你不需要重写bind:toObject:withKeyPath:options:unbind:。NSObject上的默认实现将使用NSAutounbinder来避免保留循环。如Louis Gerbarg所指出的那样,仍然存在NSAutounbinder无法启动的情况。但是,你可以使你的绑定工作至少与苹果的绑定一样好。

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

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

它处理获取绑定对象、绑定键路径和应用值转换器的操作。实现代码可从顶部链接获取。


我最近在找到这篇文章之前,已经回答了一个相关问题。如果视图不需要将更改传播到模型,使用exposeBinding:是否正确?https://dev59.com/PUbRa4cB1Zd3GeqPwQFl#7394273 - paulmelnikow
如果我没记错的话,exposeBinding: 在 Interface Builder 之外实际上并没有什么作用。它只是让你的绑定在界面构建器 GUI 中显示出来。 - Tom Dalling

3
请参考 mmalc 的 GraphicsBindings 示例,了解如何实现自己的绑定。您需要实现 NSKeyValueBindingCreation 非正式协议才能使其正常工作。为了让您的控制器知道有可以绑定的内容,请在视图的 + (id)initialize 方法中调用 exposeBinding。
+ (void)initialize { [self exposeBinding:@"ILIKEBINDAGE"]; }

你需要实现NSKeyValueBindingCreation协议中的每个绑定管理方法。基本上,你需要为视图设置KVO,以便它知道何时根据应用程序的行为进行更新,并处理清理(unbind:)。
这是很多额外、相当丑陋的代码,因此使用传统的粘合代码可能更好,更容易阅读。

1
mmalc的GraphicsBindings示例存在我提到的保留循环问题。此外,exposeBinding:已在Ryan Ballantyne的答案中讨论过。 - Tom Dalling
2
不幸的是,那是一个无效链接。 - uchuugaka

3
简短而直白的回答是,没有解决方案,即使在调用代码和nibs中也无法让它工作。即使使用NSAutounbinder,对于NSDocument和NSWindowController,Apple也无法正确处理一些情况。如果连这两个类都有问题,那么我们这些没有进入AppKit内部的人就几乎没有机会了。
话虽如此,还有两个解决方法,可能比在windowWillClose中取消绑定要好:
1. 不要绑定到File's Owner,而是将NSObjectController拖入nib并将其设置为根级对象,然后在awakeFromNib期间在对象控制器上设置内容。
2. 打开垃圾回收。如果这是一个选项,它可以解决所有对象循环问题;显然,GC引入了它自己的问题,如果您需要10.4的兼容性,那么这是不行的。

2
您可能想查看NSKeyValueBindingCreation Protocol。它允许您通过代码编程方式创建绑定。(如果需要引用IBOutlet变量,则请在awakeFromNib方法中进行操作,否则它们可能为空。)

2
谢谢你的回答,但是我正在尝试为自定义类实现自己的绑定,而不是使用现有的绑定。 - Tom Dalling
我认为这就是该协议中 +exposeBinding: 方法的作用。当然,我可能错了,但这似乎是创建自己的绑定的记录和认可方式。 - Ryan Ballantyne
2
我不是在谈论-exposedBindings。那只是告诉您对象公开的绑定内容。我说的是+exposeBinding,它(至少,如果我正确阅读文档)允许对象将任何键路径公开为绑定。 - Ryan Ballantyne
-exposedBindings(通常)只输出您使用+exposeBinding指定的内容。 - Tom Dalling
我刚刚在一个演示项目中尝试使用exposeBinding:,但似乎它并没有起作用。不过还是谢谢你的提示,因为我之前没有想到过尝试它。 - Tom Dalling
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接