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

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

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

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

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

Но не был вызван метод ObservationValueForKeyPath, и я обнаружил, что массивы не совместимы с KVO :-(

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

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

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

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

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

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

// Delegate
[myController eventsDidChange];

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

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


person MathewS    schedule 03.05.2012    source источник


Ответы (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 для вставки / удаления.

person Ryder Mackay    schedule 03.05.2012
comment
Спасибо! mutableArrayValueForKey делает свое дело. Есть ли у вас какие-либо советы по выбору того, какой шаблон использовать (KVO, NotificationCenter, делегат), когда вы хотите обмениваться данными между моделью и контроллером? - person MathewS; 04.05.2012
comment
Я определенно что-то здесь упускаю. Может ли кто-нибудь объяснить, где будет вызываться setEvents, если кто-то должен добавить или вставить объект: atIndex: объект в поддерживающий изменяемый массив? - person Alex Zavatone; 29.03.2016

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

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

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

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

person Bryan    schedule 20.03.2015