Key-Value-Observing
aus Wikipedia, der freien Enzyklopädie
Key-Value-Observing (KVO) ist eine Technologie der objekt-orientierten Programmierung, bei der sichergestellt wird, dass Veränderungen an einem Objekt von einer Stelle des Programmes aus an einer anderen Stelle synchronisiert werden.
Inhaltsverzeichnis |
[Bearbeiten] Problemstellung und Anwendungsgebiete
Nach dem von Smalltalk eingeführten Model-View-Controller-Entwurfsmuster besteht eine Trennung zwischen den gespeicherten Werten (Model) und er Ansicht (View) derselben. Dabei sollen die einzelnen Ansichten unabhängig von dem Model sein. Dies führt zur Problematik, dass jede Änderung der Daten des Models in den vorhandenen Ansichten aktualisiert werden muss.
Dies sei an einer einfachen Adressapplikation erläutert, in der mehrere Fenster existieren, die Adresseinträge anzeigen und dem User zur Veränderung geben. Wird nun in einem Fenster ein Wert bearbeitet, so müssen alle weiteren Fenster, die ebenfalls den gleichen Adresseintrag zeigen, aktualisiert werden. Zur Verbesserung des Laufzeitverhaltens ist es angezeigt, die Menge der benachrichtigen Ansichten durch möglichst genaue Auswahl möglichst klein zu halten.
Ein System zur Lösung dieser Problematik ist KVO. Hierbei wird bereits in der Run-Time-Engine ein Mechanismus für die Aktualisierung vorgesehen ist. In Cocoa etwa kann sich jede Ansicht, aber auch jedes andere Objekt, für eine bestimmte Eigenschaft eines anderen Objektes interessieren. Dies bedeutet, dass es eine Observierung anmeldet. So meldet sich etwa im folgenden Code ein Objekt observer
für die Observierung der Eigenschaft name
des Objektes interestingObject
an:
[interestingObject addObserver:observer forKeyath:@"name"];
Bei jeder Änderung des Wertes von name im Objekt interestingObject erhält der Observierer automatisch die Nachricht -observeValueForKeyPath:ofObject:change:context:. Hier kann sich der Observierer dann selbst aktualisieren:
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context { // Daten neu abholen und Anzeige aktualiseren }
Ein Observierungszyklus:
[Bearbeiten] Funktionsweise
Grundsätzlich geht KVO davon aus, dass ein Objekt als Entität über verschiedene Eigenschaften verfügt, die über einen Schlüssel (Key) angesprochen werden können. KVO ist also nur bei Entitäten realisierbar. Die weitere Funktionsweise unterscheidet sich je nach Art des KVO:
[Bearbeiten] Manuelles KVO
Beim manuellen KVO existieren spezielle Nachrichten, die für das Auslösen der Observierungsnachricht sorgen. Zentrales Element ist dabei eine Observierungs-Datenbank, die einerseits die Observierer sammelt und andererseits auf die entsprechenden Nachrichten Observierungsereignisse auslöst.
Der Nachteil besteht darin, dass jeder, der eine Änderung an einer Eigenschaft des Objektes vornimmt, sich an diese Vereinbarung halten muss. Insbesondere bei fremden Code, der nicht als Source vorliegt, ist man dem ursprünglichen Programmierer ausgeliefert.
// manuelles KVO: Der Ändernde teilt die Änderung explizit mit. [self willChangeValueForKey:@"name"]; name = … // Neuen Wert zuweisen [self didChangeValueForKey:@"name"];
[Bearbeiten] Automatisches KVO
Der Trick beim automatischen KVO besteht dabei, bereits in der Laufzeitumgebung der jeweiligen Sprache bzw. des jeweiligen Frameworks das Auslösen der entsprechenden Nachrichten zu implementieren. Da auf diese Weise das verändernde Objekt nicht selbst die Aktualisierungsnachricht veranlassen muss, kann der Programmierer fremden Codes nichts "vergessen". Damit ist jeder beliebige Code, der Accessoren benutzt automatisch KVO-tauglich. Dies erfordert jedoch, dass die Programmiersprache selbst Methodenaufrufe anhand ihres Namens erkennen kann, was entsprechende Informationen zur Laufzeit notwendig macht, wie sie etwa C++ nicht zur Verfügung stellt.
// automatisches KVO: Die RTE betrachtet automatisch die aufgerufenen Methoden [self setName:…];
Selbstverständlich kann die Laufzeitumgebung nicht die gesamte Zeit alle Methodenaufrufe aller Objekte nach dem Methodennamen parsen. Da allerdings etwa Objective-C eine komplette Beschreibung der Instanzobjekte und ihrer Klassen mitführt, wird zur Laufzeit bei Einrichtung einer Observierung eine besondere Klasse mit dem Namen NSKVONotifying_OriginalKlasse
hergestellt und das ursprüngliche Instanzobjekt durch dieses ersetzt (Proxying). Bei Aufruf der Methode -setName: (ProxyClass)
weiß diese Instanz dann, dass sie andere informieren muss und ruft gleichzeitig mittels Forwarding die Methode des Originalobjektes auf, um die Operation selbst durchzuführen.
KVO ist durch Cocoa Bindings weiter formalisiert worden. Hierbei gibt die Ansicht selbst Eigenschaften bekannt, über die sie informiert werden muss. Ein View zur Anzeige von Text nennt etwa die Bindinge value
oder font
. Die Verbindung zwischen Entität im Model und dem View erfolgt dann in der IDE sourcodelos.
[Bearbeiten] Beispiele
In der praktischen Anwendung eines Users kann man diesen Unterschied zwischen etwa C++ und etwa Objective-C alltäglich an Anwendungen erkennen. So benötigen Windows-Einstelldialoge (C++ ist die Standardprogrammeirsprache für Windows) häufig einen "OK"- oder "Set"-Button, damit die Aktualisierung durch den Programmierer angestellt werden kann. OS-X-Applikationen benötigen dies nicht, da jede Objekt-Änderung automatisch zu einer Aktualisierung des gesamten Systems führt. Daher hat auch etwa der Font-Dialog in OS X keinen "OK"-Button, sondern funktioniert synchron.