您可以手动实现 Cocoa 绑定吗?
我已经尝试为我自己的 NSView 子类实现绑定。 它可以工作,但是从 nib 文件绑定到文件所有者时存在保留周期问题。 经过一番阅读后,我发现苹果几年前也遇到过同样的问题,但已经用一些神奇的未记录类(NSAutounbinder)修复了它。
这里有一个关于保留周期问题的冗长讨论 http:// www.cocoabuilder.com/archive/message/cocoa/2004/6/12/109600。 解决方法是在窗口控制器释放之前取消绑定所有绑定,而不是在窗口控制器解除分配之前,在像 windowWillClose: 这样的地方。 对我来说,这似乎是不必要的黑客攻击。
我的问题是:有没有什么方法可以使自定义绑定与 Apple 制作的绑定一样工作,而不使用未记录的功能? 我是否以错误的方式处理这个问题?
更新 2:我找到了一个解决方案,允许手动实现的绑定与 Apple 的绑定完全相同。 它利用了未记录的 NSAutounbinder 类,但实际上并未使用未记录的功能。 我将在今天晚些时候发布解决方案。
更新:我尝试过使用 exposeBinding:
,它似乎没有任何区别。 但是,bind:toObject:withKeyPath:options:
的 NSObject
实现有一半有效。 它将更改从绑定者传播到绑定者(即从模型/控制器到视图),但不能以相反的方式工作。 此外,尽管绑定对象显然正在被观察,但 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 _autounbinder] 从 NSWindowController 获取的。 NSAutounbinder 是 NSWindowController 对象的非保留代理。 它是非保留的,以避免保留周期问题。
当 -[NSWindowController release] 被调用并且retainCount == 1时,NSAutounbinder解除所有与自身的绑定。 这可以确保在释放对象之前不存在指向该对象的悬空指针。
I've had a crack at implementing bindings for my own NSView subclass. It works, but there are problems with retain cycles when binding to File's Owner from a nib file. After reading into it a little, I discovered that Apple had the same problem a few years back but have fixed it with some magic undocumented class (NSAutounbinder).
There is a lengthy discussion of the retain cycle problem here http://www.cocoabuilder.com/archive/message/cocoa/2004/6/12/109600 . The workaround is to to unbind all bindings before the window controller is released, not before it is deallocated, in a place like windowWillClose:. This seems like an unnecessary hack to me.
My question is this: Is there any way to make custom bindings that work as well as the ones made by Apple, without using undocumented features? Am I going about this the wrong way?
UPDATE 2: I have found a solution that allows manually implemented bindings to work exactly like Apple's bindings. It takes advantage of the undocumented NSAutounbinder class, without actually using undocumented features. I will post the solution later today.
UPDATE: I've tried using exposeBinding:
, and it doesn't seem to make any difference. However, the NSObject
implementation of bind:toObject:withKeyPath:options:
half works. It propogates changes from bindee to binder (i.e. from model/controller to view), but doesn't work the opposite way. Also, although the bindee is obviously being observed, observeValueForKeyPath:ofObject:change:context:
is never triggered.
Example project here: http://www.tomdalling.com/wp-content/BindingsTest.zip
Apple's documentation indicates that you do, in fact, have to override bind:toObject:withKeyPath:options:
to implement manual bindings. See here: http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaBindings/Concepts/HowDoBindingsWork.html
SIDE NOTE: I've investigated how the undocumented NSAutounbinder works, and here's what I know.
When a binding is created to an NSWindowController, the bound object is actually an NSAutounbinder that is acquired from the NSWindowController with -[NSWindowController _autounbinder]. NSAutounbinder is a non-retaining proxy for the NSWindowController object. It is non-retaining to avoid the retain cycle problem.
When -[NSWindowController release] is called and retainCount == 1, The NSAutounbinder unbinds all bindings to itself. This ensures that there are no dangling pointers to the object before it is deallocated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这是我能找到的最佳解决方案。 我在这里有更详细的讨论和演示代码: http: //tomdalling.com/blog/cocoa/implementing-your-own-cocoa-bindings/
基本上,您不要覆盖
bind:toObject:withKeyPath:options: 或<代码>取消绑定:。
NSObject
上的默认实现将使用NSAutounbinder
来避免保留循环。 正如 Louis Gerbarg 指出的那样,在某些情况下,NSAutounbinder
仍然无法发挥作用。但是,您可以让您的绑定至少像 Apple 的绑定一样工作。由于
bind:toObject:withKeyPath:options:
的默认实现在视图更改时不会更新模型,因此必须手动传播视图驱动的更改。 您可以使用-[NSObject infoForBinding:]
获取更新绑定对象所需的所有信息。 我在 NSObject 上添加了自己的方法和类别:它处理获取绑定对象、绑定键路径以及应用值转换器。 可以从顶部的链接获取该实现。
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:
orunbind:
. The default implementation onNSObject
will useNSAutounbinder
to avoid retain cycles. As Louis Gerbarg pointed out, there are still situations whereNSAutounbinder
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: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.
简短的回答是,不,如果调用代码和笔尖中没有解决方法,您就无法让它工作。 即使 NSAutounbinder 也错过了 NSDocument 和 NSWindowController 的一些情况,如果 Apple 无法让它在 2 个类中正常工作,他们会专门为我们这些无法访问 AppKit 内部结构的人基本上没有机会。
话虽如此,有两种解决方法可能比在 windowWillClose: 中取消绑定更好一些。
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:.
请参阅 mmalc 的 GraphicsBindings 示例是如何实现您自己的绑定的一个很好的示例。 您需要实现 NSKeyValueBindingCreation 非正式协议才能使其正常工作。 为了让你的控制器知道有一些东西可以绑定,请在你的视图的 + (id)initialize 方法中调用exposeBinding:
然后你需要实现 NSKeyValueBindingCreation 协议中的每个绑定管理方法。 您基本上需要为视图设置 KVO,以便它知道何时根据应用程序的行为进行更新并处理清理(取消绑定:)。
这是很多额外的、相当丑陋的代码,因此使用传统的粘合代码可能效果更好并且更容易阅读。
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:
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.
您可能需要查看 NSKeyValueBindingCreation 协议< /a>. 它允许您通过代码以编程方式创建绑定。 (如果您需要引用 IBOutlet 变量,请记住在 awakeFromNib 方法中完成这项工作,否则它们可能为零。)
You may want to check out the NSKeyValueBindingCreation Protocol. It lets you create bindings programmatically through code. (Remember to do the work in an awakeFromNib method if you need to reference IBOutlet variables or they could be nil.)