Kategorie: ObjC


Properties für Accessoren

3 June 2009 — 10:32

Datenkapselung ist einer der wichtigsten Konzepte in der objektorientierten Programmierung. Um Datenkapselung zu gewährleisten, müssen Setter- und Getter-Methoden (Accessoren) für jede Instanzvariable geschrieben werden. Dies kann unter Umständen sehr zeitaufwendig sein. Die meisten Accessoren haben den gleichen Aufbau und unterscheiden sich lediglich durch die Instanzvariable. Da liegt die Idee nahe, diese Accessoren automatisch generieren zu lassen. Dieses vorgehen nennt man synthetisieren und wurde in Objektive-C 2.0 mit den sogenannten Properties eingeführt.

Propertys haben die allgemeine Syntax:

header
@property (attribut1, attribut2, attribut3, attribut4) datentyp variablenname;

implementation
@syntesize variablenname;

Hat man eine Instanzvariable synthetisiert kann wie folgt darauf zugegriffen werden:

Standard-Syntax
[Obj setVariablenname:wert]
printf(„Der wert lautet %i“, [Obj variablenname]);

Punkt-Syntax
Obj.variblenname = wert;
printf(„Der wert lautet %i“, Obj.variablenname);

Anmerkung:
Die Punkt-Syntax ist unabhängig von den Properties. Es ist möglich die Accessoren selbst zu schreiben (ohne Properties) und über die Punkt Syntax darauf zuzugreifen.

Die 4 Attribute beschreiben folgendes:
-Zugriffsart (readonly, readwrite)
-Setter-Semantik (assign, retain, copy
-Atomicity
-Setter- und Getter-Namen

Zugriffsart

Es gibt 2 Zugriffsarten. Entweder nur lesen (readonly) oder schreiben und lesen (readwrite).
Bei readwrite werden beide Accessoren synthetisiert (vom compiler geschrieben). Im Falle von read wird nur der Getter synthetisiert und somit ist nur ein lesender Zugriff möglich.

Setter-Semantik

Assign

Assign ist die standard Setter-Semantik. Falls man also nichts angibt wird vom Compiler   automatisch assign genommen. Der neue Wert wird der Instanzvariable einfach nur zugewiesen, sonst nichts. Der Referenzcounter wird nicht erhöht. Dadurch ist eine ältere Erhöhung des Referenzcounters ausgeschlossen und er wird somit auch nicht erniedrigt. Dies hat zur Folge, dass die Empfänger -Klasse kein Besitzer des Objektes ist. Wird der Referenzcounter des Objekts von der Sender-Klasse erniedrigt, wird das Objekt gelöscht und ist somit auch nicht mehr für die Empfänger-Klasse erreichbar. Natürlich ist es auch umgekehrt möglich. Deswegen sollte man Propertys mit der assign-Semantik in der dealloc-Methode nicht releasen (den es wurde auch nirgends retained).

Beispiel (vom Compiler generierter Code für den Setter)
-(void)setVariablenname:(id)senderValue
{
   instanceValue = senderValue;
}

 assign

Retain

Retain löst die Problematik der Assign-Semantik. Die Instanzvariable wird zunächst released, da es vielleicht schon auf ein Objekt zeigt. Danach wird der neue Wert zugewiesen und dann wird die Instanzvariable retained. Dies bedeutet nun dass die Empfänger-Klasse ein Besitzer des Objektes ist. Die Sender-Klasse kann das Objekt nun löschen und es ist für die Empfänger-Klasse immernoch  erreichbar. Da man in der Retain-Semantik die Instanzvariable retained, muss sie in der dealloc-Methode auch released werden. Beide Klassen (Sender und Empfänger) zeigen nun auf dasselbe Objekt. Ändert eine Klasse das Objekt ist es auch für die andere geändert.

Beispiel  (vom Compiler generierter Code für den Setter)

-(void)setVariablenname:(id)senderValue
if(instanceValue != senderValue)
  {
    [instanceValue release];
    instanceValue = senderValue;
   [instanceValue retain];
}
}

retain

Copy

