Как получать уведомления об изменениях моделей через NSArrayController? - PullRequest
8 голосов
/ 10 июня 2009

У меня есть подкласс NSView, который связан с arrangedObjects из NSArrayController. Когда в массив вставлен или удален элемент, представление уведомляется. Как получить уведомление, если у модели, хранящейся в массиве, атрибут изменился?

Нужно ли мне добавлять свое представление в качестве наблюдателя к каждому (соответствующему) атрибуту каждого элемента, добавляемого в массив?

Когда элемент добавляется или удаляется из массива, я получаю уведомление через observeValueForKeyPath:ofObject:change:context: в моем NSView подклассе. Меня не уведомляют об изменениях в моделях, хранящихся в массиве, но я могу, каждый раз, когда мне сообщают о вставке, добавить представление в качестве наблюдателя к атрибутам нового элемента. Это лучший способ сделать это?

Я переопределил addObserver для класса модели, чтобы я мог видеть, что происходит, и заметил, что столбцы NSTableView, связанные с arrangedObjects, добавляют себя в качестве наблюдателей к соответствующим атрибутам. Можно ли сделать так, чтобы это происходило автоматически или я настроил наблюдения вручную?

Ответы [ 2 ]

9 голосов
/ 10 июня 2009

Большое спасибо 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];
  }
}

Чтобы получить уведомление об изменениях моделей в arrangedObjects NSArrayController, все, что мне нужно было добавить, это наблюдение 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 измененный, я получаю уведомление.

3 голосов
/ 10 июня 2009

Может быть, вместо того, чтобы наблюдать потенциально много путей значений ключей, почему бы не каждому объекту в массиве публиковать уведомление, когда что-то изменилось, тогда только одному объекту нужно наблюдать одно уведомление вместо одного объекта, наблюдающего множество путей значений ключей.

EDIT:

Кроме того, ваши массированные объекты могут также отвечать на метод класса с именем +keyPathsForValuesAffecting<key>, где <key> - это имя вашего ключа. Вот пример: paymentDue - это ключ, на который влияют изменения значений invoiceItems.count или paymentsMade. Когда invoiceItems.count или paymentsMade изменился, все, что связано с 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, ручные уведомления не требуются.

...