Als je ooit met KVO hebt gewerkt (Key-Value Observing) in Cocoa, is de kans groot dat je verschillende soorten problemen tegenkomt. De API is niet geweldig en het vergeten van het verwijderen van een waarnemer kan leiden tot geheugenlekken of zelfs erger-crashes. De KVOController-bibliotheek van Facebook is bedoeld om dit probleem op te lossen.
Als u KVO nog niet eerder hebt gezien, bekijk dan eerst de ontwikkelaarsgids van Apple over het onderwerp of het artikel van Mattt Thompson over NSHipster. Om Apple's handleiding over KVO te citeren, "Key-value observing biedt een mechanisme waarmee objecten op de hoogte worden gesteld van wijzigingen in specifieke eigenschappen van andere objecten." Mattt definieert KVO als volgt: "Key-Value Observing maakt ad-hoc, evented introspectie tussen specifieke objectinstanties mogelijk door te luisteren naar wijzigingen op een bepaald sleutelpad." De sleutelwoorden zijn evented en sleutelpad.
Voordat we de KVOController-bibliotheek bespreken, wil ik even de tijd nemen om de KVO API te verkennen.
Ik zal KVO niet in detail behandelen in deze tutorial, maar het is belangrijk dat je het kernconcept van KVO begrijpt. Het idee is eenvoudig. Een object kan wijzigingen in specifieke eigenschappen van een ander object beluisteren. Het waarnemende object wordt door het doelobject toegevoegd als waarnemer voor een specifiek sleutelpad.
Laten we dit illustreren met een voorbeeld. Als objectB
wenst te worden verwittigd wanneer de naam
eigendom van Objecta
veranderingen dan Objecta
moet toevoegen objectB
als een waarnemer voor het sleutelpad naam
. Dankzij de breedsprakigheid van Objective-C is de code om dit te bereiken vrij eenvoudig.
[objectA addObserver: objectB forKeyPath: @ "name" opties: NSKeyValueObservingOptionNew context: NULL];
telkens als Objecta
's naam
eigendom veranderingen, observeValueForKeyPath: ofObject: change: context:
wordt aangeroepen. De eerste parameter is het sleutelpad dat wordt waargenomen door objectB
, de tweede parameter is het object van het sleutelpad, het derde argument is een woordenboek dat de wijzigingen beschrijft, en het laatste argument is de context die werd doorgegeven als het laatste argument van addObserver: forKeyPath: options: context:
.
Ik hoop dat u het ermee eens bent dat dit niet erg elegant is. Als u uitgebreid gebruik maakt van KVO, wordt de implementatie van visibleValueForKeyPath: ofObject: change: context: snel lang en complex.
Het is belangrijk om te stoppen met luisteren naar wijzigingen wanneer een object niet langer geïnteresseerd is in het ontvangen van meldingen voor een specifiek sleutelpad. Dit wordt gedaan door aan te roepen removeObserver: forKeyPath:
of removeObserver: forKeyPath: context:
.
Het probleem waarmee elke ontwikkelaar op een bepaald moment te maken krijgt, is vergeten vergeten te callen removeObserver: forKeyPath:
of bellen removeObserver: forKeyPath:
met een sleutelpad dat niet wordt waargenomen door de waarnemer. De redenen hiervoor zijn vervijfvoudigd en zijn de oorzaak van het probleem dat veel ontwikkelaars tegenkomen bij het werken met KVO.
Als u vergeet aan te roepen removeObserver: forKeyPath:
, u kunt eindigen met een geheugenlek. Als je roept removeObserver: forKeyPath:
en het object is niet geregistreerd als een waarnemer een uitzondering wordt gegooid. De kers op de taart is dat de NSKeyValueObserving
protocol biedt geen manier om te controleren of een object een bepaald sleutelpad waarneemt.
Gelukkig was het Cocoa-team op Facebook net zo geïrriteerd door de bovenstaande problemen als jij en ze kwamen met een oplossing, de KVOController-bibliotheek. In plaats van het wiel opnieuw uit te vinden, besloot het team op Facebook om bovenop KVO te bouwen. Ondanks de tekortkomingen is KVO robuust, breed ondersteund en zeer nuttig.
De KVOController-bibliotheek voegt een aantal dingen toe aan KVO:
Voordat we beginnen, is het belangrijk om te benadrukken dat de KVOController-bibliotheek ARC vereist en dat de minimale implementatiedoelen iOS 6 voor iOS en 10.7 voor OS X zijn.
Ik ben een grote voorstander van CocoaPods en ik hoop dat jij dat ook bent. Als u de KVOController-bibliotheek wilt toevoegen aan een project met CocoaPods, voegt u de pod toe aan het Podfile van uw project en voert u deze uit pod update
om de bibliotheek te installeren.
pod 'KVOController'
U kunt ook de nieuwste versie van de bibliotheek downloaden van GitHub en de bibliotheek handmatig toevoegen door te kopiëren KVOController.h en KVOController.m naar uw project.
Het eerste dat u hoeft te doen, is een exemplaar van de FBKVOController
klasse. Bekijk het volgende codefragment waarin ik een maak FBKVOController
bijvoorbeeld in een view controller initWithNibName: bundel:
methode.
- (id) initWithNibName: (NSString *) nibNameOrNil-bundel: (NSBundle *) nibBundleOrNil self = [super initWithBibName: nibNameOrNil-bundel: nibBundleOrNil]; if (self) _KVOController = [FBKVOController controllerWithObserver: self]; terugkeer zelf;
Merk op dat ik een verwijzing naar de FBKVOController
object in de view controller's _KVOController
instantievariabele. Een geweldige functie van de KVOController-bibliotheek is dat de waarnemer automatisch wordt verwijderd op het moment dat de FBKVOController
object is deallocated. Met andere woorden, het is niet nodig om te onthouden om de waarnemer te verwijderen, omdat dit automatisch gebeurt op het moment dat de FBKVOController
object is deallocated.
Je hebt verschillende opties om te beginnen met het observeren van een object. Je kunt de traditionele aanpak nemen door aan te roepen observeren: sleutelpad: options: context:
. Het resultaat is dat observeValueForKeyPath: ofObject: change: context:
wordt aangeroepen wanneer een veranderingsgebeurtenis plaatsvindt.
[_KVOController observeren: person keyPath: @ "name" opties: NSKeyValueObservingOptionNew context: NULL];
echter, de FBKVOController
klasse maakt ook gebruik van blokken en aangepaste acties zoals u kunt zien in de volgende codefragmenten. Ik ben er zeker van dat u het ermee eens bent dat dit het werken met KVO veel aangenamer maakt.
[_KVOController observeren: person keyPath: @ "name" opties: NSKeyValueObservingOptionNew block: ^ (id observer, id object, NSDictionary * change) // Reageren op wijzigingen];
[_KVOController observeren: person keyPath: @ "name" opties: NSKeyValueObservingOptionNew action: @selector (nameDidChange :)];
Ook al wordt de waarnemer automatisch verwijderd wanneer de FBKVOController
object is deallocated, het is vaak noodzakelijk om te stoppen met het observeren van een object voordat de waarnemer wordt uitgezet. De KVOController-bibliotheek heeft een aantal methoden om deze eenvoudige taak te volbrengen.
Om te stoppen met het observeren van een specifiek sleutelpad van een object, roept u aan unobserve: sleutelpad:
en geef het object- en sleutelpad door. Je kunt ook stoppen met het observeren van een object door op te roepen unobserve:
en geef het object door dat je wilt stoppen met observeren. Om te stoppen met het observeren van elk object, kunt u de FBKVOController
object een bericht van unobserveAll
.
Als je kijkt naar de implementatie van de FBKVOController
klasse, zult u merken dat het een interne kaart bijhoudt van de objecten en sleutelpaden die de waarnemer observeert. De FBKVOController
klasse is meer vergevingsgezind dan de Apple-implementatie van KVO. Als je het vertelt FBKVOController
object om te stoppen met het observeren van een object of een sleutelpad dat niet wordt waargenomen, er wordt geen uitzondering gegenereerd. Dat is hoe het moet zijn.
Hoewel KVO geen moeilijk concept is om te begrijpen, zorgen we ervoor dat waarnemers goed worden verwijderd en de raceomstandigheden geen chaos veroorzaken, is de echte uitdaging bij het werken met KVO..
Ik raad u aan om de KVOController-bibliotheek te bekijken. Ik adviseer u echter ook om een goed begrip van KVO te krijgen voordat u het in uw projecten gebruikt, zodat u weet wat deze bibliotheek voor u achter de schermen doet.