【问题标题】:Can you manually implement Cocoa bindings?你可以手动实现 Cocoa 绑定吗?
【发布时间】:2010-11-13 05:40:03
【问题描述】:

我在为我自己的 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 对象的非保留代理。为避免retain cycle问题,是non-retaining。

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

【问题讨论】:

标签: objective-c cocoa cocoa-bindings


【解决方案1】:

这是我能找到的最佳解决方案。我在这里有更详细的讨论和演示代码: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;

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

【讨论】:

  • 我最近在找到这篇文章之前写了一个相关问题的答案。在视图不需要将更改传播到模型的情况下使用exposeBinding: 是否正确? stackoverflow.com/questions/366938/…
  • 如果我没记错的话,exposeBinding: 实际上并没有在 Interface Builder 之外做任何事情。它所做的只是让您的绑定显示在界面构建器 GUI 中。
【解决方案2】:

简短的回答是,不,如果在调用代码和 nib 中没有解决方法,您就无法让它工作。甚至 NSAutounbinder 也漏掉了 NSDocument 和 NSWindowController 的一些案例,如果 Apple 不能让它为 2 个类正常工作,他们专门装配我们这些没有访问 AppKit 内部的人基本上没有机会。

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

  1. 不要绑定到文件的所有者,而是将 NSObjectController 作为根级别对象拖到 nib 中并绑定到该 nib,然后在 awakeFromNib 期间在对象控制器上设置内容:
  2. 打开垃圾收集。如果这是一个选项,它可以解决所有对象循环问题;-) 显然 GC 引入了它自己的问题,如果您需要 10.4 兼容性,那么它是不适合的。

【讨论】:

    【解决方案3】:

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

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

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

    这是很多额外的、相当难看的代码,所以使用传统的胶水代码可能效果更好,更容易阅读。

    【讨论】:

    • mmalc 的 GraphicsBindings 示例有我提到的保留周期问题。此外,exposeBinding: 已在 Ryan Ballantyne 的回答中讨论过。
    • 很遗憾,这是一个死链接。
    【解决方案4】:

    您可能想查看NSKeyValueBindingCreation Protocol。它允许您通过代码以编程方式创建绑定。 (如果您需要引用 IBOutlet 变量,请记住在 awakeFromNib 方法中完成这项工作,否则它们可能为零。)

    【讨论】:

    • 感谢您的回答,但我正在尝试为自定义类实现自己的绑定,而不是使用现有的绑定。
    • 我认为这就是该协议的 +exposeBinding: 方法的用途。我当然可能是错的,但这似乎是创建您自己的绑定的记录和认可的方式。
    • exposedBindings 仅用于根据我阅读的内容制作界面构建器插件
    • 我不是在谈论 -exposedBindings。这只是告诉您对象公开了哪些绑定。我说的是 +exposeBinding,它(至少,如果我正确地阅读了文档的话)让对象将任何键路径公开为绑定。
    • -exposedBindings(通常)只输出您使用 +exposeBinding 指定的任何内容
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-26
    • 1970-01-01
    • 1970-01-01
    • 2010-09-29
    • 1970-01-01
    • 2016-06-29
    • 1970-01-01
    相关资源
    最近更新 更多