[E] Objective-C: Zugriff auf Klassenelemente - Key-Value Observing
-
2 Zugriff auf Klassenelemente
Vorwort
C++ ist nicht der einzige Ansatz gewesen, um der Sprache C die Möglichkeiten objektorientierter Programmierung einzuhauchen. Es gab die Entwicklung von Objective-C, die ebenfalls diese Möglichkeiten ergänzte, und gleichzeitig eine Kompatibilität zu C erhielt. Die Sprache war in der Breite wenig bekannt, da sie vor allem auf den Mac-Systemen zur Applikationsentwicklung benutzt wurde und wird. Durch den Boom von Mac, iPhone und wohl bald auch iPad wird die Sprache allerdings zusehends wichtiger werden, daher gibt es im Magazin nun eine einführende Artikelserie zu Objective-C (kurz: ObjC).
Dieser Artikel ist von http://www.cocoa-coding.de übernommen und behandelt wie Klassen und Objekte in Objective-C aufgebaut sind.
2.3 Key-Value Observing
Ebenfalls ein sehr nützliches Verfahren ist Key-Value Observing, dass Sie zum Beispiel verwenden können, um Änderungen an einem Wert zu verfolgen oder zu protokollieren.
Realisiert wird das Ganze durch Observer, von denen Sie durchaus mehrere benutzen können.Gegeben sein soll wieder die Klasse
person, die Ihnen noch aus dem letzten Kapitel bekannt ist. Ziel ist es Veränderungen an der Instanzvariablennamezu überwachen.Person *myPerson = [[Person alloc] init]; [myPerson addObserver:myPerson forKeyPath:@"name"options:0 context:NULL];Mit dem Befehl
addObserverwird dem ObjektmyPersonein Observer zugewiesen, oder besser gesagt, angehängt. Das Wort add lässt schon darauf schliessen, dass es mehrere sein können. Als nächster Parameter folgt das Objekt, in dem die Methode des Observers zu finden ist. Dieses Objekt ist es, das bei einer Änderung benachrichtigt wird, dafür muss es eine genau definierte Methode implementieren. Dann gibt es den ParameterforKeyPathwelcher die Variable bestimmt.In diesem Beispiel liegt die Methode zur Überwachung auch in der Klasse person. Das muss nicht so sein, ist aber hier sehr praktisch. Fügen Sie also
Person.mfolgenden Methode hinzu.-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context { NSLog(@"name geändert"); }In Ihrem Programm ändert sich der Name zwei Mal. Ein Mal durch
setNameund das zweite Mal durch Key-Value Coding. Der Observer sollte also diese Änderungen bemerken und dementsprechend Ausgaben machen.[myPerson setAlter:[NSNumber numberWithInt:32] ]; [myPerson setName:@"Müller"]; [myPerson setVorname:@"Max"]; NSString *wert = @"name"; [myPerson setValue:@"Huber"forKey:wert];
Sie können den gleichen Observer auch für eine weitere Variable benutzen.
Person *myPerson = [[Person alloc] init]; [myPerson addObserver:myPerson forKeyPath:@"name"options:0 context:NULL]; [myPerson addObserver:myPerson forKeyPath:@"vorname"options:0 context:NULL];In diesem Fall müssen Sie aber unterscheiden, durch welchen Aufruf die Methode ausgelöst wurde. Durch den Parameter
keyPathist dies aber sehr leicht möglich.-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context { if( [keyPath isEqualToString:@"name"] ) { NSLog(@"name geändert"); } else if( [keyPath isEqualToString:@"vorname"] ) { NSLog(@"vorname geändert"); } }
Zwei weitere Parameter sind
changeundoptions, mit denen Sie verfolgen können, wie sich ein Wert geändert hat. Dabei istchangeeinNSDictionary, eine Art Liste, welche die neuen und die alte Werte beinhaltet. Zuerst müssen sie allerdings bestimmen, welche Werte sie bekommen wollen. In diesem Beispiel natürlich den neuen und den alten Wert. Dies wird über den Parameteroptionsgeregelt. Ändern Sie 0 zu(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld).Für den Rest des Beispiels begnügen wir uns mit der Variablen
name. Diemain-Methode kann daher etwas aufgeräumt werden und sollte etwa so aussehen:int main (intargc, const char* argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Person *myPerson = [[Person alloc] init]; [myPerson addObserver:myPerson forKeyPath:@"name" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:NULL]; [myPerson setName:@"Müller"]; NSString *wert = @"name"; [myPerson setValue:@"Huber"forKey:wert]; NSLog(@"Name: %@",[myPerson valueForKey:wert]); [myPerson release]; [pool drain]; return 0; }Wie zuvor wird
namezwei mal geändert. Wie also muss die Observer-Methode angepasst werden, um diese Veränderungen zu protokollieren?if( [keyPath isEqualToString:@"name"] ) { NSLog(@"name geändert"); NSString *alterWert = [change valueForKey:NSKeyValueChangeOldKey]; NSLog(@"alter Wert=%@",alterWert); NSString *neuerWert = [change valueForKey:NSKeyValueChangeNewKey]; NSLog(@"neuer Wert=%@",neuerWert); }Durch
NSKeyValueChangeOldKeyundNSKeyValueChangeNewKeybekommt man die entsprechenden Einträge aus demchange-Dictionary. Da Sie sicher sein können, dassnameeinemNSStringentspricht, können Sie die Werte auch wieder in solch ein Objekt umwandeln.
Gut erkennbar ist, dass
namezuerst leer, also null, ist. Dann nimmt er den Text Müller an. Danach wird Müller mit Huber ersetzt und dieser Name zum Schluss ausgegeben.Am Anfang diese Kapitels haben Sie erfahren wie man einem Key einen Observer hinzufügt. Natürlich können Sie Observer auch wieder entfernen falls Sie das wünschen.
[myPerson removeObserver:myPerson forKeyPath:@"name"];Anmerkung für Experten:
Die Methode [/c]observeValueForKeyPath
kommt ohne eine Definition in der Headerdatei aus, wie es auch beidelegate-Methoden der Fall ist. Obwohl es ähnlich funktioniert, ist es aber keine solche Methode. Delegates werden aufgerufen wenn sie implementiert sind, sind sie das nicht, funktioniert das Programm weiterhin normal. Das ist beiobserveValueForKeyPath[c] nicht der Fall. Weisen Sie eine Klasse als Observer zu, die diese Methode nicht implementiert, wird das Programm zur Laufzeit abstürzen.