5

Cosa devo fare per aggiornare un oggetto TableView associato a un NSArrayController quando viene chiamato un metodo che aggiorna l'array sottostante? Un esempio potrebbe chiarire questo.NSArrayController e KVO

All'avvio dell'applicazione, viene creato un SubwayTrain. Quando SubwayTrain viene inizializzato, crea una singola SubwayCar. SubwayCar ha un "passeggeri" di array mutevole. Quando viene inizializzata una macchina della metropolitana, viene creata la matrice passeggeri e vengono inseriti un paio di oggetti People (diciamo una persona con il nome "raccoglitore di biglietti" e un'altra, chiamata "ragazzo senza casa"). Questi ragazzi sono sempre sulla SubwayCar quindi li creo all'inizializzazione e li aggiungo alla schiera dei passeggeri.

Durante la vita dell'applicazione, i passeggeri salgono a bordo dell'auto. 'addPassenger' viene chiamato sulla SubwayCar, con la persona passata come argomento.

Ho un NSArrayController legato a subwayTrain.subwayCar.passengers e al momento del lancio il mio collezionista di biglietti e il senzatetto si presentano bene. Ma quando uso [subwayCar aggiungere Passenger:], il TableView non si aggiorna. Ho confermato che il passeggero è definitivamente aggiunto alla matrice, ma nulla viene aggiornato nella GUI.

Che cosa potrei fare di sbagliato? Il mio istinto è che è collegato a KVO - il controller di array non si sa aggiornare quando si chiama addPassenger (anche se le chiamate addPassenger [addOggetto passeggeri:] .Che potrei sbagliare qui - Posso inserire il codice se aiuta

Grazie a chiunque abbia voglia di dare una mano.

UPDATE

Così, si scopre che posso arrivare a questo lavoro, modificando il metodo addPassenger da

[seatedPlayers addObject:person]; 

a

NSMutableSet *newSeatedPlayers = [NSMutableSet setWithSet:seatedPlayers]; 

[newSeatedPlayers addObject:sp]; 

[seatedPlayers release]; 

[self setSeatedPlayers:newSeatedPlayers]; 

Immagino che questo sia dovuto al fatto che sto usando [auto setSeatedPlayers]. È questo il modo giusto per farlo? Sembra terribilmente complicato copiare l'array, rilasciare quello vecchio e aggiornare la copia (invece di aggiungere semplicemente all'array esistente).

+0

In che modo la vista tabella è vincolata al controller? Avete ciascuna colonna di tabella associata a una proprietà di 'subwayTrain.subwayCar.passengers' (ad esempio, nome colonna vincolato a' subwayTrain.subwayCar.passengers.name')? – outis

+0

sì esatto. E i nomi dei passeggeri si presentano quando si lancia. –

risposta

1

Così, si scopre che posso arrivare a questo lavoro, modificando il metodo addPassenger da

[seatedPlayers addObject:person]; 

a

NSMutableSet *newSeatedPlayers = [NSMutableSet setWithSet:seatedPlayers]; 
[newSeatedPlayers addObject:sp]; 
[seatedPlayers release]; 
[self setSeatedPlayers:newSeatedPlayers]; 

Credo che questo è perché sto usando [self setSeatedPlayers]. È questo il modo giusto per farlo?

Prima di tutto, è il setSeatedPlayers:, con i due punti. Questo è di vitale importanza in Objective-C.

Utilizzare i setter è il modo corretto per farlo, ma si sta utilizzando il modo corretto errato. Funziona, ma stai ancora scrivendo più codice di quello che ti serve.

Quello che dovresti fare è implementare i set accessor, come addSeatedPlayersObject:. Quindi, invia te stesso quel messaggio. Questo rende l'aggiunta di persone un breve one-liner:

[self addSeatedPlayersObject:person]; 

E finché si seguono the KVC-compliant accessor formats, si otterrà notifiche KVO gratuitamente, proprio come si fa con setSeatedPlayers:.

I vantaggi di questo corso setSeatedPlayers: sono:

  • Il codice di mutare il set sarà più breve.
  • Perché è più breve, sarà più pulito.
  • L'utilizzo di specifici accessori di set-mutazione fornisce la possibilità di specifiche notifiche KVO di set-mutazione, invece di notifiche generali di tutto-dang-set-changed.

Anch'io preferisco questa soluzione oltre mutableSetValueForKey:, sia per la brevità e perché è così facile da errori ortografici la chiave in questa stringa letterale. (Uli Kusterer has a macro to cause a warning when that happens, che è utile quando si ha realmente bisogno di parlare con KVC o KVO stesso.)

7

Non so se è considerato un bug, ma addObject: (e removeObject: atIndex :) non genera notifiche KVO, motivo per cui la vista del controller di array/tabella non viene aggiornata. Per essere KVO-compatibile, usare mutableArrayValueForKey:

Esempio:

[[self mutableArrayValueForKey:@"seatedPlayers"] addObject:person]; 

Potrai anche desidera implementare InsertObject: inSeatedPlayersAtIndex: dal momento che il default metodi KVO è molto lento (che creano un nuovo array, aggiungere l'oggetto di tale matrice, e impostare l'array originale al nuovo array - molto inefficiente)

- (void)insertObject:(id)object inSeatedPlayerAtIndex:(int)index 
{ 
    [seatedPlayers insertObject:object atIndex:index]; 
} 

noti che questo metodo sarà inoltre chiamata quando il controller aggiunge oggetti, quindi è anche un bel gancio per pensa come registrare un'operazione di annullamento , eccetera.

1

Non ho provato questo, quindi non posso dire che funziona, ma non è vero ricevere notifiche KVO chiamando

InsertObject: atArrangedObjectIndex:

sul ArrayController?

+0

Sì, l'utilizzo di NSArrayController per la modifica della matrice fa sì che NSArrayController sia a conoscenza della mutazione e che (e le cose ad esso associate) debbano essere aggiornate di conseguenza. – ipmcc

0
  1. Usa willChangeValueForKey: e didChangeValueForKey: avvolto intorno a un cambiamento di un membro quando il cambiamento non sembra causare una notifica KVO. Ciò è utile quando si modifica direttamente una variabile di istanza.

  2. Utilizzare willChangeValueForKey:withSetMutation:usingObjects: e didChangeValueForKey:withSetMutation:usingObjects: avvolto attorno a una modifica del contenuto di una raccolta quando la modifica non sembra causare una notifica KVO.

  3. Utilizzare [seatedPlayers setByAddingObject:sp] per rendere le cose più corte e per evitare di allocare inutilmente il set mutabile.

Nel complesso, mi piacerebbe fare o questo:

[self willChangeValueForKey:@"seatedPlayers" 
      withSetMutation:NSKeyValueUnionSetMutation 
       usingObjects:sp]; 
[seatedPlayers addObject:sp]; 
[self didChangeValueForKey:@"seatedPlayers" 
      withSetMutation:NSKeyValueUnionSetMutation 
       usingObjects:sp]; 

o questo:

[self setSeatedPlayers:[seatedPlayers setByAddingObject:sp]]; 

con la seconda alternativa provocando un richiamo automatico delle funzioni elencate al punto 1 prima alternativa dovrebbe essere migliore prestazione.

1

La chiave della magia di Key Value Observing è Key Value Compliance. Inizialmente stavi usando un nome metodo addObject: che è associato solo al "pattern accessor non ordinato" e la tua proprietà era una proprietà indicizzata (NSMutableArray). Quando hai cambiato la tua proprietà in una proprietà non ordinata (NSMutableSet) ha funzionato. Considerare NSArray o NSMutableArray come proprietà indicizzate e NSSet o NSMutableSet come proprietà non ordinate. Devi davvero leggere attentamente questa sezione per sapere cosa è necessario per far accadere la magia ... Key-Value-Compliance. Esistono alcuni metodi "Richiesti" per le diverse categorie anche se non si prevede di utilizzarli.