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.
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 evented e percorso chiave.
Prima di discutere la libreria KVOController, mi piacerebbe prendere un momento per esplorare l'API KVO.
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];
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.
È 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.
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:
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.
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.
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.
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 :)];
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
.
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.
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.