Наблюдение за изменениями в изменчивом массиве с использованием 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 будет запускать уведомления при их вызове.