Valore chiave osservando con KVOController di Facebook

introduzione

Se hai mai lavorato con KVO (Key-Value Observing) in Cocoa, è probabile che ti imbatti in vari tipi di problemi. L'API non è eccezionale e dimenticare di rimuovere un osservatore può causare perdite di memoria o, addirittura, peggiori arresti anomali. La libreria KVOController di Facebook ha lo scopo di risolvere questo problema.

Qual è l'osservazione del valore-chiave?

Se sei nuovo nell'osservazione dei valori-chiave o KVO, ti consiglio di leggere prima la guida per gli sviluppatori di Apple sull'argomento o l'articolo di Mattt Thompson su NSHipster. Per citare la guida di Apple su KVO, "L'osservazione del valore-chiave fornisce un meccanismo che consente agli oggetti di essere notificati delle modifiche a proprietà specifiche di altri oggetti". Mattt definisce KVO come segue: "L'osservazione valore-chiave consente l'introspezione ad-hoc, eventualizzata tra specifiche istanze di oggetto, ascoltando le modifiche su un particolare percorso chiave". Le parole chiave sono eventedpercorso chiave.

Prima di discutere la libreria KVOController, mi piacerebbe prendere un momento per esplorare l'API KVO.

Aggiunta di un osservatore

Non tratterò KVO in modo molto dettagliato in questo tutorial, ma è importante che tu capisca il concetto centrale di KVO. L'idea è semplice. Un oggetto può ascoltare modifiche a proprietà specifiche di un altro oggetto. L'oggetto di osservazione viene aggiunto dall'oggetto di destinazione come osservatore per uno specifico percorso di chiave.

Illustriamo questo con un esempio. Se ObjectB desidera essere avvisato quando il nome proprietà di Objecta cambia, quindi Objecta deve aggiungere ObjectB come osservatore per il percorso chiave nome. Grazie alla verbosità di Objective-C, il codice per realizzarlo è piuttosto semplice.

[objectA addObserver: objectB forKeyPath: @ "name" options: NSKeyValueObservingOptionNew context: NULL];

Rispondere ai cambiamenti

Ogni volta Objecta'S nome cambiamenti di proprietà, observeValueForKeyPath: ofObject: cambiamento: contesto: è invocato. Il primo parametro è il percorso chiave che viene osservato da ObjectB, il secondo parametro è l'oggetto del percorso chiave, il terzo argomento è un dizionario che descrive le modifiche e l'argomento finale è il contesto che è stato passato come argomento finale di addObserver: forKeyPath: opzioni: contesto:.

Spero tu accetti che non è molto elegante. Se stai facendo ampio uso di KVO, l'implementazione di observValueForKeyPath: ofObject: change: context: diventa rapidamente lunga e complessa.

Rimozione di un osservatore

È importante interrompere l'ascolto delle modifiche quando un oggetto non è più interessato alla ricezione di notifiche per un percorso chiave specifico. Questo viene fatto invocando removeObserver: forKeyPath: o removeObserver: forKeyPath: contesto:.

Il problema a cui ogni sviluppatore si imbatte a un certo punto o sta dimenticando di chiamare removeObserver: forKeyPath: o chiamando removeObserver: forKeyPath: con un percorso chiave che non viene osservato dall'osservatore. Le ragioni di ciò sono molteplici e sono la radice del problema che molti sviluppatori devono affrontare quando lavorano con KVO.

Se dimentichi di invocare removeObserver: forKeyPath:, potresti finire con una perdita di memoria. Se invochi removeObserver: forKeyPath: e l'oggetto non è registrato come osservatore viene generata un'eccezione. La ciliegina sulla torta è che il NSKeyValueObserving il protocollo non fornisce un modo per verificare se un oggetto sta osservando un particolare percorso di chiave.

KVOController to the Rescue

Fortunatamente, il team Cocoa di Facebook è stato altrettanto infastidito dai problemi di cui sopra così come sei e hanno trovato una soluzione, la libreria KVOController. Invece di reinventare la ruota, il team di Facebook ha deciso di costruire su KVO. Nonostante le sue carenze, KVO è robusto, ampiamente supportato e molto utile.

La libreria KVOController aggiunge una serie di cose a KVO:

  • thread-sicurezza
  • rimozione indolore degli osservatori
  • supporto per blocchi e azioni personalizzate

Requisiti

