13

Desidero avere più osservatori su più eventi di un singolo oggetto (relazione 1-a-N).Quando utilizzare NSNotificationCenter

Un meccanismo per realizzare questa operazione è fornito dallo NSNotificationCenter. Il meccanismo sembra piuttosto eccessivo se usato per il mio problema.

Come vorrei farlo manualmente senza l'uso di NSNotificationCenter:

- (void)addDelegate:(id<DelegateProtocol>)delegate; 
- (void)removeDelegate:(id<DelegateProtocol>)delegate; 

per aggiungere e rimuovere gli osservatori dal mio oggetto.

- (void)someEventFired:(NSObject<NSCopying> *)eventData 
{ 
    for (id delegate in delegates) { 
     NSObject *data = [eventData copy]; 
     [delegate someEventFired:data]; 
    } 
} 

Questo meccanismo è semplice e semplice da implementare senza che gli oggetti debbano condividere stringhe aggiuntive.

  • Esiste uno schema ufficiale per i delegati 1-to-N (come gli eventi C#) in un framework iOS oltre allo NSNotificationCenter?
  • Quando deve essere utilizzato lo NSNotificationCenter e quando no?
  • Quando deve essere utilizzata un'implementazione come quella che sto suggerendo qui e quando no?
+0

Ho usato raramente o mai NSNotificationCenter ma ho utilizzato lo stesso metodo descritto. L'ho usato in numerose app iOS (direi sopra le 50 app) per molti anni, finora non ho riscontrato alcun problema. Un possibile trucchetto potrebbe essere il fatto che è necessario essere certi di rimuovere gli osservatori a volte, o potrebbero non essere rilasciati quando previsto, perché sono conservati dal detentore dei delegati. – Jonny

+0

Penso che anche se NSNotificationCenter non è l'approccio che conosciamo da C#, dovremmo comunque concentrarci sull'uso dei modelli di progettazione per la piattaforma specifica per cui stiamo lavorando. Detto questo, sono andato con NSNotificationCenter in diverse app ora (non come molti come te, però), e non credo che il codice sia peggiorato con esso. – Etan

risposta

14

Per convenzione, i delegati dovrebbero probabilmente essere utilizzati solo per le relazioni 1: 1. Se hai veramente bisogno delle relazioni 1: N per questo tipo di funzionalità, hai due opzioni:

  1. Come hai detto, NSNotificationCenter.
  2. Key-Value Observing (noto anche come KVO).

KVO è appropriato se si interessa solo quando una particolare proprietà di un oggetto cambia. Altrimenti, dovresti semplicemente considerare l'utilizzo di NSNotificationCenter. Puoi anche essere avvisato solo quando un oggetto specifico pubblica quella notifica passando quell'oggetto nel metodo addObserver:selector:name:object:.

Apple utilizza NSNotification in scenari simili (come le notifiche definite per UITextField, tra cui UITextFieldTextDidBeginEditingNotification, UITextFieldTextDidChangeNotification, e UITextFieldTextDidEndEditingNotification).

+0

Puoi dirmi se lo sto usando giusto? http://stackoverflow.com/questions/42111829/share-object-state-between-view-controllers-in-ios-development/42113389#42113389 –

-3

dico NSNotificationCenter deve essere sempre utilizzato, rispetto al modello delegato, tranne che in situazioni in cui si esegue una query un delegato delle informazioni (ad esempio -webView:shouldLoadRequest:). È più stabile, più facile da implementare e produce un codice più pulito, quindi tenta di utilizzare un delegato. L'altra alternativa sono i blocchi, che possono essere buoni, ma possono essere un problema quando si tratta di gestione della memoria.

Alla fine, dipende da voi, ma penso che NSNotificationCenter sia il modo migliore per andare in quasi tutte le situazioni, se non altro per la funzionalità di osservatore multiplo.

1

NSNotificationCenter non è eccessivo per quello che stai suggerendo, è esattamente la soluzione giusta. Impedisce che l'oggetto osservato debba conoscere o preoccuparsi dei suoi osservatori, rendendo il codice più liberamente accoppiato e più pulito.

La condivisione di stringhe per i nomi di notifica è banale e possono essere definite in un file di costanti condivisi o nell'intestazione dell'oggetto osservato, se gli osservatori devono importare questa intestazione per eseguire i loro lavori.

0

Un rapporto delegato 1-N non ha senso. Date un'occhiata a

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 

per esempio. E se questo oggetto avesse davvero n delegati? Come dovrebbe decidere quale delle n viste che riceve da tutti i suoi delegati dovrebbe essere usato? I delegati sono esattamente questo principio 1 a 1.

NSNotificationCenter è l'approccio giusto. Basta usare

addObserver:selector:name:object: 

rispettivamente

postNotification: 

Questo non è sicuramente troppo codice. Ed è molto facile per te dato che il centro gestisce tutte le chiamate.

2

utilizzando le notifiche trasmette: 1 mittente invia semplicemente un'informazione e chi si è sintonizzato, lo riceve. Insignificante come una stazione radio, non c'è nessun canale indietro (per il momento lasciamo perdere i telefoni)

la delega è qualcosa di diverso. L'oggetto, che chiede a un delegato di fare qualcosa, di solito ha bisogno di un risultato di quella richiesta, quindi la delega è una comunicazione 1-a-1, che è sempre iniziata dall'oggetto, non dal delegato (mentre l'oggetto può avere metodi che può essere chiamato per informare l'oggetto di avviare la comunicazione, ovvero [tableView reloadData]).

Quindi, se il mittente deve recuperare i dati, è una delega. Se al mittente non interessa nulla dopo la trasmissione, vai con le notifiche.

Se si verifica la situazione, è necessario delega, ma diversi oggetti devono implementare il protocollo. dovresti avere 1 delegato, che mantiene i riferimenti agli altri oggetti e chiama i metodi per conto dei mittenti, oppure potresti andare con i blocchi.

+0

Oggi vorrei avere solo un oggetto che tutti i VC condividono e che li informa. – vikingosegundo

1

La soluzione proposta non è né più semplice dell'utilizzo di NSNotificationCenter né è thread-safe.

Per rendere sicuro il thread della soluzione, è necessario fornire un meccanismo per impedire la modifica dell'array dei delegati durante l'esecuzione del ciclo di invio eventi per loop.

La soluzione richiede anche di mantenere l'array dei delegati nella classe. Con NotificationCenter puoi semplicemente utilizzare il centro predefinito e non è necessario implementare i metodi di aggiunta/rimozione nella tua classe. Al contrario, le istanze possono registrarsi per ricevere le notifiche nel modo migliore (selettore/blocco, coda, origine). La tua classe di origine non deve preoccuparsi di questi dettagli. Deve solo registrarsi come fonte di notifiche di un tipo specificato. Usare i blocchi per gestire le notifiche è davvero conveniente.

Un'alternativa al centro di notifica consiste nell'utilizzare Valore-Osservazione-chiave se soddisfa le esigenze del caso d'uso.

In definitiva, il meccanismo che si decide di utilizzare dipende dal modo in cui si applica al caso d'uso specifico.

0

Non si desidera utilizzare NSNotificationCenter per eventi diversi dall'intero sistema (ad esempio l'aspetto della tastiera o qualche evento simile). Il motivo è che non è completamente sicuro per il tipo, può rendere tutto dipendente da tutto e che non si ottengono più controlli del tempo di compilazione o risultati di ricerca dell'utilizzo.

