【问题标题】:Is it necessary to override bind:toObject:withKeyPath:options: in an NSView subclass to implement binding?是否有必要在 NSView 子类中重写 bind:toObject:withKeyPath:options: 来实现绑定?
【发布时间】:2008-12-14 20:21:28
【问题描述】:

我有一个 NSView 子类,它具有我想要绑定的属性。我在子类中实现了以下内容:

myView.h:

@property (readwrite, retain) NSArray *representedObjects;

myView.m:

@synthesize representedObjects;

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


-(void)bind:(NSString *)binding toObject:(id)observableController withKeyPath:(NSString *)keyPath options:(NSDictionary *)options
{
    if ([binding isEqualToString:@"representedObjects"]) {
        [observableController addObserver: self forKeyPath:@"arrangedObjects" options:NSKeyValueChangeNewKey context:nil];
    } else {
        [super bind: binding toObject:observableController withKeyPath:keyPath options: options];
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"arrangedObjects"]) {
        [self setRepresentedObjects: [object arrangedObjects]];
    }
}

然后我在-[AppController awakeFromNib] 中创建与arrayController 的绑定:

[myView bind:@"representedObjects" toObject:arrayController withKeyPath:@"arrangedObjects" options: nil];

这是实现绑定的正确方法吗?它涉及很多样板代码,让我觉得我做错了什么。

我认为 NSObject 会自动实现我在-bind:toObject:withKeyPath:options: 中手动完成的操作,但事实并非如此。如果我注释掉我的-bind:toObject:withKeyPath:options:,则永远不会调用 setRepresentedObjects 方法。

附加信息: 我做了更多的调查,得出的结论是我原来的方法是正确的,你必须超越-bind:toObject:withKeyPath:options:。这是Cocoa Bindings Programming Topics: How Do Bindings Work?的引述:

在其 bind:toObject:withKeyPath:options: 方法中,对象必须至少执行以下操作:

  • 确定要设置的绑定
  • 使用什么键路径和什么选项记录它绑定到的对象
  • 注册为绑定对象的 keypath 的观察者,以便接收更改通知

清单 2 中的代码示例显示了操纵杆的 bind:toObject:withKeyPath:options: 方法的部分实现,该方法仅处理角度绑定。

清单 2 Joystick 类的 bind:toObject:withKeyPath:options 方法的部分实现:

static void *AngleBindingContext = (void *)@"JoystickAngle";
 
- (void)bind:(NSString *)binding
 toObject:(id)observableObject
 withKeyPath:(NSString *)keyPath
 options:(NSDictionary *)options
{
 // Observe the observableObject for changes -- note, pass binding identifier
 // as the context, so you get that back in observeValueForKeyPath:...
 // This way you can easily determine what needs to be updated.
 
if ([binding isEqualToString:@"angle"])
 {
    [observableObject addObserver:self
                   forKeyPath:keyPath
                  options:0
                  context:AngleBindingContext];
 
    // Register what object and what keypath are
    // associated with this binding
    observedObjectForAngle = [observableObject retain];
    observedKeyPathForAngle = [keyPath copy];
 
    // Record the value transformer, if there is one
    angleValueTransformer = nil;
    NSString *vtName = [options objectForKey:@"NSValueTransformerName"];
    if (vtName != nil)
    {
        angleValueTransformer = [NSValueTransformer
            valueTransformerForName:vtName];
    }
 }
 // Implementation continues...

这清楚地表明 Joystick 类(它是一个 NSView 子类)需要覆盖 -bind:toObject:withKeyPath:options:

我觉得这很令人惊讶。我对这个结论持怀疑态度,因为我没有发现其他代码示例可以做到这一点。但是,正如 Apple 官方文档所说,我应该超越 -bind:toObject:withKeyPath:options:,我认为这是正确的方法。

如果有人能证明我错了,我会非常高兴!

【问题讨论】:

标签: objective-c cocoa cocoa-bindings


【解决方案1】:

不,你不应该需要那个胶水代码。

您所说的“似乎并非如此”是什么意思?如果你忽略它会发生什么?

【讨论】:

    【解决方案2】:

    不,没有必要覆盖bind:

    正如 Peter Hosey 在对先前答案的评论中所写,您可以调用 exposeBinding: 并实现与 KVC 和 KVO 兼容的访问器和设置器。

    MyView.h:

    @interface MyView : NSView {
        NSArray *_representedObjects;
    }
    
    // IBOutlet is not required for bindings, but by adding it you can ALSO use
    // an outlet
    @property (readonly, retain) IBOutlet NSArray *representedObjects;
    
    @end
    

    MyView.m:

    + (void)initialize {
        [self exposeBinding:@"representedObjects"];
    }
    
    // Use a custom setter, because presumably, the view needs to re-draw
    - (void)setRepresentedObjects:(NSArray *)representedObjects {
        [self willChangeValueForKey:@"representedObjects"];
        // Based on automatic garbage collection
        _representedObjects = representedObjects;
        [self didChangeValueForKey:@"representedObjects"];
    
        [self setNeedsDisplayInRect:[self visibleRect]];
    }
    

    然后您可以通过编程方式设置绑定:

    [myView bind:@"representedObjects" toObject:arrayController withKeyPath:@"arrangedObjects" options: nil];
    

    但是,要在 Interface Builder 中设置绑定,您必须创建自定义调色板。

    【讨论】:

      【解决方案3】:

      如果您想在该视图中实现绑定,您肯定需要在自定义视图中实现 -bind:toObject:withKeyPath:options:。您在 myView.m 中的实现非常准确。

      【讨论】:

      • 不,你没有。您只需要调用exposeBinding: 并为每个可绑定属性实现符合KVC 和KVO 的访问器。 @property 和 @synthesize 为您完成后一部分。
      • 有趣,你是对的。我总是用冗长的方式完成它,所以我要删除一些代码。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-26
      • 1970-01-01
      • 1970-01-01
      • 2015-04-28
      相关资源
      最近更新 更多