Prima di iniziare, è importante sottolineare che la libreria KVOController richiede ARC e che gli obiettivi minimi di implementazione sono iOS 6 per iOS e 10.7 per OS X.

Installazione

Sono un grande sostenitore di CocoaPods e spero che lo sia anche tu. Per aggiungere la libreria KVOController a un progetto utilizzando CocoaPods, aggiungi il pod al Podfile del tuo progetto ed esegui aggiornamento pod per installare la libreria.

pod 'KVOController'

In alternativa, puoi scaricare l'ultima versione della libreria da GitHub e aggiungere manualmente la libreria copiando KVOController.h e KVOController.m al tuo progetto.

Esempi

Inizializzazione

La prima cosa che devi fare è inizializzare un'istanza di FBKVOController classe. Dai un'occhiata al seguente frammento di codice in cui creo a FBKVOController istanza in un controller di visualizzazione initWithnibName: bundle: metodo.

- (id) initWithNibName: (NSString *) nibNameOrNil bundle: (NSBundle *) nibBundleOrNil self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil]; if (self) _KVOController = [FBKVOController controllerWithObserver: self];  return self; 

Si noti che memorizzo un riferimento a FBKVOController oggetto nel controller della vista _KVOController variabile di istanza. Un'ottima caratteristica della libreria KVOController è che l'osservatore viene automaticamente rimosso nel momento in cui si trova FBKVOController l'oggetto è deallocato. In altre parole, non è necessario ricordare di rimuovere l'osservatore poiché questo viene fatto automaticamente nel momento in cui si verifica il FBKVOController l'oggetto è deallocato.

Aggiunta di un osservatore

Hai diverse opzioni per iniziare a osservare un oggetto. Puoi seguire l'approccio tradizionale invocando osservare: percorso chiave: opzioni: contesto:. Il risultato è quello observeValueForKeyPath: ofObject: cambiamento: contesto: viene invocato ogni volta che si verifica un evento di modifica.

[_KVOController osserva: persona keyPath: @ "nome" opzioni: NSKeyValueObservingOptionNuovo contesto: NULL];

comunque, il FBKVOController class sfrutta anche blocchi e azioni personalizzate come puoi vedere nei seguenti frammenti di codice. Sono sicuro che sei d'accordo sul fatto che questo rende il lavoro con KVO molto più piacevole.

[_KVOController osserva: person keyPath: @ "name" options: NSKeyValueObservingOptionNew block: ^ (id observer, id object, NSDictionary * change) // Rispondi alle modifiche];
[_KVOController osserva: persona keyPath: @ "nome" opzioni: NSKeyValueObservingOptionNuova azione: @selector (nameDidChange :)];

Rimozione di un osservatore

Anche se l'osservatore viene automaticamente rimosso quando il FBKVOController l'oggetto è deallocato, spesso è necessario smettere di osservare un oggetto prima che l'osservatore venga disallocato. La libreria KVOController ha un numero di metodi per eseguire questa semplice operazione.

Per interrompere l'osservazione di un percorso chiave specifico di un oggetto, invoca unobserve: percorso chiave: e passare l'oggetto e il percorso della chiave. Puoi anche smettere di osservare un oggetto invocando unobserve: e passa nell'oggetto che vuoi smettere di osservare. Per smettere di osservare ogni oggetto, puoi inviare il FBKVOController oggetto un messaggio di unobserveAll.

Nessuna eccezione

Se date un'occhiata all'implementazione del FBKVOController classe, noterai che mantiene una mappa interna degli oggetti e dei percorsi chiave che l'osservatore sta osservando. Il FBKVOController la classe è più indulgente rispetto all'implementazione di KVO da parte della Apple. Se lo dici al FBKVOController oggetto di smettere di osservare un oggetto o un percorso chiave che non sta osservando, non viene lanciata alcuna eccezione. Ecco come dovrebbe essere.

Conclusione

Anche se KVO non è un concetto difficile da comprendere, assicurandosi che gli osservatori siano correttamente rimossi e le condizioni di gara non causino caos è la vera sfida quando si lavora con KVO.

Vi incoraggio a dare un'occhiata alla libreria KVOController. Tuttavia, ti consiglio anche di avere una buona conoscenza di KVO prima di utilizzarlo nei tuoi progetti in modo da sapere cosa sta facendo questa libreria per te dietro le quinte.