Osservazione di un NSMutableArray per l’inserimento / la rimozione

Una class ha una proprietà (e istanza var) di tipo NSMutableArray con accessors sintetizzati (tramite @property ). Se osservi questo array usando:

 [myObj addObserver:self forKeyPath:@"theArray" options:0 context:NULL]; 

E quindi inserisci un object nella matrice in questo modo:

 [myObj.theArray addObject:NSString.string]; 

Una notifica observValueForKeyPath … non viene inviata. Tuttavia, quanto segue invia la notifica corretta:

 [[myObj mutableArrayValueForKey:@"theArray"] addObject:NSString.string]; 

Questo perché mutableArrayValueForKey restituisce un object proxy che si occupa di notificare gli osservatori.

Ma gli accessori sintetizzati non dovrebbero restituire automaticamente tale object proxy? Qual è il modo corretto per ovviare a questo: dovrei scrivere un accessorio personalizzato che invochi solo [super mutableArrayValueForKey...] ?

Ma gli accessori sintetizzati non dovrebbero restituire automaticamente tale object proxy?

No.

Qual è il modo corretto per ovviare a questo: dovrei scrivere un accessorio personalizzato che invochi solo [super mutableArrayValueForKey...] ?

No. Implementare gli accessori di array . Quando li chiami, KVO pubblicherà automaticamente le notifiche appropriate. Quindi tutto ciò che devi fare è:

 [myObject insertObject:newObject inTheArrayAtIndex:[myObject countOfTheArray]]; 

e la cosa giusta accadrà automaticamente.

Per comodità, è ansible scrivere un addTheArrayObject: accessor. Questo accessore chiamerebbe uno dei veri accessori di array descritti sopra:

 - (void) addTheArrayObject:(NSObject *) newObject { [self insertObject:newObject inTheArrayAtIndex:[self countOfTheArray]]; } 

(Puoi e dovresti compilare la class corretta per gli oggetti nell’array, al posto di NSObject .)

Quindi, invece di [myObject insertObject:…] , scrivi [myObject addTheArrayObject:newObject] .

Tristemente, addObject: e la sua controparte removeObject: sono, l’ultima volta che ho controllato, riconosciute da KVO solo per le proprietà set (come in NSSet), non per le proprietà dell’array, quindi non ottieni notifiche KVO gratuite con a meno che non li si implementi in cima agli accessors che riconosce. Ho presentato un bug su questo: x-radar: // problema / 6407437

Ho un elenco di tutti i formati dei selettori di accesso sul mio blog.

Non willChangeValueForKey e didChangeValueForKey in questa situazione. Per uno, hanno lo scopo di indicare che il valore di quel percorso è cambiato, non che i valori in una relazione to-many stanno cambiando. Si vorrebbe usare willChange:valuesAtIndexes:forKey: invece, se l’hai fatto in questo modo. Anche così, l’uso di notifiche KVO manuali come questa è incapsulamento errato. Un modo migliore per farlo è definire un metodo addSomeObject: nella class che possiede effettivamente l’array, che includerebbe le notifiche KVO manuali. In questo modo, i metodi esterni che aggiungono oggetti all’array non devono preoccuparsi di gestire anche il KVO del proprietario dell’array, il che non sarebbe molto intuitivo e potrebbe portare a codice non necessario e possibili bug se si inizia ad aggiungere oggetti al array da diversi posti.

In questo esempio, continuerei a utilizzare mutableArrayValueForKey: Non sono positivo con gli array mutabili, ma credo che leggendo la documentazione che questo metodo sostituisca effettivamente l’intero array con un nuovo object, quindi se le prestazioni sono un problema, è necessario implementare insertObject:inAtIndex: e removeObjectFromAtIndex: nella class che possiede l’array.

quando si desidera semplicemente osservare il conteggio modificato, è ansible utilizzare un percorso chiave aggregato:

 [myObj addObserver:self forKeyPath:@"[email protected]" options:0 context:NULL]; 

ma tieni presente che qualsiasi riordino in theArray non sparerà.

La tua risposta alla tua stessa domanda è quasi giusta. Non theArray esternamente. Invece, dichiarare una proprietà diversa, theMutableArray , corrispondente a nessuna variabile di istanza, e scrivere questa accessoria:

 - (NSMutableArray*) theMutableArray { return [self mutableArrayValueForKey:@"theArray"]; } 

Il risultato è che altri oggetti possono utilizzare thisObject.theMutableArray per apportare modifiche all’array e queste modifiche triggersno KVO.

Le altre risposte indicano che l’efficienza aumenta se si implementa anche insertObject:inTheArrayAtIndex: e removeObjectFromTheArrayAtIndex: sono ancora corretti. Ma non c’è bisogno che altri oggetti debbano saperlo o chiamarli direttamente.

Se non hai bisogno del setter puoi anche usare il modulo più semplice di seguito, che ha prestazioni simili (stesso tasso di crescita nei miei test) e meno boilerplate.

 // Interface @property (nonatomic, strong, readonly) NSMutableArray *items; // Implementation @synthesize items = _items; - (NSMutableArray *)items { return [self mutableArrayValueForKey:@"items"]; } // Somewhere else [myObject.items insertObject:@"test"]; // Will result in KVO notifications for key "items" 

Questo funziona perché se gli accessori di array non sono implementati e non c’è un setter per la chiave, mutableArrayValueForKey: cercherà una variabile di istanza con il nome _ o . Se trova uno, il proxy inoltrerà tutti i messaggi a questo object.

Vedi questi documenti Apple , sezione “Modello di ricerca degli accessori per raccolte ordinate”, # 3.

Devi avvolgere il tuo addObject: chiama in willChangeValueForKey: e didChangeValueForKey: calls. Per quanto ne so, non c’è modo per NSMutableArray che stai modificando per sapere di eventuali osservatori che osservano il suo proprietario.

una soluzione è usare un NSArray e crearlo da zero inserendo e rimuovendo, come

 - (void)addSomeObject:(id)object { self.myArray = [self.myArray arrayByAddingObject:object]; } - (void)removeSomeObject:(id)object { NSMutableArray * ma = [self.myArray mutableCopy]; [ma removeObject:object]; self.myArray = ma; } 

di quanto si ottiene il KVO e può confrontare vecchio e nuovo array

NOTA: self.myArray non dovrebbe essere nullo, altrimenti arrayByAddingObject: risultati anche in nil

A seconda del caso, questa potrebbe essere la soluzione, e poiché NSArray memorizza solo i puntatori, questo non è un sovraccarico, a meno che non si lavori con array di grandi dimensioni e operazioni frequenti