KVO secondo me non dovrebbe essere usato per osservare i cambiamenti al di fuori dell'oggetto che stai ascoltando dal momento che ha lati negativi simili (nessun controllo del tempo di compilazione, crash se non si rimuovono gli ascoltatori correttamente o li si registra due volte) .

Il pattern addDelegate/removeDelegate che si pone è a mio avviso il percorso corretto poiché ha il vantaggio di mantenere controlli di sicurezza del tipo e del compilatore e rende esplicite le dipendenze. L'unico problema è che Apple non fornisce una soluzione pronta all'uso per questo modello, dal momento che è necessario un tipo di raccolta che conservi gli elementi in modo debolmente per evitare i cicli di conservazione.

Tuttavia, vedere il codice dal mio BMCommons framework che risolve questo problema ordinatamente utilizzando BMNullableArray e macro. Vedere l'intestazione BMCore.h per una definizione di quelle macro:

BM_LISTENER_METHOD_DECLARATION(protocol) 
BM_LISTENER_METHOD_IMPLEMENTATION(protocol) 

L'implementazione assicura che lo stesso ascoltatore non verrà mai aggiunto due volte e, inoltre, che gli ascoltatori sono debolmente mantenuti, senza causare alcun incidente, anche se si dimentica di annullare la registrazione se stessi su deallocation (anche se preferisco cogliere questa condizione con un asserzione dato che si tratta di un errore di programmazione).

Problemi correlati