2014-06-06 22 views
7

Sto cercando di utilizzare KVO per ascoltare gli eventi di modifica raccolta su una proprietà NSArray. Pubblicamente, la proprietà è un NSArray sola lettura, ma è sostenuta da un Ivar NSMutableArray in modo che possa modificare l'insieme.KVO notifiche di modifica di un NSArray sostenuta da un NSMutableArray

so di poter impostare la proprietà ad un nuovo valore per ottenere un “set” cambiamento, ma mi interessa aggiungere, rimuovere, sostituire le modifiche. Come faccio a notificare correttamente questo tipo di modifiche per un 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]; 

classe Observer:

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

Puoi pubblicare quale codice hai scritto finora? – Holly

+0

Inoltre, dovresti probabilmente controllare [questo] (http://stackoverflow.com/questions/3478451/key-value-observing-with-an-nsarray) post. – Holly

+0

La domanda duplicata sembra mettere l'array mutabile nell'intestazione, è comunque necessario per eseguire questa operazione senza fornire l'accesso pubblico per la modifica della matrice in questione? –

risposta

18

In primo luogo, è necessario comprendere che KVO è di osservazione di un oggetto per i cambiamenti nella proprietà. Cioè, non è possibile "osservare un array" in quanto tale, si osserva una proprietà di raccolta indicizzata. Quella proprietà può essere supportata da un array o implementata in altro modo. Fintanto che è conforme a KVC e modificato in modo conforme a KVO, è sufficiente. (Quindi, non importa se la proprietà è di tipo NSArray* o implementato utilizzando una o niente NSMutableArray*.)

Quindi, si sta osservando un esempio di Model per i cambiamenti nella sua proprietà items. Se si desidera che l'osservatore per ottenere una notifica di modifica, è necessario assicurarsi di modificare sempre la proprietà items in maniera KVO-compliant.

Il modo migliore, a mio parere, è l'attuazione del mutable indexed collection accessors e sempre usare quelli per modificare la proprietà. Quindi, si sarebbe implementare almeno uno di questi:

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

E uno di questi:

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

Quando la proprietà è supportato da un NSMutableArray, i metodi di cui sopra sono involucri semplici intorno ai metodi corrispondenti su _items.

Eventuali altri metodi che scrivete per modificare la vostra proprietà dovrebbe passare attraverso uno di quelli. Così, il vostro metodo di -addItem: sarebbe:

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

si potrebbe anche togliere il getter pianura per la proprietà items e invece solo esporre i getter di raccolta indicizzati:

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

che non è necessario, però, se ci è un tipico getter.

(L'esistenza di queste funzioni di accesso è quello che consente di implementare una proprietà a-molti che non è di tipo NSArray. Non c'è bisogno, dal punto di vista di KVC, per qualsiasi interfaccia attuale array tipizzato.)

Personalmente, non lo consiglio, ma una volta che si dispone di tali accessor, è possibile anche mutare la proprietà ottenendo il proxy NSMutableArray -like per la proprietà utilizzando -mutableArrayValueForKey: e quindi inviando le operazioni di mutazione ad esso. Quindi, in questo caso, potresti fare [[self mutableArrayValueForKey:@"items"] addObject:item]. Non mi piace perché ritengo che la codifica del valore-chiave valga quando la chiave è data.È dinamico o memorizzato in un file di dati come un NIB, non noto al momento della compilazione. I nomi delle chiavi hard-coding quando hai la possibilità di usare un simbolo di lingua (ad es. Selettore) per indirizzare la proprietà è un odore di codice.

Può essere giustificato, tuttavia, per operazioni che sono veramente tortuose da implementare in termini di accessorie indicizzate, come l'ordinamento.

Infine, è possibile utilizzare -willChange... e -didChange... metodi del protocollo NSKeyValueObserving per emettere le notifiche di modifica quando si modifica direttamente archiviazione secondaria della struttura senza passare attraverso un metodo di mutazione che KVO in grado di riconoscere e gancio in. Per una proprietà di raccolta indicizzata, sarebbero i metodi -willChange:valuesAtIndexes:forKey: e -didChange:valuesAtIndexes:forKey:. Questo è un odore di codice ancora peggiore, per quanto mi riguarda.

+0

Risposta straordinaria grazie per le spiegazioni, mi stavo avvicinando molto, ma questo mi ha solo solidificato per me –

+6

Ho appena creato un KVOMutableArray (https://github.com/haifengkao/KVOMutableArray) che implementa l'idea di @ Ken. –

+0

@HaiFengKao perché non creare sottoclassi di KVOMutableArray su NSMutableArray – ElonChan