Наблюдение за изменениями в изменчивом массиве с использованием KVO vs. NSNotificationCenter

В моей модели у меня есть массив объектов, называемых событиями. Я хотел бы, чтобы мой контроллер был уведомлен всякий раз, когда новый объект добавляется к событиям.

Я подумал, что хорошим способом сделать это будет использование шаблона KVO для получения уведомлений об изменениях событий (от добавляемого нового объекта)

// AppDelegate
// events is a NSMutableArray @property/@synthesize etc...

[appDelagate addObserver:self
               forKeyPath:@"events"
                  options:NSKeyValueObservingOptionNew
                  context:NULL];

Но метод Наблюдение за значением_кей не был вызван, и я обнаружил, что массивы не совместимы с КВО:-(

Один из вариантов - вручную вызвать метод, вызвав willChangeValueForKey для keyPath.

// ViewController
[self willChangeValueForKey:@"events"];
[self.events addObject:event];
[self didChangeValueForKey:@"events"];

Но это кажется тяжелым, так как я, вероятно, должен также отслеживать состояние массива событий до и после, чтобы к нему можно было получить доступ из метода наблюдений ValueForKeyPath.

Одним из подходов может быть использование стандартного массива (вместо изменяемого) и создание / установка нового экземпляра событий каждый раз, когда я хочу добавить новый объект, или я могу создать отдельное свойство, которое отслеживает, сколько элементов находится в изменяемый массив (хотелось бы, чтобы вы могли наблюдать @"events.count").

Другой вариант - использовать NSNotificationCenter. Я также прочитал некоторые ответы, которые предлагают использовать блоки (но я не знаю, с чего начать).

Наконец, могу ли я сохранить экземпляр моего контроллера в моем делегате и просто отправить соответствующее сообщение?

// Delegate
[myController eventsDidChange];

Странно ли хранить ссылку на контроллер от делегата?

Я изо всех сил пытаюсь понять, как выбрать, какой подход лучше всего использовать, поэтому любые советы по производительности, будущей гибкости кода и передовым методам очень ценятся!

2 ответа

Решение

Вы не должны делать прямые открытые свойства для изменяемых коллекций, чтобы избежать их мутации без вашего ведома. NSArray это не наблюдаемое значение ключа, а ваше свойство " один ко многим" @"events" является. Вот как это наблюдать:

Сначала объявите публичную собственность для неизменной коллекции:

@interface Model
@property (nonatomic, copy) NSArray *events;
@end

Затем в вашей реализации верните его с изменяемым ivar:

@interface Model ()
{
    NSMutableArray *_events;
}
@end

и переопределить геттер и сеттер:

@implementation Model

@synthesize events = _events;

- (NSArray *)events
{
    return [_events copy];
}

- (void)setEvents:(NSArray *)events
{
    if ([_events isEqualToArray:events] == NO)
    {
        _events = [events mutableCopy];
    }
}

@end

Если другим объектам необходимо добавить события в вашу модель, они могут получить изменяемый прокси-объект, вызвав -[Model mutableArrayValueForKey:@"events"],

NSMutableArray *events = [modelInstance mutableArrayValueForKey:@"events"];
[events addObject:newEvent];

Это вызовет уведомления KVO, каждый раз устанавливая свойство с новой коллекцией. Для повышения производительности и более детального управления внедрите остальные средства доступа к массиву.

Смотрите также: Наблюдение NSMutableArray для вставки / удаления.

Согласно документам по методам доступа, вы должны реализовать:

- (void)addEventsObject:(Event*)e
{
    [_events addObject:e];
}

- (void)removeEventsObject:(Event*)e
{
    [_events removeObject:e];
}

Тогда KVO будет запускать уведомления при их вызове.

Другие вопросы по тегам