8

我有一个NSView绑定arrangedObjectsNSArrayController. 当数组插入或删除项目时,会通知视图。如果存储在数组中的模型的属性发生更改,如何通知它?

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

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

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

4

2 回答 2

10

非常感谢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为了获得对's中模型更改的通知,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];
  }
}

就是这样!现在,如果有任何对象发生变化,我会收到通知arrangedObjectsname

于 2009-06-10T10:58:56.933 回答
3

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

编辑:

此外,您的数组对象还可以响应名为+keyPathsForValuesAffecting<key>where <key>is your key name 的类方法。下面是一个例子:paymentDue是一个键,当值invoiceItems.countpaymentsMade改变时会受到影响。当invoiceItems.countpaymentsMade已更改时,任何绑定到paymentDue的内容都会发送通知。

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

如果您在 10.4 上运行,或者以 10.4 或更早版本为目标,则需要改用方法,但它本质上归结为同一件事。

编辑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
    

如果您的模型正确兼容 KVC 并使用适当的 KVO 回调,则不需要手动通知。

于 2009-06-10T03:15:43.487 回答