2014-06-06 3 views
7

Я пытаюсь использовать KVO для прослушивания событий изменения коллекции в свойстве NSArray. Публично, свойство является readonly NSArray, но поддерживается ISMutableArray ivar, так что я могу изменить коллекцию.KVO Уведомления для модификации NSArray, поддерживаемые NSMutableArray

Я знаю, что я могу установить свойство на новое значение, чтобы получить «установленное» изменение, но меня интересуют добавление, удаление, замена изменений. Как я могу правильно сообщить об этих типах изменений для NSArray?

@interface Model : NSObject 

@property (nonatomic, readonly) NSArray *items; 

@end 

@implementation Model { 
    NSMutableArray *_items; 
} 

- (NSArray *)items { 
    return [_items copy]; 
} 

- (void)addItem:(Item *)item { 
    [_items addObject:item]; 
} 

@end 

Model *model = [[Model alloc] init]; 

[observer addObserver:model 
     forKeyPath:@"items" 
     options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) 
     context:NULL]; 

Item *item = [[Item alloc] init]; 
[model addItem:newItem]; 

класса Observer:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 
    if ([keyPath isEqualToString:@"items"]) { 
     //Not called 
    } 
} 
+0

Можете ли вы разместить код, который вы написали? – Holly

+0

Кроме того, вам следует, вероятно, просмотреть сообщение [this] (http://stackoverflow.com/questions/3478451/key-value-observing-with-an-nsarray). – Holly

+0

Дублирующий вопрос, кажется, помещает изменяемый массив в заголовок, есть ли все равно для достижения этого, не предоставляя публичный доступ к мутированию рассматриваемого массива? –

ответ

18

Во-первых, вы должны понимать, что КВО для наблюдения объекта изменения в его свойствах. То есть вы не можете «наблюдать массив» как таковой, вы наблюдаете свойство индексированной коллекции. Это свойство может быть подкреплено массивом или реализовано каким-то другим способом. До тех пор, пока он совместим с KVC и модифицируется в соответствии с KVO-совместимым способом, этого достаточно. (Таким образом, это не имеет значения, если свойство имеет тип NSArray* или реализован с использованием или что-нибудь NSMutableArray*.)

Итак, вы наблюдаете экземпляр Model изменений в его собственности items. Если вы хотите, чтобы наблюдатель получил уведомление об изменении, вы всегда должны изменить свойство items с соблюдением KVO.

Лучшим способом, на мой взгляд, является реализация mutable indexed collection accessors и всегда использовать его для изменения свойства. Таким образом, вы бы реализовать хотя бы одну из них:

- (void) insertObject:(id)anObject inItemsAtIndex:(NSUInteger)index; 
- (void) insertItems:(NSArray *)objects atIndexes:(NSIndexSet *)indexes; 

И один из них:

- (void) removeObjectFromItemsAtIndex:(NSUInteger)index; 
- (void) removeItemsAtIndexes:(NSIndexSet *)indexes; 

Когда свойство опирается на NSMutableArray, эти методы являются простые обертки вокруг соответствующих методов на _items.

Любые другие методы, которые вы пишете для изменения вашей собственности, должны проходить через один из них. Таким образом, ваш метод -addItem: будет:

- (void)addItem:(Item *)item { 
    [self insertObject:item inItemsAtIndex:[_items count]]; 
} 

Можно также удалить простой поглотитель для items собственности и вместо того, чтобы только выставить индексированные добытчик коллекции:

- (NSUInteger) countOfItems; 
- (id) objectInItemsAtIndex:(NSUInteger)index; 

В этом нет необходимости, хотя, если есть является типичным геттером.

(Существование этого аксессораха является то, что позволяет реализовать свойство ко многим, что не типа NSArray. Там нет необходимости, с точкой КЦА зрения, для любого фактического массива типизированного интерфейса.)

Лично я не рекомендую это, но как только у вас есть такие аксессоры, вы также можете изменить свойство, получив NSMutableArray-прокси-сервер для этого свойства, используя -mutableArrayValueForKey:, а затем отправив ему мутационные операции. Итак, в этом случае вы можете сделать [[self mutableArrayValueForKey:@"items"] addObject:item]. Мне это не нравится, потому что я считаю, что ключевое значение кодируется, когда ключ является данными.Он динамический или хранится в файле данных, таком как NIB, который неизвестен во время компиляции. Имена жесткого кодирования, если у вас есть возможность использовать символ языка (например, селектор) для обращения к свойству, являются запахом кода.

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

Наконец, вы можете использовать протокол NSKeyValueObserving в -willChange... и -didChange... методы испускать уведомления об изменениях, когда вы непосредственно изменить хранилище подпирают свойства, не проходя через метод мутации, что КВО может распознавать и зацепить. Для свойства индексированной коллекции это будут методы -willChange:valuesAtIndexes:forKey: и -didChange:valuesAtIndexes:forKey:. Насколько мне известно, этот запах еще хуже.

+0

Удивительный ответ спасибо за объяснения, я очень близко подошел, но это просто закрепило его для меня –

+6

Я просто сделал KVOMutableArray (https://github.com/haifengkao/KVOMutableArray), который реализует идею Кена. –

+0

@HaiFengKao почему бы не подклассифицировать ваш KVOMutableArray в NSMutableArray – ElonChan

Смежные вопросы