Copy erlaubt es einer Klasse der alleinige Beitzer eines Objektes zu werden. Das übergebene Objekt wird kopiert und danach erst zugewiesen. Dies hat den Vorteil dass Empfänger- und Sender-Klasse nicht mehr auf dasselbe Objekt zeigen und somit eine Änderung des Objekts der einen Klasse nicht für das Objekt der Anderen gilt. Die Klasse des übergebenen Objekts muss das NSCopying Protocol implementieren.

Beispiel (vom Compiler generierter Code für den Setter)
-(void)setVariablenname:(id)senderValue
{
if(instanceValue != senderValue)
  {
    [instanceValue release];
    instanceValue = [senderValue copy];
}
}

copy

Atomicity

Atomicity gewährleistet den sicheren Zugriff von mehreren Threads auf die Accessoren. Zu einer bestimmten Zeit darf nur ein Thread auf die Accessoren  zugreifen. Möchte man dies vermeiden gibt man einfach das  Attribut „nonatomic“ an. Mehr dazu auf friday.com

Setter- und Getter-Namen

Wie weiter oben beschrieben sind die Standard-Namen: „setVariablenname:“ (Setter) und „variablenname“ (Getter). Dies kann geändert werden. Um zum Beispiel im Falle von Boolean-Werten für den Getter einen Namen wie „isInitial“ zu erzeugen, kann man als Attribut „getter = isInitial“ angeben. Dadurch kann man auf den Boolwert wie folgt zugreifen:

[Object isInitial];
Object.isInitial;

Natürlich kann auch der Settername durch „setter=andererName“ geändert werden.
Außer man hat als Zugriffsart „read“ definiert, dann gibt es nähmlich einen Compilerfehler, da der Setter nicht existiert.

Quelle
developer.apple.com

5 Kommentare » | ObjC

Nachrichten VS Methodenaufrufe

12 May 2009 — 16:22

Um den Umstieg von C++ oder Java auf ObjC (Objective-C) zu erleichtern, poste ich hier den meiner Meinung nach wichtigsten Unterschied.

Einer der Hauptunterschiede von ObjC (Objective-C) zu C++ und Java liegt in der Philosophie hinter dem Aufruf von Instanzfunktionen. Während man in C++ oder Java von einem Methodenaufruf eines Objektes spricht, ist es unter ObjC das verschicken einer Nachricht an das Objekt.

Bei einem Methodenaufruf unter C++ oder Java wird bereits vor dem Ausführen des Programms die Methode an das Objekt gebunden. Das heißt, die Methode des Objektes muss existieren, ansonsten gibt es einen Compilerfehler und ein Start des Programms ist unmöglich. Dieses frühe Binden hat den Vorteil fehlerhafte Methodenaufrufe zu vermeiden, ist jedoch sehr unflexibel.

Unter ObjC verschickt man Nachrichten an Objekte, was übrigens von Smalltalk stammt. Diese Nachrichten werden erst zur Laufzeit an das Objekt geschickt. Dadurch ist eine späte Bindung möglich. Die Nachrichten werden zur Laufzeit vom Objekt interpretiert und falls eine passende Funktion vorhanden ist, wird diese ausgeführt. Dies bedeutet dass der Compiler vor dem Start nicht überprüft ob das Objekt die aufgerufene Funktion besitzt. Spätes binden eines Funktionsaufrufs hat den Nachteil dass es fehlerhafte Funktionsaufrufe geben kann, jedoch den entscheidenden Vorteil der Flexibilität.

Dieses Beispiel sieht sehr simpel aus würde jedoch mit C++ oder Java nicht laufen:

implementation ClassOne

-(void)doSomeThing
{…}
@end

implementation ClassTwo

-(void)doSomeThing
{…}
@end

int main(…)
{
id *object = [[ClassOne alloc]init]; //id ist Typlos und steht für alle Typen von Objekten
[object doSomeThing]; //ruft die Methode von ClassOne
[object release];
object = [[ClassTwo alloc]init];
[object doSomeThing]; //ruft die Methode von ClassTwo
}

1 Kommentar » | ObjC

Zurück nach oben