Si vous avez déjà travaillé avec KVO (Key-Value Observer) dans Cocoa, il y a de fortes chances que vous rencontriez divers problèmes. L'API n'est pas géniale et oublier de supprimer un observateur peut entraîner des fuites de mémoire, voire des pannes. La bibliothèque KVOController de Facebook vise à résoudre ce problème.
Si vous débutez dans l'observation clé-valeur ou KVO, je vous recommande de lire au préalable le guide du développeur d'Apple sur le sujet ou l'article de Mattt Thompson sur NSHipster. Pour citer le guide d'Apple sur KVO, "l'observation des valeurs-clés fournit un mécanisme qui permet aux objets d'être informés des modifications apportées aux propriétés spécifiques d'autres objets". Mattt définit KVO comme suit: "L'observation de valeur-clé permet une introspection événementielle ad-hoc entre des instances d'objet spécifiques en écoutant les modifications apportées sur un chemin de clé particulier". Les mots-clés sont evented et chemin clé.
Avant de discuter de la bibliothèque KVOController, j'aimerais prendre un moment pour explorer l'API KVO..
Je ne traiterai pas de manière détaillée le KVO dans ce didacticiel, mais il est important que vous compreniez le concept de base du KVO. L'idée est simple. Un objet peut écouter les modifications apportées aux propriétés spécifiques d'un autre objet. L'objet d'observation est ajouté par l'objet cible en tant qu'observateur d'un chemin de clé spécifique..
Illustrons ceci avec un exemple. Si objetB
souhaite être averti lorsque le prénom
propriété de objetA
changements, puis objetA
doit ajouter objetB
en tant qu'observateur pour le chemin clé prénom
. Grâce à la verbosité d'Objective-C, le code pour y parvenir est assez simple.
[objectA addObserver: objectB forKeyPath: @ options "name": NSKeyValueObservingOptionNew context: NULL];
N'importe quand objetA
de prénom
changements de propriété, observeValueForKeyPath: ofObject: change: context:
est invoqué. Le premier paramètre est le chemin clé observé par objetB
, le deuxième paramètre est l'objet du chemin d'accès à la clé, le troisième argument est un dictionnaire décrivant les modifications et l'argument final est le contexte qui a été passé en tant qu'argument final de addObserver: forKeyPath: options: contexte:
.
J'espère que vous êtes d'accord pour dire que ce n'est pas très élégant. Si vous utilisez beaucoup KVO, l'implémentation de observeValueForKeyPath: ofObject: change: context: devient rapidement long et complexe.
Il est important d'arrêter d'écouter les modifications lorsqu'un objet n'est plus intéressé par la réception de notifications pour un chemin de clé spécifique. Ceci est fait en invoquant removeObserver: forKeyPath:
ou removeObserver: forKeyPath: contexte:
.
Le problème que tous les développeurs rencontrent à un moment donné est soit d’oublier d’appeler removeObserver: forKeyPath:
ou en appelant removeObserver: forKeyPath:
avec un chemin clé non observé par l'observateur. Les raisons en sont multiples et sont à la base du problème auquel de nombreux développeurs sont confrontés lorsqu'ils travaillent avec KVO..
Si vous oubliez d'invoquer removeObserver: forKeyPath:
, vous pourriez vous retrouver avec une fuite de mémoire. Si vous invoquez removeObserver: forKeyPath:
et l'objet n'est pas enregistré en tant qu'observateur, une exception est levée. La cerise sur le gâteau est que le NSKeyValueObserving
le protocole ne fournit pas un moyen de vérifier si un objet observe un chemin de clé particulier.
Heureusement, l'équipe Cocoa de Facebook a été aussi ennuyée que vous par les problèmes ci-dessus et a mis au point une solution, la bibliothèque KVOController. Au lieu de réinventer la roue, l'équipe de Facebook a décidé de s'appuyer sur le KVO. Malgré ses faiblesses, KVO est robuste, largement supporté et très utile.
La bibliothèque KVOController ajoute un certain nombre d'éléments à KVO:
Avant de commencer, il est important de souligner que la bibliothèque KVOController nécessite ARC et que les cibles de déploiement minimales sont iOS 6 pour iOS et 10.7 pour OS X..
Je suis un grand partisan de CocoaPods et j'espère que vous l'êtes aussi. Pour ajouter la bibliothèque KVOController à un projet utilisant CocoaPods, ajoutez le pod au fichier Podfile de votre projet et exécutez pod update
installer la bibliothèque.
pod 'KVOController'
Sinon, vous pouvez télécharger la dernière version de la bibliothèque à partir de GitHub et ajouter manuellement la bibliothèque en copiant KVOController.h et KVOController.m à votre projet.
La première chose à faire est d’initialiser une instance du FBKVOController
classe. Jetez un coup d’œil à l’extrait de code suivant dans lequel je crée un FBKVOController
exemple dans un contrôleur de vue initWithNibName: bundle:
méthode.
- (id) initWithNibName: (NSString *) bundle nibNameOrNil: (NSBundle *) nibBundleOrNil self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil]; if (self) _KVOController = [FBKVOController controllerWithObserver: self]; retourner soi-même;
Notez que je stocke une référence à la FBKVOController
objet dans le contrôleur de vue _KVOController
variable d'instance. Une caractéristique intéressante de la bibliothèque KVOController est que l’observateur est automatiquement supprimé dès que FBKVOController
l'objet est désalloué. En d’autres termes, il n’est pas nécessaire de penser à retirer l’observateur car cela se fait automatiquement au moment où la FBKVOController
l'objet est désalloué.
Vous avez plusieurs options pour commencer à observer un objet. Vous pouvez adopter l'approche traditionnelle en invoquant observer: keyPath: options: contexte:
. Le résultat est que observeValueForKeyPath: ofObject: change: context:
est invoqué chaque fois qu'un événement de changement a lieu.
[_KVOController observe: person keyPath: @ options "name": NSKeyValueObservingOptionNew context: NULL];
Cependant, le FBKVOController
class utilise également des blocs et des actions personnalisées, comme vous pouvez le constater dans les extraits de code suivants. Je suis sûr que vous reconnaissez que cela rend le travail avec KVO beaucoup plus agréable..
[_KVOController observe: person keyPath: @ options "name": NSKeyValueObservingOptionNew bloc: ^ (observateur id, objet id, NSDictionary * change) // Répondre aux modifications];
[_KVOController observe: personkeyPath: @ options "name": NSKeyValueObservingOptionNew action: @selector (nameDidChange :)];
Même si l’observateur est automatiquement supprimé lorsque le FBKVOController
l'objet est désalloué, il est souvent nécessaire de cesser d'observer un objet avant que l'observateur ne soit désalloué. La bibliothèque KVOController a un certain nombre de méthodes pour accomplir cette tâche simple..
Pour arrêter d’observer un chemin de clé spécifique d’un objet, appelez inobservé: keyPath:
et passez l'objet et le chemin de la clé. Vous pouvez également arrêter d'observer un objet en appelant ne pas observer:
et passez l'objet que vous ne voulez plus voir. Pour arrêter d’observer tous les objets, vous pouvez envoyer le FBKVOController
objecter un message de inaperçuTout
.
Si vous regardez la mise en œuvre de la FBKVOController
classe, vous remarquerez qu’il conserve une carte interne des objets et des chemins clés observés par l’observateur. le FBKVOController
Cette classe est plus tolérante que l'implémentation de KVO par Apple. Si vous dites au FBKVOController
objet pour arrêter d'observer un objet ou un chemin de clé qu'il n'observe pas, aucune exception n'est levée. Voilà comment il devrait être.
Même si le concept de KVO n’est pas un concept difficile à appréhender, le vrai défi de travailler avec KVO est de s’assurer que les observateurs sont éliminés et que les conditions de course ne causent pas de désordre..
Je vous encourage à consulter la bibliothèque KVOController. Cependant, je vous conseille également de bien comprendre le KVO avant de l'utiliser dans vos projets pour que vous sachiez ce que cette bibliothèque fait pour vous dans les coulisses..