[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 Instanzvariablen name zu überwachen.

    Person *myPerson = [[Person alloc] init]; 
    [myPerson addObserver:myPerson forKeyPath:@"name"options:0 context:NULL];
    

    Mit dem Befehl addObserver wird dem Objekt myPerson ein 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 Parameter forKeyPath welcher 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.m folgenden 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 setName und 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 keyPath ist 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 change und options , mit denen Sie verfolgen können, wie sich ein Wert geändert hat. Dabei ist change ein NSDictionary , 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 Parameter options geregelt. Ändern Sie 0 zu (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) .

    Für den Rest des Beispiels begnügen wir uns mit der Variablen name . Die main -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 name zwei 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 NSKeyValueChangeOldKey und NSKeyValueChangeNewKey bekommt man die entsprechenden Einträge aus dem change -Dictionary. Da Sie sicher sein können, dass name einem NSString entspricht, können Sie die Werte auch wieder in solch ein Objekt umwandeln.

    Gut erkennbar ist, dass name zuerst 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 bei delegate -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 bei observeValueForKeyPath[c] nicht der Fall. Weisen Sie eine Klasse als Observer zu, die diese Methode nicht implementiert, wird das Programm zur Laufzeit abstürzen.


Anmelden zum Antworten