Objective-c


Objective-c

Objective-C, auch kurz ObjC genannt, erweitert die Programmiersprache C um Sprachmittel zur objektorientierten Programmierung. Objective-C++ erlaubt teilweise die Mischung von Objective-C mit C++-Code mit dem Ziel, älteren Code verwenden zu können. Objective-C ist die primäre Sprache von Cocoa (Mac OS X) und GNUstep.

Die Syntax und Konzeption der objektorientierten Erweiterungen ist an Smalltalk angelehnt und von der gewöhnlichen prozeduralen C-Syntax strikt getrennt. Diese Trennung erlaubt es, das gleiche Konzept zur Erweiterung auf andere imperative Sprachen anzuwenden; so gibt es z. B. ebenfalls Objective Pascal.

Inhaltsverzeichnis

Geschichte

Objective-C wurde hauptsächlich von Brad Cox und Tom Love in den 80er Jahren bei PPI, später Stepstone, entwickelt, später dann von NeXT in die GNU Compiler Collection integriert, um als Basis für NextStep zu dienen.

Wesentliche Eigenschaften

Einer der Design-Gedanken von Objective-C war es, die Flexibilität von Smalltalk anzunähern, jedoch auf das zu verzichten, was das Laufzeitverhalten verschlechtern könnte. Der offensichtlichste Verzicht gegenüber Smalltalk ist das Fehlen von Blöcken. Daher ist ein Objective-C-Programm bereits zur Übersetzungszeit vollständig compilierbar.

Viele Konzepte sind gar nicht in der Sprachdefinition selbst festgelegt, sondern werden erst durch das Framework, also etwa Cocoa oder GNUStep ermöglicht. Insbesondere ist das gesamte Laufzeitsystem nicht im Compiler implementiert, sondern besteht aus C-Funktionen. Bei Aufruf einer Member-Funktion in C++ fügt etwa grundsätzlich der Compiler den entsprechenden Code ein, während in Objective-C (bei Versenden einer Nachricht an ein Objekt, dazu später mehr) die C-Funktion objc_msg_send() aufgerufen wird. Daher ist eine Darstellung ohne das entsprechende Laufzeitsystem kaum denkbar und nicht sinnvoll. Originäre Objective-C-Schlüsselwörter erkennt man indessen an dem vorangestellten @.

Spätes Binden

Die wohl gerade gegenüber C++-Programmen bemerkenswerteste Eigenschaft ist das späte Binden von Methoden. Polymorphie ist im Gegensatz zu Sprachen, die auf Simula-67 basieren, nicht nur innerhalb einer Klassenhierarchie möglich, sondern auch darüber hinaus. Eine Methode mit einem bestimmten Namen (Signatur) kann von Objekten jeder Klasse ausgeführt werden, die sie implementieren. Es ist nicht erforderlich, dass der Aufrufer die Klasse kennt oder die Methoden bereits in einer Basisklasse – wenn auch nur virtuell – definiert worden sind.

