如何通过NSArrayController获得有关模型更改的通知?

时间:2009-06-10 00:46:13

标签: objective-c cocoa cocoa-bindings key-value-observing nsarraycontroller

我有一个NSView子类,它绑定到arrangedObjects的{​​{1}}。当数组插入或删除项目时,将通知视图。如果存储在数组中的模型的属性发生了变化,如何通知它?

我是否需要将我的视图作为观察者添加到添加到数组中的每个项目的每个(相关)属性中?

当在数组中添加或删除项目时,我会通过NSArrayController子类中的observeValueForKeyPath:ofObject:change:context:通知我。我没有收到存储在数组中的模型更改的通知,但我可以在每次收到插入通知时,将视图作为观察者添加到新项目的属性中。这是最好的方法吗?

我为模型类覆盖了NSView,以便我可以看到发生了什么,并注意到绑定到addObserver的{​​{1}}列将自己添加为适当属性的观察者。这可以自动发生还是手动设置观察结果?

2 个答案:

答案 0 :(得分:9)

非常感谢dreamlax,但我认为我没有做好解释我的问题的工作。我的模型类是可观察的并产生了正确的通知,但我无法在不直接观察数组中的每个项目的情况下观察它们。

我认为关键路径的文档可以改进,因为我找不到任何解释我需要进行的非常简单的更改的内容。数组魔术键路径有一些很好的信息,但没有简单的“这些是常见的东西”文档。

反正。以前在我的NSView子类中,我有以下内容:

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

要获得NSArrayController arrangedObjects内模型更改的通知,我需要添加的是对arrangedObjects.name的观察(对于我的模型的name属性)。所以上面的代码变成了:

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

就是这样!现在,如果arrangedObjects中的任何对象更改name,我会收到通知。

答案 1 :(得分:3)

也许不是观察潜在的许多键值路径,为什么不让数组中的每个对象在发生变化时发布通知,然后只有一个对象需要观察一个通知而不是一个对象观察到许多键值路径。

编辑:

此外,您的阵列对象也可以响应名为+keyPathsForValuesAffecting<key>的类方法,其中<key>是您的密钥名称。以下是一个示例:paymentDue是一个键,当值invoiceItems.countpaymentsMade发生更改时会受到影响。 invoiceItems.countpaymentsMade发生更改后,系统会向paymentDue发送通知。

+ (NSSet *) keyPathsForValuesAffectingPaymentDue:
{
    return [NSSet setWithObjects:@"invoiceItems.count", @"paymentMade", nil];
}

如果您在10.4上运行,或者定位到10.4或更早版本,则需要使用this方法,但它基本上归结为同样的事情。

编辑2:

澄清你的其他评论;你仍然可以手动调用数组中的每个对象

[[NSNotificationCenter defaultCenter] postNotificationName:@"ModelDidChange" object:self];

然后,使用某些控制器代码,您可以注册来自对象的通知更新。如果您选择一个唯一的通知名称,那么您不需要手动监听特定对象,您可以告诉NSNotificationCenter您希望从任何对象接收通知。您的控制器可以很容易地确定哪个对象已经发生了变化。

  1. 注册通知中心(这些方法应位于控制器对象中):

    // This string could really be just about anything you want, but make it conspicuous.
    static NSString * const ModelDidChangeName = @"ModelDidChange";
    
    - (void) awakeFromNib
    {
        // using nil for the object parameter will make the notification center
        // invoke modelDidChange: regardless of who the sender is.
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(modelDidChange:) name:ModelDidChangeName object:nil];
    }
    
  2. 实施处理通知的方法。

    - (void) modelDidChange:(NSNotification *) notification
    {
        MyModelClass *myObject = [notification object];
        // do stuff with your model if necessary.
        // do stuff with your view too
    }
    
  3. 让模型对象在部分内容发生变化时发布通知:

    @implementation MyModelClass
    
    - (void) setSomething:(NSString *) newThing
    {
        [something autorelease];
        something = [newThing copy];
        if (something == nil)
        {
            // special case scenario for when something is nil
            // do stuff, modify MyModelClass instance's attributes
            [[NSNotificationCenter defaultCenter] postNotificationName:ModelDidChange object:self];
            // the controller's modelDidChange: method is automatically invoked.
        }
    }
    @end
    
  4. 但是

    如果您的型号符合KVC标准且使用适当的KVO回调制作,则无需手动通知。