@interface KlasseA : NSObject {
    //  Instanzvariablen}
 
- (void) doSomething; // Eine neue Methode in der Subklasse
- (void) dumpToLog;   // Noch eine
@end
 
@interface KlasseB : NSObject {
    //  Instanzvariablen}
 
- (void) dumpToLog;   // Es wird nur die zweite Methode implementiert.
@end
 
id anObject = … // irgendeine Instanz der Klasse A oder B.
[anObject dumpToLog]; 
// Obwohl beide Implementierungen unabhängig sind und
// keine entsprechende Methode in der Basisklasse NSObject 
// existiert, wird die Methode jeweils sicher gefunden.

Nachrichten, die an super geschickt werden, unterliegen allerdings nicht dem Dispatching, so dass sie bereits zur Übersetzungszeit vom Compiler der Superklasse zugeordnet werden:

- (id)init {
   self = [super init]; // Ruft stets die Methode der Superklasse auf}

Typlosigkeit

Es ist daher für den Absender nicht notwendig, die Klasse des Empfängers zu kennen. Vielmehr existiert wie im obigen Code erkennbar ein Typ id, der für jedes Objekt jeder Klasse stehen kann. Dabei sei aber erwähnt, dass der Zeiger auf ein Instanzobjekt nicht typisiert ist, um spätes Binden zu ermöglichen. Die einzelnen Instanzen sind stets typisiert, gehören also genau einer Klasse an. Objective-C typisiert also streng, jedoch spät.

Nachrichten

Dies bedingt in Objective-C die strikte Trennung von Nachrichten und Methoden. Man spricht daher eigentlich in Objective-C gar nicht von Methodenaufrufen. Vielmehr gibt es einen Nachrichtensender (Sender) und einen Nachrichtenempfänger (Receiver). Alleine der Receiver entscheidet anhand der Nachricht, welche Methode ausgeführt wird. Dabei wird zunächst versucht, eine gleichnamige Methode zu finden. Es existiert dazu korrespondierend ein Datentyp SEL (Selector), der einen Nachrichtennamen abbildet. Zur vollständigen Nachricht fehlen dann noch die Parameter und der Empfänger.

SEL nachricht = @selector( dumpToLog );
id anObject = …
[anObject performSelector: nachricht];

Es ist darüber hinaus möglich, Nachrichtennamen erst zur Laufzeit zu erstellen.

// Ein Text, der den Namen einer 1:n-Beziehung enthält.
NSString *relationship = @"Children"; 
 
// Mittels String-Verarbeitung bauen wir uns zur Laufzeit daraus einen Nachrichtentext
NSString *messageText = [NSString stringWithFormat: @"countOf%@", relationship];
 
// Das wird jetzt in eine Nachricht umgewandelt
SEL message = NSSelectorFromString( messageText );
 
// Der Empfänger führt die entsprechende Methode aus:
int countOfInstancesInRelationship = [receiver performSelector: message];

Kompliziertere Nachrichten, insbesondere mit zahlreichen Argumenten, werden als Instanzen der Klasse NSInvocation abgebildet.

Hieraus ergibt sich die Möglichkeit, in einer IDE ganze Objektgraphen und Bedienungsoberflächen zu gestalten und zu verbinden, ohne dass die Eingabe von Sourcecode durch den Programmierer oder durch die IDE selbst erforderlich ist. Umgekehrt handelt es sich aber um das „normale“ Vorgehen des Dispatchers, sodass kein Aufsatz hierfür erforderlich ist und der Programmierer jederzeit die Kontrolle über die Ausführung jeder Methode behält (Beispielvideo von Apple). In der Realität entfällt damit ein Großteil der Programmierarbeit.

Kategorien

Kategorien sind Erweiterungen bereits bestehender Klassen um weitere Methoden. Hervorzuheben ist hierbei, dass die in den Kategorien enthaltenen Methoden auch Instanzen erweitern, die von fremden Code erzeugt werden. Dies gilt auch dann, wenn der fremde Code die Kategorie gar nicht kennt.

Wir haben etwa eine Klasse NSNumber, die Zahlen darstellt. Dieser Klasse wollen wir eine Methode hinzufügen, die eine neue Instanz nach Addition einer anderen Instanz von NSNumber liefert. Also schreiben wir hierzu eine Kategorie:

@interface NSNumber( CalculationAddition ) 
- (NSNumber*)numberByAddingNumber:(NSNumber*)summand;
…
@end
@implementation( CalculationAddition ) 
- (NSNumber*)numberByAddingNumber:(NSNumber*)summand
{
   // Additionskramreturn sum;
}
@end

Die in dieser Kategorie enthaltenen Methoden können verwendet werden, wenn die Kategorie im Sourcecode bekannt ist in dem die Instanz erzeugt wird. Allerdings fällt hierbei bereits das Fehlen der Subklasse auf:

#import "NSNumberCalculationAddition.h"// Wir erzeugen eine Instanz der Basisklasse NSNumber!
NSNumber* summand1 = [NSNumber numberWithInt:98]; 
NSNumber* summand2 = [NSNumber numberWithInt:11];
NSNumber* summe = [summand1 numberByAddingNumber:summand2];

Dies liegt darin begründet, dass bereits die Basisklasse selbst um die neuen Methode erweitert wird, Ableitung also nicht notwendig ist.

Darüber hinaus verhält es sich jedoch so, dass diese Kategorie auch Instanzen hinzugefügt wird, die aus bestehendem Code stammen und daher unsere nachträglich hinzugefügte Kategorie gar nicht kennt! Wird also etwa in einem Framework, welches vor Jahren entwickelt wurde und dessen Sourcecode uns gar nicht vorliegt, eine Instanz von NSNumber erzeugt, so hat diese ebenfalls die vorgenannte Methode. Damit ist es möglich, vollständig fremden Klassen Methoden hinzuzufügen.

Protokolle

Ähnlich wie in Java mit Interfaces lassen sich in Objective-C Sätze von Methoden in Protokollen zusammenfassen:

@protocol MyDelegateProtocol
    // Instanzen der Klasse FileSystemGuard verschicken diese Nachricht an Delegates
    - (void) fileSystemGuard: (FileSystemGuard *) guard detectedMount: (NSString *) mountPath;
@end

RTTI/Reflexion

Zur Laufzeit wird zu jedem Objekt ein Verweis auf seinen Typen, also die Klasse mitgeführt. Die Klasse enthält darüber hinaus eine Beschreibung aller Instanzvariablen und implementierten Methoden. Hieran entscheidet der Dispatcher im Receiver, welche Methode er der Nachricht zuordnet. Umgekehrt kann der Absender erfragen, welche Methoden implementiert sind.

// Eine Methode, welche Nachrichten des GUI implementiert
- (IBAction) doSomething: (id) sender {
    // Klassenabfrage: RTTI
    if ([sender isKindOfClass: [NSButton class]]) {
        // Es handelte sich um eine Instanz der Klasse NSButton
        [self doSomethingElseWithInt: [sender tag]];
    }
 
    // Methodenabfrage: Reflexion
    if ([sender respondsToSelector: @selector( tag )]) {}
}
 
- (void) setDelegate: (id) delegate {
    // Nur Delegates akzeptieren, die ein Mindestmaß an Methoden implementieren. Der Methodensatz ist 
    // im Protokoll MyDelegateProtocol angegeben.
    if (![delegate conformsToProtocol: @protocol( MyDelegateProtocol )]) {}
}

Klassenobjekte

In Objective-C existieren nicht nur Instanzobjekte (kurz: Instanzen), sondern auch Klassenobjekte. Die Bezeichnung „Exemplar einer Klasse“ ist daher in Objective-C mehrdeutig. Klassenobjekte können jedoch keine Member-Variablen enthalten und sind stets Singletons. Klassenmethoden werden durch ein vorangestelltes + gekennzeichnet. Da es sich um Objekte handelt, können diese zugewiesen werden. Sie haben selbst den Typen Class. Es existiert eine Instanzmethode -class und eine Klassenmethode +class, um das Klassenobjekt zu erhalten.

// Eine Variable, die eine Klasse speichert
Class aShapeClass;
 
…
 
// Es können Klassenobjekte zugewiesen werden:
- (void) setShapeClass: (Class) newShapeClass {
    aShapeClass = newShapeClass;
}
 
- (void) addNewShape {
    id shape = [[aShapeClass alloc] init]; // Erzeugung einer Instanz der Klasse Shape
    [shape markForRedraw];
 
    [arrayWithAllShapes addObject: shape];
    …
}

Bemerkenswert ist in diesem Zusammenhang die Eigenschaft, dass es für diese Klassenobjekte einen self-Zeiger gibt, der der Polymorphie zugänglich ist:

@interface Shape : NSObject {
}
+ (NSRect) boundingRectForShapeRect: (NSRect) shapeRect
@end
 
@interface Circle : Shape {
}
@end
 
@implementation Circle;
// Überschreiben einer Klassenmethode der Basisklasse
+ (NSRect) boundingRectForShapeRect: (NSRect) shapeRect {
    // Subklassenmethode
    // Es existiert ein self-Zeiger, der auf Circle oder eine Subklasse von Circle zeigt. 
    [self doSomething]; 
    …
}
@endNSRect boundingRect;
 
// Nicht polymorpher Aufruf: Entspricht in etwa Shape::boundingRectForShapeRect() in C++
boundingRect = [Shape boundingRectForShapeRect: aRect]; 
 
Shape *aShape = … // Eine Instanz der Klasse Shape oder einer Subklasse wie Circle
 
// Polymorpher Aufruf einer Klassenmethode
[[aShape class] boundingRectForShapeRect: aRect]:
…

Syntax

Die Syntax von Objective-C erweitert die C-Syntax um objektorientierte Elemente. Diese Syntaxerweiterungen lehnen sich jedoch nicht an die C-Syntax an, wie es etwa viele verbreitete objektorientierte Programmiersprachen tun, sondern an die der Programmiersprache Smalltalk. Der Hintergrund ist, dass der objektorientierte Aufsatz sich grundsätzlich auch mit anderen Programmiersprachen kombinieren lässt, etwa mit Pascal zu Objective-Pascal.

Nachrichten

Auch das objektorientierte Konzept von Objective-C unterscheidet sich von dem anderer Programmiersprachen, die auf Simula zurückgehen. Es entspricht dem Konzept von Smalltalk. Anstatt den Methodenaufruf als eine Art an das Objekt gebundene Funktion zu betrachten, ist dieser Mechanismus in Objective-C das Versenden einer Nachricht.

[Objekt Nachricht]

sendet also die Nachricht, methode auszuführen, an das Objekt. Z. B. sendet

int stringLaenge = [@"Hallo Welt" length];

die Nachricht length an das String-Objekt "Hallo Welt", das mit der Länge „antwortet“.

Parameter können jeweils nach einem Doppelpunkt angegeben werden, danach kann der Methodenname weitergehen, zum Beispiel:

[NSColor colorWithCalibratedRed: 0.0 green: 0.0 blue: 0.0 alpha: 0.0];

(Der eigentliche Methodenname wäre dann colorWithCalibratedRed: green: blue: alpha:.)

Nachrichten können auch verschachtelt werden. Z. B.

NSString *string = @"Hallo Welt";
NSData *data = [NSData dataWithBytes: [string cString] length: [string cStringLength]];

Hiermit wird ein neues Objekt der Klasse NSData erstellt. Die Bytes, die in das neue Objekt kopiert werden, werden mit [string cString] erfragt, die Länge des Blocks mit [string cStringLength].

Es werden also die Rückgabewerte der Methoden verwendet. Dies geht auch beim Empfänger, zum Beispiel entspricht die Methode new diesem Aufruf:

[[Klasse alloc] init]

Das Objekt, das mit [Klasse alloc] erzeugt wurde, bekommt die Botschaft init.

Kann ein Objekt eine Nachricht nicht verstehen, so kann diese mittels Forwarding an ein anderes Objekt weitergeleitet werden.

Klassendefinition

Um seine Eigene Art von Objekten zu erstellen, muss man sie in einer Klasse beschreiben. Dazu werden im @interface-Teil – gewöhnlich in einer Header-Datei – der Zustand solcher Objekte und ihre Eigenschaften definiert, d.h. ihre Variablen und Methoden. Hier im Beispiel eine Bruch-Klasse.

@interface Bruch : NSObject {
    int zaehler;     // Zustandsgrößen, auch Attribute genannt
    int nenner;
}
- (void) printLn;    // Fähigkeiten
- (void) setZaehler: (int) z nenner: (int) n;
- (int) zaehler;
- (int) nenner;
- (float) float;
- init;              // Anfangszustand setzen
@end

Die @implementation beschreibt, wie die Fähigkeiten ausgeführt werden:

@implementation Bruch : NSObject;
- (void) printLn {
    printf("%i/%i\n", zaehler, nenner);
}
- (void) setZaehler: (int) z nenner: (int) n {
    zaehler = z;
    nenner = n;
}
- (int) zaehler {
    return zaehler;
}
- (int) nenner {
    return nenner;
}
- (float) float {
    return ((float) zaehler) / nenner;
}
- (id) init {
    if (self = [super init])
        [self setZaehler: 1 nenner: 1];
    return self;
}
@end

Ein Objekt kann nun erstellt werden, indem man die Nachricht [Klasse alloc] sendet. Die Bruch-Klasse kann man also folgendermaßen benutzen:

int main(int argc, const char **argv)
{
    Bruch *meinBruch = [[Bruch alloc] init];
    /* Achtung:
     * obige Schachtelung entspricht in etwa einem
     *    meinBruch = malloc(sizeof(Bruch));
     *    init(meinBruch); // init versucht dabei
     *                     //    if(meinBruch) meinBruch->zaehler = 1;
     *                     // d.h., ob meinBruch auf ein passendes Objekt
     *                     // zeigt, wird hier und im Folgenden nicht geprüft!
     * d.h. bei nicht erfolgter Speicherzuweisung oder anderen
     * Fehlern bei der Instanziierung der Superklasse können damit
     * zur Laufzeit gravierende Probleme entstehen!
     */
    [meinBruch setZaehler: 10 nenner: 20];
    [meinBruch printLn];                 // Ausgabe: 10/20
    printf("%f", [meinBruch float]);      // Ausgabe: 0.500000
    [meinBruch release];
    return 0;
}

Klassenobjekte

Objective-C besitzt so genannte Klassenobjekte. Dies bedeutet: Nicht nur die Instanzen, sondern auch die Klassen sind Objekte und können Nachrichten empfangen, wie oben [Klasse alloc]. – Zum Instanzieren sind damit keine zusätzlichen Sprachelemente wie Konstruktoren und Schlüsselwörter nötig.

In der Klassendefinition werden Klassenmethoden mit ‚+‘, Instanzmethoden mit ‚-‘ gekennzeichnet.

+alloc ist kein Bestandteil der Sprachbeschreibung, sondern eine – beliebige – Methode des Frameworks und daher der eigenen Implementierung zugänglich. Die Methode zu überschreiben ist jedoch nur für Experten eine gute Idee. Ganz im Gegensatz ist die Methode +initialize, die vor Verwendung einer Klasse aufgerufen wird, standardmäßig leer und kann für klassenbezogene Vorauseinstellungen überschrieben werden.

Typkonzept

Objective-C fügt zu den Standard-C-Datentypen den Datentyp id hinzu. Ein Objekt des Types id ist irgendein Objekt. Was mit diesem Objekt angefangen werden kann, wird erst zur Laufzeit bestimmt. Hierzu existieren Methoden, etwas über das Objekt zu erfahren. Wichtig sind:

  • [obj class] // bestimmt die Klasse
  • [obj respondsToSelector:] // bestimmt, ob eine Methode vorhanden ist
  • [obj conformsToProtocol:] // bestimmt, ob ein Protokoll implementiert ist

Das Typkonzept erlaubt zudem die Typisierung des allgemeinen Objektes oder anderer Objekte durch (formale) Protokolle. Diese legen einen Satz von Methoden fest. Auch diese Protokolle müssen nicht in einem Hierarchiebaum einheitlich sein, werden aber vererbt.

Die Typfreiheit erlaubt die Erstellung allgemeiner Container, die anders als templates auch typgemischt (heterogen) sein können.

NSMutableDictionary *meineWelt = [[NSMutableDictionary alloc] init];
// Eine Map mit einem String, einer URL und einem Wert
[meineWelt setObject: @"Ein Text" forKey: @"Text"];
[meineWelt setObject: [NSURL URLWithString: @"www.example.com"] forKey: @"URL"];
[meineWelt setObject: [NSNumber numberWithInt: 3] forKey: @"Zahl"];
// Alle Objekte implementiern die Methode description
NSLog( @"%@", [[meineWelt objectForKey: @"Text"] description] );
NSLog( @"%@", [[meineWelt objectForKey: @"URL"] description] );
NSLog( @"%@", [[meineWelt objectForKey: @"Zahl"] description] );
[meineWelt release];

Ausgabe: Ein Text www.example.com 3

Spätes Linken (dynamisches Linken)

Objective-C bindet einen Methodenaufruf erst zur Laufzeit an eine Methode. Grundsätzlich ist dies auch in C++ so. Jedoch muss in C++ bereits zur Übersetzungszeit sichergestellt sein, dass eine Methode existiert, weil sie zu der Klassenhierarchie gehört. C++ ist also nur bereit, innerhalb eines Zweiges einer Hierarchie spät zu binden, während Objective-C dies unabhängig von der Stellung einer Klasse in einem Hierarchiebaum macht. Dies soll an einem Syntax-neutralen Beispiel verdeutlicht werden:

Klasse A
  methode()
Klasse B von Klasse A
  methode()
Klasse C
  methode()
...
// Spätes Binden in C++
A* objekt = new B;
objekt->methode();  // Ruft in Wahrheit B::methode auf
delete objekt;
objekt = new C;     // Führt zu Fehlern, da typfremd.
objekt->methode();  // Auch mit casting geht dies nicht, weil methode()
                    // in einem anderen Zweig des Baumes auftaucht,
                    // obwohl C::methode() sehr wohl existiert
// Spätes Binden in oC
id objekt = [[B alloc] init];  // id bedeutet „Irgend ein Objekt“
[objekt methode];  // Funktioniert, weil B methode() kennt
[objekt release];
objekt = [[C alloc] init];
[objekt methode];  // Funktioniert auch, weil C die methode() kennt
                    // Wo sie in der Klassenhierarchie bekannt ist,
                    // spielt keinerlei Rolle

Zu diesem Zweck führt Objective-C zur Laufzeit umfangreiche Informationen über ein Objekt mit, was über RTTI hinausgeht.

Aus dem letztlich gleichen Grunde ist es auch ohne weiteres möglich, zur Laufzeit erst eine Methode zu bestimmen. Die Methode kann sich ähnlich wie bei einem Methodenzeiger in einer Variablen befinden, die erst zur Laufzeit mit einem Wert besetzt wird:

methode = @selector( eineMethode );
[objekt performSelector: methode]; // Ruft die Methode eineMethode auf
methode = @selector( andereMethode );
[objekt performSelector: methode]; // Ruft andereMethode auf

Von dieser Technik macht etwa Cocoa bei der Bindung von Interface-Elementen an Methoden regen Gebrauch. Die Bestimmung der Methode kann auch durch ihren Klarnamen erfolgen.

Programmbeispiel

NSMutableDictionary *aDictionary = [[NSMutableDictionary alloc] init];
 
[aDictionary setObject: @"Ei" forKey: @"egg"];
[aDictionary setObject: @"Auto" forKey: @"car"];
[aDictionary setObject: @"Hallo Welt!" forKey: @"Hello, world!"];
 
NSLog([aDictionary objectForKey: @"Hello, world!"]);
 
[aDictionary release];

Objective-C++

Objective-C++ ist ein Frontend der GNU Compiler Collection das Quellen übersetzen kann, die sowohl C++- als auch Objective-C-Syntax verwenden. Objective-C++ versucht nicht, die unterschiedlichen Konzepte der beiden Sprachen zu vereinheitlichen. Vielmehr erweitert es lediglich C++ um die Erweiterungen, die Objective-C zu C hinzufügt. Dies führt zu gewissen Einschränkungen:

  • C++-Klassen können nicht von Objective-C-Klassen erben.
  • Umgekehrt können Objective-C-Klassen auch nicht von C++-Klassen erben.
  • Innerhalb einer Objective-C-Deklaration kann man keine C++-Namensräume deklarieren.
  • C++-Klassen, die keinen Defaultkonstruktor haben, oder die eine oder mehrere virtuelle Funktionen haben, können nicht als Instanzvariablen einer Objective-C-Klasse verwendet werden. Wohl aber kann man Zeiger auf solche Klassen verwenden.
  • Objective-C-Objekte können nicht als Wertparameter verwendet werden, da sie nur über Zeiger ansprechbar sind.
  • Objective-C-Deklarationen können nicht in C++-Template-Deklarationen benutzt werden, und umgekehrt. Jedoch können Objetive-C-Typen (wie z.B. Klassenname *) als C++-Template-Parameter benutzt werden.
  • Die Ausnahmebehandlungsmechanismen von Objective-C und C++ sind voneinander unabhängig; Ein Objective-C-Exception-Handler kann keine C++-Exceptions auffangen und umgekehrt. Dies gilt allerdings nicht mehr für das 64-Bit-Runtime von Objective-C 2.0, da hier das Exceptionkonzept von C++ übernommen wurde.
  • Besondere Vorsicht erfordert die Tatsache, dass die Konventionen zum Aufruf von Destruktoren nicht kompatibel sind. So wird etwa ein C++-Destruktor nicht aufgerufen, wenn der Gültigkeitsbereich des C++-Objekts mittels einer Objective-C-Ausnahme verlassen wird.

Ein typischer Anwendungsbereich von Objective-C++ ist zum Beispiel die Verwendung einer C++-Bibliothek in einer Objective-C-Anwendung.

Objective-C 2.0

Im Oktober 2007 hat Apple mit Mac OS X 10.5 Objective-C 2.0 ausgeliefert, eine Erweiterung von Objective-C, die „moderne Garbage Collection, Syntaxerweiterungen, Verbesserungen der Laufzeitleistung und 64-Bit-Unterstützung umfasst“.[1]

Die GNU Laufzeitumgebung für Objective-C unterstützt diese Erweiterungen derzeit (Mai 2008) jedoch noch nicht.

Garbage Collection

Objective-C 2.0 enthält einen optionalen konservativen Garbage Collector.

Properties

Objective-C 2.0 führt eine neue Syntax ein, mit der man Instanzvariablen als Properties deklarieren kann. Die Syntax unterstützt optionale Attribute, welche die Zugriffsmethoden näher definieren: Eine Property kann als readonly deklariert werden, und kann mit verschiedenen Verhaltensregeln zur Speicherverwaltung wie „assign“, „copy“, „retain“ versehen werden.

@interface Person : NSObject {
    @public NSString *name;
    @private int age;
}
@property(copy) NSString *name;
@property(readonly) int age;
-(id)initWithAge:(int)age;
@end

Properties werden mit dem Schlüsselwort @synthesize implementiert, welches Getter und Setter-Method gemäß der Property-Deklaration erzeugt. Alternativ kann man auch das Schlüsselwort @dynamic verwenden, um anzuzeigen, dass man diese Methoden selber implementiert. In diesem Falle ist es jedoch optional, da der Compiler die Implementierung der Accessoren bemerken kann und bemerkt. Wichtiger ist das Schlüsselwort @dynamic, wenn die Accessoren erst zur Laufzeit erzeugt werden. Denn hier würde der Compiler eine entsprechende Definition vermissen und eine Warning ausgeben. Dies ist etwa bei Managed Objects der Fall, die zur Laufzeit Accessoren aus der geladenen Beschreibungsstruktur der Entität erzeugen.

@implementation Person
@synthesize name;
@dynamic age;
-(id)initWithAge:(int)initAge
{
    age = initAge; // Dies ist ein direkter Zugriff auf die Instanzvariable, der Property-Setter wird hier nicht verwendet
    return self;
}
-(int)age
{
    return 29; // Beispiel: lügt bezüglich des Alters
}
@end

Dot-Notation

Auf Accessoren, gleichgültig ob explizit definiert oder als Property angelegt, kann mit der aus anderen Sprachen bekannten Punkt-Notation zugegriffen werden. Hierbei erfolgt allerdings kein unkontrollierter Zugriff auf die Instanzvariable, sondern es werden die Accessoren -eigenschaft und -setEigenschaft: verwendet.

Person *aPerson = [[Person alloc] initWithAge: 53];
aPerson.name = @"Steve"; // Punkt-Notation, verwendet den Setter
NSLog(@"Zugriff per Getter (%@), Property-Name (%@) und Direktzugriff auf die Instanzvariable (%@)",
      aPerson.name, [aPerson valueForKey:@"name"], aPerson->name);

Um die Punktnotation innerhalb einer Instanzmethode zu verwenden, benutzt man das Schlüsselwort self:

-(void) introduceMyselfWithDotNotation
{
    NSLog(@"Hallo, mein Name ist %@.", self.name); // verwendet Getter
    NSLog(@"Hallo, mein Name ist %@.", name); // Direktzugriff auf die Instanzvariable
}

Auf die Eigenschaften einer Instanz kann auch dynamisch per Reflexion zugegriffen werden:

int i, propertyCount = 0;
objc_property_t *propertyList = class_copyPropertyList([aPerson class], &propertyCount);
for (i=0; i<propertyCount; i++) {
    objc_property_t *thisProperty = propertyList + i;
    const char* propertyName = property_getName(*thisProperty);
    NSLog(@"Person hat eine Property: '%s'", propertyName);
}

Schnelle Enumeration

Objective-C 2.0 stellt die for-in-Syntax zur Verfügung, um durch eine Kollektion zu iterieren. Die folgende Schleife:

Person* person;
NSEnumerator* personsEnum = [thePeople objectEnumerator]; // NSArray, NSSet, NSDictionay
while( (person = [personsEnum nextObject]) ) {
    NSLog(@"%@ ist %i Jahre alt.", [person name], [person age]);
}

kann daher in Objective-C 2.0 kürzer so formuliert werden:

for (Person *p in thePeople)
    NSLog(@"%@ ist %i Jahre alt.", [p getName], [p getAge]);

Folgen für die Cocoa-Entwicklung

In Objective-C geschriebene Mac-OS-X-Anwendungen, die diese Objective-C-2.0-Verbesserungen benutzen, sind mit Versionen von Mac OS X vor 10.5 (Leopard) inkompatibel.

Vergleich zu anderen Programmiersprachen

C++

Obwohl beide Sprachen in C einen gemeinsamen Vorfahren haben, ergeben sich insbesondere aus dem späten Binden und der Möglichkeit, Klassen und Nachrichten zur Laufzeit zu erstellen, erhebliche Konzeptunterschiede.

Funktionalitätsspezialisierung

Ein häufiges Grundproblem der klassenbasierten Programmierung ist die Spezialisierung einer Standardfunktionalität. Das Mittel der Wahl ist in C++ meist die Ableitung. Hiergegen wird eingewendet, dass bei der Ableitung "Innereien" der Basisklasse bekannt werden (White-Boxing). Demgegenüber lässt sich in Objective-C leicht das Delegating implementieren, welches eine Funktionsspezialisierung durch Nachfrage bei einem Objekt einer anderen Klasse zulässt. Die zu spezialisierende Klasse bleibt also gekapselt (Black-Boxing).

Dies sei an einem Beispiel verdeutlicht: Es sei ein Tableview (eine Darstellung einer Tabelle mit Zeilen und Spalten im UI), welches Selektion zulässt. Man nehme ferner an, dass bei der Selektion eine zentrale Methode -selectRow: aufgerufen wird. Diese ist berechtigt, die Selektion abzulehnen.

Bei einer Lösung mittels Ableitung würde man die Methode -selectRow: in einer Subklasse MyTableView : NSTableView spezialisieren:

- (BOOL)selectRow:(NSInteger)rowIndex
{
  // Die erste Zeile gruppiert nur und darf nicht selektiert werden.
  if( rowIndex == 0 ) {
    return NO;
  } else {
    return YES;
  }
}

Bei einem Delegate befragt indessen das Tableview sein Delegate. Dieses ist von irgendeiner Klasse:

- (BOOL)tableview:(NSTableView*)tableView shouldSelectRow:(NSInteger)rowIndex 
{
  // Die erste Zeile gruppiert nur und darf nicht selektiert werden.
  if( rowIndex == 0 ) {
    return NO;
  } else {
    return YES;
  }
}

Der gleiche Code wird also aus der Klasse ausgelagert. Ähnlich wie bei der Ableitung ist dabei die Implementierung einer Delegate-Methode in der Regel ein Angebot, keine Pflicht. In diesem Falle würde also das Tableview nachfragen, ob sein Delegate die Methode -tableView:shouldSelectRow: implementiert:

// Pseudocode NSTableView.m
- (BOOL)selectRow:(NSInteger)rowIndex {
   SEL method = @selector( tableView:shouldSelectRow: );
 
   // Nur bei Implementierung
   if( [delegate respondsToSelector:method ) {
      return [delegate tableView:self shouldSelectRow:rowIndex];
   // Standardverhalten
   } else {
      return YES;
   }
}

Diese Fähigkeit von Objective-C wird in weiten Teilen von Cocoa ausgenutzt.

Funktionalitätserweiterung

Auch die Erweiterung einer bestehenden Klassenfunktionalität wird in C++ häufig über Subklassen implementiert. Objective-C bietet demgegenüber Kategorien als Lösungsmöglichkeit an. Hierbei wird nachträglich der Methodensatz einer Klasse erweitert.

Literatur

  • Cocoa: Progammierung für Mac OS X, 3. Ausgabe, Aaron Hillegass, Mitp-Verlag, 2008, Deutsch, ISBN 3-8266-5960-0
  • Programming in Objective-C, Stephen Kochan, Sams, 2003, Englisch, ISBN 0-672-32586-1
  • Object-Oriented Programming and the Objective C Language, NeXTSTEP Developers Library Release 3, Addison-Wesley, 1993, ISBN 0-201-63251-9, 240 Seiten
  • Object-Oriented Programming: an evolutionary approach, Brad J. Cox and Andrew J. Novobilski, Addison-Wesley, 1986, 1991 (2nd Edition), ISBN 0-201-54834-8, 270 Seiten. Einführung vom Autor dieser Sprache.
  • Objective-C und Cocoa: Programmieren unter Apple Mac OS X, Klaus Rodewig, Amin Negm-Awad, 2. Auflage, SmartBooks Verlag, CH-Kilchberg, 2007, ISBN 978-3-908497-42-4
  • Amin Negm-Awad, "Objective-C und Cocoa – Band 1:Grundlagen".1./3. Auflage. SmartBooks Verlag, CH-Kilchberg, 2008, ISBN 978-3908497-82-0

Quellen

  1. http://developer.apple.com/leopard/overview/objectivec2.html Apple - Objective-C 2.0 Overview

Weblinks

Dieser Artikel oder Abschnitt bedarf einer Überarbeitung. Näheres ist auf der Diskussionsseite angegeben. Hilf mit, ihn zu verbessern, und entferne anschließend diese Markierung.

Wikimedia Foundation.

Schlagen Sie auch in anderen Wörterbüchern nach:

  • Objective-C — Paradigm(s) reflective, object oriented Appeared in 1983 Designed by Tom Love Brad Cox Developer Apple Inc. Typing discipline …   Wikipedia

  • Objective-C — Entwickler: Brad Cox, Tom Love Typisierung: stark, statisch, explizit Einflüsse: Smalltalk, C (Programmiersprache) Betriebssystem: Mac OS X …   Deutsch Wikipedia

  • Objective-C++ — Objective C, auch kurz ObjC genannt, erweitert die Programmiersprache C um Sprachmittel zur objektorientierten Programmierung. Objective C++ erlaubt teilweise die Mischung von Objective C mit C++ Code mit dem Ziel, älteren Code verwenden zu… …   Deutsch Wikipedia

  • Objective C — Objective C, auch kurz ObjC genannt, erweitert die Programmiersprache C um Sprachmittel zur objektorientierten Programmierung. Objective C++ erlaubt teilweise die Mischung von Objective C mit C++ Code mit dem Ziel, älteren Code verwenden zu… …   Deutsch Wikipedia

  • Objective C++ — Objective C, auch kurz ObjC genannt, erweitert die Programmiersprache C um Sprachmittel zur objektorientierten Programmierung. Objective C++ erlaubt teilweise die Mischung von Objective C mit C++ Code mit dem Ziel, älteren Code verwenden zu… …   Deutsch Wikipedia

  • Objective-C — Класс языка: объектно ориентированный, мультипарадигмальный: рефлексивно ориентированный Появился в: 1986 Автор(ы): Бред Кокс Типизация данных: нестрогая, статическая / динамическая …   Википедия

  • Objective-C — Información general Paradigma orientado a objetos Apareció en 1980 Diseñado por Brad Cox Tipo de dato …   Wikipedia Español

  • Objective-J — Paradigm(s) Multi paradigm: reflective, object oriented, functional, imperative, scripting Appeared in 2008 Developer 280 North, Inc. Typing discipline dynamic, weak …   Wikipedia

  • Objective-C — Apparu en 1983 Auteur Brad Cox et Tom Love Paradigme orienté objet, réflexif …   Wikipédia en Français

  • Objective-J — Класс языка: мультипарадигмальный: рефлексивно ориентированное, объектно ориентированное, функциональное, императивное, скриптовое Появился в: 2008 Типизация данных: динамическая, слабая, утиная …   Википедия