Ziel c - Objective-C

Ziel c
Familie C
Entworfen von Tom Love und Brad Cox
Erstmals erschienen 1984 ; Vor 37 Jahren ( 1984 )
Stabile Version
2.0
Schreibdisziplin statisch , dynamisch , schwach
Betriebssystem Plattformübergreifend
Dateinamenerweiterungen .h, .m, .mm, .M
Webseite entwickler.apple.com
Wichtige Implementierungen
Clang , GCC
Beeinflusst von
C , Smalltalk
Beeinflusst
Groovy , Java , Nu , Objective-J , TOM , Swift

Objective-C ist eine universelle , objektorientierte Programmiersprache , die der Programmiersprache C Smalltalk- ähnliche Nachrichtenübermittlung hinzufügt . Ursprünglich von Brad Cox und Tom Love in den frühen 1980er Jahren entwickelt, wurde es von NeXT für sein NeXTSTEP- Betriebssystem ausgewählt. Objective-C war bis zur Einführung von Swift im Jahr 2014 die von Apple unterstützte Standardprogrammiersprache für die Entwicklung von macOS (das von NeXTSTEP abstammt) und iOS- Anwendungen unter Verwendung ihrer jeweiligen Anwendungsprogrammierschnittstellen (APIs), Cocoa und Cocoa Touch .

Objective-C-Programme, die für Nicht-Apple-Betriebssysteme entwickelt wurden oder nicht von Apples APIs abhängig sind, können auch für jede Plattform kompiliert werden , die von GNU GCC oder LLVM / Clang unterstützt wird .

Objective-C-Quellcode- 'Messaging /Implementation'-Programmdateien haben normalerweise die Dateinamenerweiterung .m , während Objective-C- 'Header /Interface'-Dateien die Erweiterung .h haben , genau wie C- Header-Dateien. Objective-C++-Dateien werden mit einer .mm- Dateierweiterung gekennzeichnet.

Geschichte

Objective-C wurde in erster Linie von Brad Cox und Tom Love in den frühen 1980er Jahren in ihrer Firma Productivity Products International (PPI) entwickelt .

Im Vorfeld der Gründung ihres Unternehmens hatten beide eingeführt Smalltalk , während bei ITT Corporation ‚s Programmierung Technology Center im Jahr 1981. Die früheste Arbeit auf Objective-C Spuren um diese Zeit zurück. Cox war fasziniert von Problemen der echten Wiederverwendbarkeit bei Softwaredesign und -programmierung. Er erkannte, dass eine Sprache wie Smalltalk beim Aufbau von Entwicklungsumgebungen für Systementwickler bei ITT von unschätzbarem Wert sein würde. Er und Tom Love erkannten jedoch auch, dass die Abwärtskompatibilität mit C im Telekommunikations-Engineering-Umfeld von ITT von entscheidender Bedeutung war.

Cox begann, einen Präprozessor für C zu schreiben , um einige der Fähigkeiten von Smalltalk hinzuzufügen. Er hatte bald eine funktionierende Implementierung einer objektorientierten Erweiterung der C- Sprache, die er "OOPC" für Object-Oriented Pre-Compiler nannte. Love wurde 1982 von Schlumberger Research eingestellt und hatte die Gelegenheit, die erste kommerzielle Kopie von Smalltalk-80 zu erwerben, die die Entwicklung ihrer Idee weiter beeinflusste. Um zu demonstrieren, dass echte Fortschritte erzielt werden können, zeigte Cox, dass die Herstellung austauschbarer Softwarekomponenten nur wenige praktische Änderungen an bestehenden Tools erfordert. Insbesondere mussten sie Objekte flexibel unterstützen, mit einem verwendbaren Satz von Bibliotheken geliefert werden und es ermöglichen, den Code (und alle vom Code benötigten Ressourcen) in einem plattformübergreifenden Format zu bündeln.

Love und Cox gründeten schließlich PPI, um ihr Produkt zu kommerzialisieren, das einen Objective-C-Compiler mit Klassenbibliotheken koppelte. 1986 veröffentlichte Cox die Hauptbeschreibung von Objective-C in seiner ursprünglichen Form in dem Buch Object-Oriented Programming, An Evolutionary Approach . Obwohl er sorgfältig darauf hinwies, dass das Problem der Wiederverwendbarkeit mehr ist als nur das, was Objective-C bietet, wurde die Sprache oft Feature für Feature mit anderen Sprachen verglichen.

Popularisierung durch NeXT

1988 lizenzierte NeXT Objective-C von StepStone (der neue Name von PPI, dem Eigentümer der Objective-C-Marke) und erweiterte den GCC- Compiler, um Objective-C zu unterstützen. NeXT entwickelte die AppKit- und Foundation Kit- Bibliotheken, auf denen die NeXTSTEP- Benutzeroberfläche und der Interface Builder basierten. Während die NeXT-Workstations keinen großen Einfluss auf den Markt hatten, wurden die Tools in der Branche weithin gelobt. Dies führte dazu, dass NeXT die Hardwareproduktion aufgab und sich auf Softwaretools konzentrierte und NeXTSTEP (und OPENSTEP) als Plattform für kundenspezifische Programmierung verkaufte.

Um die Bedingungen der GPL zu umgehen , hatte NeXT ursprünglich beabsichtigt, das Objective-C-Frontend separat zu liefern, sodass der Benutzer es mit GCC verknüpfen kann, um die ausführbare Compiler-Datei zu erstellen. Obwohl er ursprünglich von Richard M. Stallman akzeptiert wurde , wurde dieser Plan abgelehnt, nachdem Stallman sich mit den Anwälten von GNU beraten hatte und NeXT zustimmte, Objective-C in GCC aufzunehmen.

Die Arbeiten zur Erweiterung des GCC wurden von Steve Naroff geleitet, der von StepStone zu NeXT kam. Die Compiler-Änderungen wurden gemäß den GPL- Lizenzbedingungen zur Verfügung gestellt , die Laufzeitbibliotheken jedoch nicht, wodurch der Open-Source-Beitrag für die breite Öffentlichkeit unbrauchbar wurde. Dies führte dazu, dass andere Parteien solche Laufzeitbibliotheken unter Open-Source-Lizenz entwickelten. Später war Steve Naroff auch maßgeblich an der Arbeit bei Apple beteiligt, um das Objective-C-Frontend für Clang zu entwickeln .

Das GNU- Projekt begann mit der Arbeit an seiner freien Software-Implementierung von Cocoa mit dem Namen GNUstep , die auf dem OpenStep- Standard basiert . Dennis Glatting schrieb 1992 die erste GNU Objective-C- Laufzeit . Die seit 1993 im Einsatz befindliche GNU Objective-C-Laufzeit wurde von Kresten Krab Thorup während seines Studiums in Dänemark entwickelt . Thorup arbeitete auch bei NeXT von 1993 bis 1996.

Apple-Entwicklung und Swift

Nach dem Erwerb von NeXT 1996 Apple Computer verwendete Openstep in seinem damals neuen Betriebssystem Mac OS X . Dazu gehörten Objective-C, NeXTs Objective-C-basiertes Entwicklertool Project Builder , und sein Interface-Design-Tool Interface Builder . Beide wurden später zu einer Anwendung, Xcode, zusammengeführt . Der Großteil der aktuellen Cocoa-API von Apple basiert auf OpenStep-Schnittstellenobjekten und ist die wichtigste Objective-C-Umgebung, die für die aktive Entwicklung verwendet wird.

Auf der WWDC 2014 stellte Apple mit Swift eine neue Sprache vor, die als „Objective-C without the C“ bezeichnet wurde.

Syntax

Objective-C ist eine dünne Schicht über C und ist eine "strenge Obermenge " von C, was bedeutet, dass es möglich ist, jedes C-Programm mit einem Objective-C-Compiler zu kompilieren und C-Sprachcode frei in eine Objective-C-Klasse einzubinden.

Objective-C leitet seine Objektsyntax von Smalltalk ab . Die gesamte Syntax für nicht objektorientierte Operationen (einschließlich primitiver Variablen, Vorverarbeitung, Ausdrücke, Funktionsdeklarationen und Funktionsaufrufe) ist identisch mit der von C, während die Syntax für objektorientierte Funktionen eine Implementierung von Smalltalk ist. Stilnachrichten.

Mitteilungen

Das Objective-C-Modell der objektorientierten Programmierung basiert auf der Nachrichtenweitergabe an Objektinstanzen. In Objective-C ruft man keine Methode auf ; man sendet eine Nachricht . Dies unterscheidet sich vom Programmiermodell im Simula- Stil, das von C++ verwendet wird . Der Unterschied zwischen diesen beiden Konzepten besteht darin, wie der Code ausgeführt wird, auf den durch den Methoden- oder Nachrichtennamen verwiesen wird. In einer Sprache im Simula-Stil wird der Methodenname in den meisten Fällen vom Compiler an einen Codeabschnitt in der Zielklasse gebunden . In Smalltalk und Objective-C wird das Ziel einer Nachricht zur Laufzeit aufgelöst, wobei das empfangende Objekt die Nachricht selbst interpretiert. Ein Verfahren wird von einem identifizierten Selektor oder SEL - eine eindeutige Kennung für jeden Nachrichtennamen, oft nur ein NUL -terminierte Zeichenkette den Namen darstellt - und auf einen C - Methode aufgelöst Zeiger es Implementieren: eine IMP . Dies hat zur Folge, dass das Message-Passing-System keine Typprüfung hat. Es ist nicht garantiert, dass das Objekt, an das die Nachricht gerichtet ist, der Empfänger , auf eine Nachricht antwortet, und wenn dies nicht der Fall ist, wird eine Ausnahme ausgelöst.

Das Senden der Message- Methode an das Objekt, auf das der Zeiger obj zeigt, würde den folgenden Code in C++ erfordern :

obj->method(argument);

In Objective-C wird dies wie folgt geschrieben:

[obj method:argument];

Der Aufruf "method" wird vom Compiler in die Familie von Laufzeitfunktionen objc_msgSend(id self, SEL op, ...) übersetzt . Verschiedene Implementierungen handhaben moderne Ergänzungen wie super . In GNU-Familien heißt diese Funktion objc_msg_sendv , sie wurde jedoch zugunsten eines modernen Lookup-Systems unter objc_msg_lookup veraltet .

Beide Programmierstile haben ihre Stärken und Schwächen. Die objektorientierte Programmierung im Simula- Stil ( C++ ) ermöglicht eine Mehrfachvererbung und eine schnellere Ausführung, indem nach Möglichkeit die Bindung zur Kompilierzeit verwendet wird , aber sie unterstützt standardmäßig keine dynamische Bindung . Es erzwingt auch, dass alle Methoden eine entsprechende Implementierung haben, es sei denn, sie sind abstrakt . Die Programmierung im Smalltalk-Stil, wie sie in Objective-C verwendet wird, ermöglicht es, dass Nachrichten nicht implementiert werden, wobei die Methode zur Laufzeit in ihre Implementierung aufgelöst wird. Beispielsweise kann eine Nachricht an eine Sammlung von Objekten gesendet werden, auf die nur einige antworten werden, ohne befürchten zu müssen, Laufzeitfehler zu erzeugen. Die Nachrichtenweitergabe erfordert auch nicht, dass ein Objekt zur Kompilierzeit definiert wird. Für den Aufruf der Methode im abgeleiteten Objekt ist noch eine Implementierung erforderlich. ( Weitere Vorteile der dynamischen (späten) Bindung finden Sie im Abschnitt zur dynamischen Typisierung weiter unten.)

Schnittstellen und Implementierungen

Objective-C erfordert, dass sich die Schnittstelle und die Implementierung einer Klasse in separat deklarierten Codeblöcken befinden. Konventionell platzieren Entwickler die Schnittstelle in einer Headerdatei und die Implementierung in einer Codedatei. Die Header-Dateien, normalerweise mit dem Suffix .h, ähneln C-Header-Dateien, während die Implementierungs-(Methoden-)Dateien, normalerweise mit dem Suffix .m, C-Code-Dateien sehr ähnlich sein können.

Schnittstelle

Dies ist analog zu Klassendeklarationen, wie sie in anderen objektorientierten Sprachen wie C++ oder Python verwendet werden.

Die Schnittstelle einer Klasse wird normalerweise in einer Header-Datei definiert. Eine übliche Konvention ist, die Header-Datei nach dem Namen der Klasse zu benennen , zB würde Ball.h die Schnittstelle für die Klasse Ball enthalten .

Eine Schnittstellendeklaration hat die Form:

@interface classname : superclassname {
  // instance variables
}
+ classMethod1;
+ (return_type)classMethod2;
+ (return_type)classMethod3:(param1_type)param1_varName;

- (return_type)instanceMethod1With1Parameter:(param1_type)param1_varName;
- (return_type)instanceMethod2With2Parameters:(param1_type)param1_varName
                              param2_callName:(param2_type)param2_varName;
@end

Oben bezeichnen Pluszeichen Klassenmethoden oder Methoden, die für die Klasse selbst (nicht für eine Instanz) aufgerufen werden können , und Minuszeichen bezeichnen Instanzmethoden , die nur für eine bestimmte Instanz der Klasse aufgerufen werden können. Klassenmethoden haben auch keinen Zugriff auf Instanzvariablen .

Der obige Code entspricht ungefähr der folgenden C++- Schnittstelle:

class classname : public superclassname {
protected:
  // instance variables

public:
  // Class (static) functions
  static void *classMethod1();
  static return_type classMethod2();
  static return_type classMethod3(param1_type param1_varName);

  // Instance (member) functions
  return_type instanceMethod1With1Parameter(param1_type param1_varName);
  return_type
  instanceMethod2With2Parameters(param1_type param1_varName,
                                 param2_type param2_varName = default);
};

Beachten Sie, dass instanceMethod2With2Parameters:param2_callName: die Verschachtelung von Selektorsegmenten mit Argumentausdrücken demonstriert, für die es in C/C++ keine direkte Entsprechung gibt.

Rückgabetypen können ein beliebiger Standard- C- Typ, ein Zeiger auf ein generisches Objective-C-Objekt, ein Zeiger auf einen bestimmten Objekttyp wie NSArray *, NSImage * oder NSString * oder ein Zeiger auf die Klasse sein, zu der die Methode gehört (Instanztyp). Der Standardrückgabetyp ist die generische Objective-C-Typ- ID .

Methodenargumente beginnen mit einem Namen, der das Argument bezeichnet, das Teil des Methodennamens ist, gefolgt von einem Doppelpunkt, gefolgt vom erwarteten Argumenttyp in Klammern und dem Argumentnamen. Das Etikett kann weggelassen werden.

- (void)setRangeStart:(int)start end:(int)end;
- (void)importDocumentWithName:(NSString *)name
      withSpecifiedPreferences:(Preferences *)prefs
                    beforePage:(int)insertPage;

Eine Ableitung der Interface-Definition ist die Kategorie , die es erlaubt, Methoden zu bestehenden Klassen hinzuzufügen.

Implementierung

Die Schnittstelle deklariert nur die Klassenschnittstelle und nicht die Methoden selbst: Der eigentliche Code wird in die Implementierungsdatei geschrieben. Implementierungs-(Methoden-)Dateien haben normalerweise die Dateierweiterung .m, die ursprünglich "Nachrichten" bedeutete.

@implementation classname
+ (return_type)classMethod {
  // implementation
}
- (return_type)instanceMethod {
  // implementation
}
@end

Methoden werden mit ihren Schnittstellendeklarationen geschrieben. Vergleich von Objective-C und C:

- (int)method:(int)i {
  return [self square_root:i];
}
int function(int i) {
  return square_root(i);
}

Die Syntax erlaubt die Pseudobenennung von Argumenten .

- (void)changeColorToRed:(float)red green:(float)green blue:(float)blue {
  //... Implementation ...
}

// Called like so:
[myColor changeColorToRed:5.0 green:2.0 blue:6.0];

Interne Darstellungen einer Methode variieren zwischen verschiedenen Implementierungen von Objective-C. Wenn myColor von der Klasse Color ist , kann die Instanzmethode -changeColorToRed:green:blue: intern mit _i_Color_changeColorToRed_green_blue bezeichnet werden . Das i soll auf eine Instanzmethode verweisen, wobei die Klassen- und dann Methodennamen angehängt und Doppelpunkte in Unterstriche geändert werden. Da die Reihenfolge der Parameter Teil des Methodennamens ist, kann sie nicht wie bei echten benannten Parametern an den Codierungsstil oder Ausdruck angepasst werden.

Interne Namen der Funktion werden jedoch selten direkt verwendet. Im Allgemeinen werden Nachrichten in Funktionsaufrufe konvertiert, die in der Objective-C-Laufzeitbibliothek definiert sind. Es ist zur Linkzeit nicht unbedingt bekannt, welche Methode aufgerufen wird, da die Klasse des Empfängers (das Objekt, an das die Nachricht gesendet wird) erst zur Laufzeit bekannt sein muss.

Instanziierung

Sobald eine Objective-C-Klasse geschrieben ist, kann sie instanziiert werden. Dies geschieht, indem zuerst eine nicht initialisierte Instanz der Klasse (ein Objekt) zugewiesen und dann initialisiert wird. Ein Objekt ist erst dann voll funktionsfähig, wenn beide Schritte abgeschlossen sind. Diese Schritte sollten mit einer Codezeile ausgeführt werden, damit es nie ein zugewiesenes Objekt gibt, das nicht initialisiert wurde (und weil es unklug ist, das Zwischenergebnis beizubehalten, da -initein anderes Objekt als das Objekt zurückgegeben werden kann, für das es aufgerufen wird).

Instanziierung mit dem standardmäßigen, parameterlosen Initialisierer:

MyObject *foo = [[MyObject alloc] init];

Instanziierung mit einem benutzerdefinierten Initialisierer:

MyObject *foo = [[MyObject alloc] initWithString:myString];

Für den Fall, dass keine benutzerdefinierte Initialisierung durchgeführt wird, kann oft die Methode "new" anstelle der alloc-init-Nachrichten verwendet werden:

MyObject *foo = [MyObject new];

Außerdem implementieren einige Klassen Klassenmethodeninitialisierer. Wie +new, kombinieren sie +allocund -init, aber im Gegensatz zu +new, geben sie eine automatisch freigegebene Instanz zurück. Einige Initialisierer für Klassenmethoden verwenden Parameter:

MyObject *foo = [MyObject object];
MyObject *bar = [MyObject objectWithString:@"Wikipedia :)"];

Die alloc- Nachricht weist genügend Speicher zu, um alle Instanzvariablen für ein Objekt aufzunehmen, setzt alle Instanzvariablen auf Nullwerte und verwandelt den Speicher in eine Instanz der Klasse; Zu keinem Zeitpunkt während der Initialisierung ist der Speicher eine Instanz der Oberklasse.

Die Init- Nachricht führt die Einrichtung der Instanz bei der Erstellung durch. Die init- Methode wird oft wie folgt geschrieben:

- (id)init {
    self = [super init];
    if (self) {
        // perform initialization of object here
    }
    return self;
}

Beachten Sie im obigen Beispiel den idRückgabetyp. Dieser Typ steht in Objective-C für "Zeiger auf ein beliebiges Objekt" (siehe Abschnitt " Dynamische Typisierung ").

Das Initialisierungsmuster wird verwendet, um sicherzustellen, dass das Objekt von seiner Superklasse richtig initialisiert wird, bevor die init-Methode ihre Initialisierung durchführt. Es führt die folgenden Aktionen aus:

  1. selbst = [Superinit]
    Sendet der Superklasseninstanz eine Init- Nachricht und weist das Ergebnis self (Zeiger auf das aktuelle Objekt) zu.
  2. wenn (selbst)
    Prüft, ob der zurückgegebene Objektzeiger gültig ist, bevor eine Initialisierung durchgeführt wird.
  3. kehre selbst zurück
    Gibt den Wert von self an den Aufrufer zurück.

Ein ungültiger Objektzeiger hat den Wert nil ; Bedingte Anweisungen wie "if" behandeln nil wie einen Nullzeiger, sodass der Initialisierungscode nicht ausgeführt wird, wenn [super init] nil zurückgibt. Wenn bei der Initialisierung ein Fehler auftritt, sollte die init-Methode alle erforderlichen Bereinigungen durchführen, einschließlich des Sendens einer "release"-Nachricht an self und nil zurückgeben, um anzuzeigen, dass die Initialisierung fehlgeschlagen ist. Eine Überprüfung auf solche Fehler darf erst nach Aufruf der Superklassen-Initialisierung durchgeführt werden, um sicherzustellen, dass das Objekt korrekt zerstört wird.

Wenn eine Klasse mehr als eine Initialisierungsmethode hat, muss nur eine davon (der "designierte Initialisierer") diesem Muster folgen; andere sollten den designierten Initialisierer anstelle des Superklasseninitialisierers aufrufen.

Protokolle

In anderen Programmiersprachen werden diese "Schnittstellen" genannt.

Objective-C wurde bei NeXT erweitert , um das Konzept der mehrfachen Vererbung der Spezifikation, aber nicht der Implementierung, durch die Einführung von Protokollen einzuführen . Dies ist ein Muster, das entweder als abstrakte mehrfach vererbte Basisklasse in C++ oder als "Schnittstelle" (wie in Java und C# ) erreichbar ist. Objective-C verwendet Ad-hoc-Protokolle, die als informelle Protokolle bezeichnet werden, und vom Compiler erzwungene Protokolle, die als formale Protokolle bezeichnet werden .

Ein informelles Protokoll ist eine Liste von Methoden, die eine Klasse implementieren kann. Es ist in der Dokumentation angegeben, da es in der Sprache nicht vorhanden ist. Informelle Protokolle werden als Kategorie (siehe unten) auf NSObject implementiert und enthalten oft optionale Methoden, die bei Implementierung das Verhalten einer Klasse ändern können. Beispielsweise kann eine Textfeldklasse einen Delegaten haben , der ein informelles Protokoll mit einer optionalen Methode zum Ausführen der automatischen Vervollständigung von benutzertypisiertem Text implementiert. Das Textfeld ermittelt, ob der Delegat diese Methode implementiert (über Reflection ) und ruft in diesem Fall die Methode des Delegate auf, um die Funktion zur automatischen Vervollständigung zu unterstützen.

Ein formales Protokoll ähnelt einer Schnittstelle in Java, C# und Ada 2005 . Es ist eine Liste von Methoden, die jede Klasse selbst deklarieren kann, um sie zu implementieren. Versionen von Objective-C vor 2.0 erforderten, dass eine Klasse alle Methoden in einem Protokoll implementieren muss, das sie selbst als übernehmen deklariert; der Compiler gibt einen Fehler aus, wenn die Klasse nicht jede Methode aus ihren deklarierten Protokollen implementiert. Objective-C 2.0 hat Unterstützung für das Markieren bestimmter Methoden in einem Protokoll als optional hinzugefügt, und der Compiler erzwingt die Implementierung optionaler Methoden nicht.

Eine Klasse muss deklariert werden, um dieses Protokoll zu implementieren, um als konform zu gelten. Dies ist zur Laufzeit erkennbar. Formale Protokolle können keine Implementierungen bereitstellen; sie versichern den Aufrufern lediglich, dass Klassen, die dem Protokoll entsprechen, Implementierungen bereitstellen. In der NeXT/Apple-Bibliothek werden Protokolle häufig vom Distributed Objects-System verwendet, um die Fähigkeiten eines Objekts darzustellen, das auf einem entfernten System ausgeführt wird.

Die Syntax

@protocol NSLocking
- (void)lock;
- (void)unlock;
@end

bedeutet, dass es die abstrakte Idee des Sperrens gibt. Durch die Angabe in der Klassendefinition, dass das Protokoll implementiert ist,

@interface NSLock : NSObject <NSLocking>
// ...
@end

Instanzen von NSLock behaupten, dass sie eine Implementierung für die beiden Instanzmethoden bereitstellen.

Dynamisches Tippen

Objective-C kann wie Smalltalk dynamische Typisierung verwenden : einem Objekt kann eine Nachricht gesendet werden, die in seiner Schnittstelle nicht angegeben ist. Dies kann eine erhöhte Flexibilität ermöglichen, da es einem Objekt ermöglicht, eine Nachricht "einzufangen" und die Nachricht an ein anderes Objekt zu senden, das angemessen auf die Nachricht antworten kann, oder die Nachricht in ähnlicher Weise an ein anderes Objekt weiterzuleiten. Dieses Verhalten wird als Nachrichtenweiterleitung oder -delegierung bezeichnet (siehe unten). Alternativ kann ein Fehlerhandler verwendet werden, falls die Nachricht nicht weitergeleitet werden kann. Wenn ein Objekt eine Nachricht nicht weiterleitet, darauf reagiert oder einen Fehler behandelt, generiert das System eine Laufzeitausnahme. Wenn Nachrichten an nil (den Nullobjektzeiger) gesendet werden , werden sie je nach Compileroptionen stillschweigend ignoriert oder eine generische Ausnahme ausgelöst.

Optional können auch statische Tippinformationen zu Variablen hinzugefügt werden. Diese Informationen werden dann zur Kompilierzeit überprüft. In den folgenden vier Anweisungen werden immer spezifischere Typinformationen bereitgestellt. Die Anweisungen sind zur Laufzeit äquivalent, aber die zusätzlichen Informationen ermöglichen es dem Compiler, den Programmierer zu warnen, wenn das übergebene Argument nicht mit dem angegebenen Typ übereinstimmt.

- (void)setMyValue:(id)foo;

In der obigen Anweisung kann foo eine beliebige Klasse sein.

- (void)setMyValue:(id<NSCopying>)foo;

In der obigen Anweisung kann foo eine Instanz einer beliebigen Klasse sein, die dem NSCopyingProtokoll entspricht.

- (void)setMyValue:(NSNumber *)foo;

In der obigen Anweisung muss foo eine Instanz der NSNumber- Klasse sein.

- (void)setMyValue:(NSNumber<NSCopying> *)foo;

In der obigen Anweisung muss foo eine Instanz der NSNumber- Klasse sein und dem NSCopyingProtokoll entsprechen.

In Objective-C werden alle Objekte als Zeiger dargestellt und eine statische Initialisierung ist nicht zulässig. Das einfachste Objekt ist der Typ, auf den id ( objc_obj * ) zeigt, der nur einen isa- Zeiger hat, der seine Klasse beschreibt. Andere Typen aus C, wie Werte und Strukturen, bleiben unverändert, da sie nicht Teil des Objektsystems sind. Diese Entscheidung unterscheidet sich vom C++-Objektmodell, bei dem Strukturen und Klassen vereint sind.

Weiterleitung

Objective-C ermöglicht das Senden einer Nachricht an ein Objekt, das möglicherweise nicht reagiert. Anstatt zu antworten oder die Nachricht einfach zu verwerfen, kann ein Objekt die Nachricht an ein Objekt weiterleiten, das antworten kann. Die Weiterleitung kann verwendet werden, um die Implementierung bestimmter Entwurfsmuster zu vereinfachen , beispielsweise des Beobachtermusters oder des Proxymusters .

Die Objective-C-Laufzeit spezifiziert ein Methodenpaar in Object

  • Weiterleitungsmethoden:
    - (retval_t)forward:(SEL)sel args:(arglist_t)args; // with GCC
    - (id)forward:(SEL)sel args:(marg_list)args; // with NeXT/Apple systems
    
  • Aktionsmethoden:
    - (retval_t)performv:(SEL)sel args:(arglist_t)args; // with GCC
    - (id)performv:(SEL)sel args:(marg_list)args; // with NeXT/Apple systems
    

Ein Objekt, das eine Weiterleitung implementieren möchte, muss nur die Weiterleitungsmethode mit einer neuen Methode überschreiben, um das Weiterleitungsverhalten zu definieren. Die Aktionsmethode performv:: muss nicht überschrieben werden, da diese Methode lediglich eine Aktion basierend auf dem Selektor und den Argumenten ausführt. Beachten Sie den SELTyp, der der Nachrichtentyp in Objective-C ist.

Hinweis: In OpenStep, Cocoa und GNUstep, den häufig verwendeten Frameworks von Objective-C, wird die Object- Klasse nicht verwendet . Die Methode - (void)forwardInvocation:(NSInvocation *)anInvocation der NSObject- Klasse wird für die Weiterleitung verwendet.

Beispiel

Hier ist ein Beispiel für ein Programm, das die Grundlagen der Weiterleitung demonstriert.

Spediteur.h
#import <objc/Object.h>

@interface Forwarder : Object {
  id recipient; // The object we want to forward the message to.
}

// Accessor methods.
- (id)recipient;
- (id)setRecipient:(id)_recipient;
@end
Spediteur.m
#import "Forwarder.h"

@implementation Forwarder
- (retval_t)forward:(SEL)sel args:(arglist_t)args {
  /*
  * Check whether the recipient actually responds to the message.
  * This may or may not be desirable, for example, if a recipient
  * in turn does not respond to the message, it might do forwarding
  * itself.
  */
  if ([recipient respondsToSelector:sel]) {
    return [recipient performv:sel args:args];
  } else {
    return [self error:"Recipient does not respond"];
  }
}

- (id)setRecipient:(id)_recipient {
  [recipient autorelease];
  recipient = [_recipient retain];
  return self;
}

- (id)recipient {
  return recipient;
}
@end
Empfänger.h
#import <objc/Object.h>

// A simple Recipient object.
@interface Recipient : Object
- (id)hello;
@end
Empfänger.m
#import "Recipient.h"

@implementation Recipient

- (id)hello {
  printf("Recipient says hello!\n");

  return self;
}

@end
main.m
#import "Forwarder.h"
#import "Recipient.h"

int main(void) {
  Forwarder *forwarder = [Forwarder new];
  Recipient *recipient = [Recipient new];

  [forwarder setRecipient:recipient]; // Set the recipient.
  /*
  * Observe forwarder does not respond to a hello message! It will
  * be forwarded. All unrecognized methods will be forwarded to
  * the recipient
  * (if the recipient responds to them, as written in the Forwarder)
  */
  [forwarder hello];

  [recipient release];
  [forwarder release];

  return 0;
}

Anmerkungen

Beim Kompilieren mit gcc meldet der Compiler:

$ gcc -x objective-c -Wno-import Forwarder.m Recipient.m main.m -lobjc
main.m: In function `main':
main.m:12: warning: `Forwarder' does not respond to `hello'
$

Der Compiler meldet den zuvor erwähnten Punkt, dass Forwarder nicht auf Hallo-Nachrichten reagiert. In diesem Fall ist es sicher, die Warnung zu ignorieren, da die Weiterleitung implementiert wurde. Das Ausführen des Programms erzeugt diese Ausgabe:

$ ./a.out
Recipient says hello!

Kategorien

Bei der Entwicklung von Objective-C war eines der Hauptanliegen die Wartbarkeit großer Codebasen. Erfahrungen aus der Welt der strukturierten Programmierung hatten gezeigt, dass eine der wichtigsten Möglichkeiten zur Verbesserung des Codes darin besteht, ihn in kleinere Teile zu zerlegen. Objective-C hat das Konzept der Kategorien von Smalltalk-Implementierungen übernommen und erweitert , um diesen Prozess zu unterstützen.

Außerdem werden die Methoden innerhalb einer Kategorie zur Laufzeit zu einer Klasse hinzugefügt . Somit erlauben Kategorien dem Programmierer, Methoden zu einer bestehenden Klasse - einer offenen Klasse - hinzuzufügen, ohne diese Klasse neu kompilieren oder sogar Zugriff auf ihren Quellcode haben zu müssen. Wenn ein System beispielsweise keine Rechtschreibprüfung in seiner String-Implementierung enthält, könnte es hinzugefügt werden, ohne den String-Quellcode zu ändern.

Methoden innerhalb von Kategorien werden beim Ausführen des Programms von den Methoden in einer Klasse nicht mehr zu unterscheiden. Eine Kategorie hat vollen Zugriff auf alle Instanzvariablen innerhalb der Klasse, einschließlich privater Variablen.

Deklariert eine Kategorie eine Methode mit derselben Methodensignatur wie eine vorhandene Methode in einer Klasse, wird die Methode der Kategorie übernommen. Somit können Kategorien nicht nur Methoden zu einer Klasse hinzufügen, sondern auch bestehende Methoden ersetzen. Diese Funktion kann verwendet werden, um Fehler in anderen Klassen zu beheben, indem deren Methoden neu geschrieben werden, oder um eine globale Änderung des Verhaltens einer Klasse innerhalb eines Programms zu bewirken. Wenn zwei Kategorien Methoden mit demselben Namen, aber unterschiedlichen Methodensignaturen haben, ist es nicht definiert, welche Methode der Kategorie übernommen wird.

Andere Sprachen haben versucht, diese Funktion auf verschiedene Weise hinzuzufügen. TOM ging mit dem Objective-C-System noch einen Schritt weiter und ermöglichte auch das Hinzufügen von Variablen. Andere Sprachen haben stattdessen prototypbasierte Lösungen verwendet, von denen Self die bemerkenswerteste ist .

Die Sprachen C# und Visual Basic.NET implementieren oberflächlich ähnliche Funktionalitäten in Form von Erweiterungsmethoden , die jedoch keinen Zugriff auf die privaten Variablen der Klasse haben. Ruby und mehrere andere dynamische Programmiersprachen bezeichnen die Technik als „ Affen-Patching “.

Logtalk implementiert ein Konzept von Kategorien (als erstklassige Entitäten), das die Funktionalität von Objective-C-Kategorien subsumiert (Logtalk-Kategorien können auch als feinkörnige Kompositionseinheiten verwendet werden, wenn z. B. neue Klassen oder Prototypen definiert werden; insbesondere kann eine Logtalk-Kategorie sein von beliebig vielen Klassen und Prototypen virtuell importiert).

Beispiel für die Verwendung von Kategorien

In diesem Beispiel wird eine Integer- Klasse aufgebaut, indem zuerst eine Basisklasse mit nur implementierten Accessormethoden definiert und zwei Kategorien hinzugefügt werden, Arithmetic und Display , die die Basisklasse erweitern. Während Kategorien auf die privaten Datenmember der Basisklasse zugreifen können, empfiehlt es sich häufig, über die Accessormethoden auf diese privaten Datenmember zuzugreifen, um Kategorien unabhängiger von der Basisklasse zu halten. Die Implementierung solcher Zugriffsmethoden ist eine typische Verwendung von Kategorien. Eine andere besteht darin, Kategorien zu verwenden, um der Basisklasse Methoden hinzuzufügen. Es wird jedoch nicht als gute Praxis angesehen, Kategorien zum Überschreiben von Unterklassen zu verwenden, auch bekannt als Monkey Patching . Informelle Protokolle werden als Kategorie in der Basisklasse NSObject implementiert . Konventionsgemäß erhalten Dateien, die Kategorien enthalten, die Basisklassen erweitern, den Namen BaseClass+ExtensionClass.h .

Integer.h
#import <objc/Object.h>

@interface Integer : Object {
  int integer;
}

- (int)integer;
- (id)integer:(int)_integer;
@end
Integer.m
#import "Integer.h"

@implementation Integer
- (int) integer {
  return integer;
}

- (id) integer: (int) _integer {
  integer = _integer;
  return self;
}
@end
Integer+Arithmetic.h
#import "Integer.h"

@interface Integer (Arithmetic)
- (id) add: (Integer *) addend;
- (id) sub: (Integer *) subtrahend;
@end
Integer+Arithmetic.m
# import "Integer+Arithmetic.h"

@implementation Integer (Arithmetic)
- (id) add: (Integer *) addend {
  return [self integer: [self integer] + [addend integer]];
}

- (id) sub: (Integer *) subtrahend {
  return [self integer: [self integer] - [subtrahend integer]];
}
@end
Integer+Display.h
#import "Integer.h"

@interface Integer (Display)
- (id) showstars;
- (id) showint;
@end
Integer+Display.m
# import "Integer+Display.h"

@implementation Integer (Display)
- (id) showstars {
  int i, x = [self integer];
  for (i = 0; i < x; i++) {
    printf("*");
  }
  printf("\n");

  return self;
}

- (id) showint {
  printf("%d\n", [self integer]);

  return self;
}
@end
main.m
#import "Integer.h"
#import "Integer+Arithmetic.h"
#import "Integer+Display.h"

int main(void) {
  Integer *num1 = [Integer new], *num2 = [Integer new];
  int x;

  printf("Enter an integer: ");
  scanf("%d", &x);

  [num1 integer:x];
  [num1 showstars];

  printf("Enter an integer: ");
  scanf("%d", &x);

  [num2 integer:x];
  [num2 showstars];

  [num1 add:num2];
  [num1 showint];

  return 0;
}

Anmerkungen

Die Kompilierung erfolgt beispielsweise durch:

gcc -x objective-c main.m Integer.m Integer+Arithmetic.m Integer+Display.m -lobjc

Man kann experimentieren, indem man die Zeilen #import "Integer+Arithmetic.h" und [num1 add:num2] weglässt und Integer+Arithmetic.m bei der Kompilierung weglässt . Das Programm wird weiterhin ausgeführt. Dies bedeutet, dass es möglich ist, hinzugefügte Kategorien bei Bedarf zu mischen und zuzuordnen; Wenn eine Kategorie keine Fähigkeiten haben muss, kann sie einfach nicht einkompiliert werden.

Posieren

Objective-C erlaubt einer Klasse, eine andere Klasse innerhalb eines Programms vollständig zu ersetzen. Die ersetzende Klasse soll sich als die Zielklasse ausgeben.

Class Posing wurde mit Mac OS X v10.5 als veraltet erklärt und ist in der 64-Bit-Laufzeit nicht verfügbar. Eine ähnliche Funktionalität kann durch die Verwendung von Methoden-Swizzling in Kategorien erreicht werden, die die Implementierung einer Methode mit einer anderen mit derselben Signatur vertauscht.

Bei den Versionen, die noch Posing unterstützen, werden alle an die Zielklasse gesendeten Nachrichten stattdessen von der Posing-Klasse empfangen. Es gibt mehrere Einschränkungen:

  • Eine Klasse darf sich nur als eine ihrer direkten oder indirekten Oberklassen ausgeben.
  • Die Posing-Klasse darf keine neuen Instanzvariablen definieren, die in der Zielklasse fehlen (obwohl sie Methoden definieren oder überschreiben kann).
  • Die Zielklasse darf vor dem Posing keine Nachrichten erhalten haben.

Das Posing ermöglicht, ähnlich wie bei Kategorien, eine globale Erweiterung bestehender Klassen. Posing erlaubt zwei Funktionen, die in Kategorien fehlen:

  • Eine Posing-Klasse kann überschriebene Methoden durch super aufrufen und so die Implementierung der Zielklasse einbinden.
  • Eine Posing-Klasse kann in Kategorien definierte Methoden überschreiben.

Zum Beispiel,

@interface CustomNSApplication : NSApplication
@end

@implementation CustomNSApplication
- (void) setMainMenu: (NSMenu*) menu {
  // do something with menu
}
@end

class_poseAs ([CustomNSApplication class], [NSApplication class]);

Dadurch wird jeder Aufruf von setMainMenu an NSApplication abgefangen.

#importieren

In der Sprache C #includebewirkt die Pre-Compile-Direktive immer, dass der Inhalt einer Datei an dieser Stelle in die Quelle eingefügt wird. Objective-C hat die #importäquivalente Direktive, außer dass jede Datei nur einmal pro Kompilierungseinheit eingeschlossen wird, wodurch Include-Guards überflüssig werden .

Linux gcc-Zusammenstellung

// FILE: hello.m
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
    /* my first program in Objective-C */
    NSLog(@"Hello, World! \n");
    return 0;
}
# Compile Command Line for gcc and MinGW Compiler:
$ gcc \
    $(gnustep-config --objc-flags) \
    -o hello \
    hello.m \
    -L /GNUstep/System/Library/Libraries \
    -lobjc \
    -lgnustep-base

$ ./hello

Andere Eigenschaften

Die Funktionen von Objective-C ermöglichen oft flexible und oft einfache Lösungen für Programmierprobleme.

  • Das Delegieren von Methoden an andere Objekte und der Remote-Aufruf können mithilfe von Kategorien und Nachrichtenweiterleitung leicht implementiert werden.
  • Das Verschieben des isa- Zeigers ermöglicht es, dass sich Klassen zur Laufzeit ändern. Wird normalerweise zum Debuggen verwendet, bei dem freigegebene Objekte in Zombie-Objekte umgewandelt werden, deren einziger Zweck darin besteht, einen Fehler zu melden, wenn jemand sie aufruft. Swizzling wurde auch im Enterprise Objects Framework verwendet , um Datenbankfehler zu erzeugen. Swizzling wird heute von Apples Foundation Framework verwendet, um Key-Value-Observing zu implementieren .

Sprachvarianten

Ziel-C++

Objective-C++ ist eine vom Frontend der GNU Compiler Collection und Clang akzeptierte Sprachvariante , die Quelldateien kompilieren kann, die eine Kombination aus C++- und Objective-C-Syntax verwenden. Objective-C++ fügt C++ die Erweiterungen hinzu, die Objective-C C hinzufügt. Da nichts unternommen wird, um die Semantik hinter den verschiedenen Sprachfeatures zu vereinheitlichen, gelten bestimmte Einschränkungen:

  • Eine C++-Klasse kann nicht von einer Objective-C-Klasse abgeleitet werden und umgekehrt.
  • C++-Namespaces können nicht innerhalb einer Objective-C-Deklaration deklariert werden.
  • Objective-C-Deklarationen dürfen nur im globalen Geltungsbereich erscheinen, nicht in einem C++-Namespace
  • Objective-C-Klassen können keine Instanzvariablen von C++-Klassen haben, denen kein Standardkonstruktor fehlt oder die eine oder mehrere virtuelle Methoden haben , aber Zeiger auf C++-Objekte können ohne Einschränkung als Instanzvariablen verwendet werden (weisen Sie sie mit new in der -init-Methode zu).
  • Die C++-Semantik "nach Wert" kann nicht auf Objective-C-Objekte angewendet werden, auf die nur über Zeiger zugegriffen werden kann.
  • Eine Objective-C-Deklaration kann nicht innerhalb einer C++-Template-Deklaration sein und umgekehrt. Objective-C-Typen (zB Classname *) können jedoch als C++-Template-Parameter verwendet werden.
  • Die Ausnahmebehandlung von Objective-C und C++ ist unterschiedlich; die Handler der einzelnen können Ausnahmen des anderen Typs nicht behandeln. Daher werden Objektdestruktoren nicht ausgeführt. Dies wird in neueren "Objective-C 2.0"-Laufzeiten gemildert, da Objective-C-Ausnahmen entweder vollständig durch C++-Ausnahmen ersetzt werden (Apple-Laufzeit) oder teilweise, wenn die Objective-C++-Bibliothek verlinkt wird (GNUstep libobjc2).
  • Objective-C-Blöcke und C++11- Lambdas sind unterschiedliche Entitäten. Unter macOS wird jedoch ein Block transparent generiert, wenn ein Lambda dort übergeben wird, wo ein Block erwartet wird.

Objektiv-C 2.0

Auf der Worldwide Developers Conference 2006 kündigte Apple die Veröffentlichung von "Objective-C 2.0" an, einer Überarbeitung der Objective-C-Sprache, die "moderne Garbage Collection, Syntaxverbesserungen, Verbesserungen der Laufzeitleistung und 64-Bit-Unterstützung" umfasst. Mac OS X 10.5 , veröffentlicht im Oktober 2007, enthielt einen Objective-C 2.0-Compiler. GCC 4.6 unterstützt viele neue Objective-C-Funktionen, wie deklarierte und synthetisierte Eigenschaften, Punktsyntax, schnelle Aufzählung, optionale Protokollmethoden, Methoden-/Protokoll-/Klassenattribute, Klassenerweiterungen und eine neue GNU Objective-C-Laufzeit-API.

Die Benennung Objective-C 2.0 stellt einen Bruch im Versionierungssystem der Sprache dar, da die letzte Objective-C-Version für NeXT "objc4" war. Dieser Projektname wurde in der letzten Version des älteren Objective-C-Laufzeitquellcodes in Mac OS X Leopard (10.5) beibehalten .

Müllabfuhr

Objective-C 2.0 bietet einen optionalen konservativen, generationsübergreifenden Garbage Collector . Bei Ausführung im abwärtskompatiblen Modus hat die Laufzeit Referenzzähloperationen wie "Retain" und "Release" in No-Ops umgewandelt . Alle Objekte unterliegen der Garbage Collection, wenn die Garbage Collection aktiviert ist. Reguläre C-Zeiger könnten mit "__strong" qualifiziert werden, um auch die zugrunde liegenden Compiler-Intercepts der Schreibbarriere auszulösen und somit an der Garbage Collection teilzunehmen. Ein schwaches Untersystem zur Nullung wurde ebenfalls bereitgestellt, so dass Zeiger, die als "__weak" markiert sind, auf Null gesetzt werden, wenn das Objekt (oder einfacher GC-Speicher) gesammelt wird. Der Garbage Collector ist in der iOS-Implementierung von Objective-C 2.0 nicht vorhanden. Die Garbage Collection in Objective-C wird in einem Hintergrundthread mit niedriger Priorität ausgeführt und kann bei Benutzerereignissen angehalten werden, um die Benutzererfahrung reaktionsfähig zu halten.

Die Garbage Collection wurde in Mac OS X v10.8 zugunsten von Automatic Reference Counting (ARC) eingestellt. Objective-C unter iOS 7, das auf ARM64 ausgeführt wird, verwendet 19 Bits aus einem 64-Bit-Wort, um den Referenzzähler als Form von markierten Zeigern zu speichern .

Eigenschaften

Objective-C 2.0 führt eine neue Syntax ein, um Instanzvariablen als Eigenschaften zu deklarieren , mit optionalen Attributen, um die Generierung von Zugriffsmethoden zu konfigurieren. Eigenschaften sind gewissermaßen öffentliche Instanzvariablen; dh die Deklaration einer Instanzvariablen als Eigenschaft ermöglicht externen Klassen den Zugriff (möglicherweise eingeschränkt, zB nur lesend) auf diese Eigenschaft. Eine Eigenschaft kann als "schreibgeschützt" deklariert und mit Speichersemantik wie assign, copyoder versehen werden retain. Standardmäßig werden Eigenschaften berücksichtigt atomic, was dazu führt, dass eine Sperre verhindert, dass mehrere Threads gleichzeitig darauf zugreifen. Eine Eigenschaft kann als deklariert werden nonatomic, wodurch diese Sperre aufgehoben wird.

@interface Person : NSObject {
@public
  NSString *name;
@private
  int age;
}

@property(copy) NSString *name;
@property(readonly) int age;

- (id)initWithAge:(int)age;
@end

Eigenschaften werden über das @synthesizeSchlüsselwort implementiert , das gemäß der Eigenschaftsdeklaration getter- (und setter-, wenn nicht schreibgeschützte) Methoden generiert. Alternativ müssen die Getter- und Setter-Methoden explizit implementiert werden, oder das @dynamicSchlüsselwort kann verwendet werden, um anzugeben, dass Zugriffsmethoden auf andere Weise bereitgestellt werden. Wenn mit clang 3.1 oder höher kompiliert wird, werden alle Eigenschaften, die nicht explizit mit deklariert @dynamic, markiert readonlyoder über einen vollständigen benutzerimplementierten Getter und Setter verfügen, automatisch implizit @synthesize'd.

@implementation Person
@synthesize name;

- (id)initWithAge:(int)initAge {
  self = [super init];
  if (self) {
    // NOTE: direct instance variable assignment, not property setter
    age = initAge;
  }
  return self;
}

- (int)age {
  return age;
}
@end

Auf Eigenschaften kann mit der traditionellen Message-Passing-Syntax, Punktnotation oder bei der Schlüsselwert-Codierung nach Namen über die Methoden "valueForKey:"/"setValue:forKey:" zugegriffen werden.

Person *aPerson = [[Person alloc] initWithAge:53];
aPerson.name = @"Steve"; // NOTE: dot notation, uses synthesized setter,
                         // equivalent to [aPerson setName: @"Steve"];
NSLog(@"Access by message (%@), dot notation(%@), property name(% @) and "
       "direct instance variable access(% @) ",
              [aPerson name],
      aPerson.name, [aPerson valueForKey:@"name"], aPerson -> name);

Um die Punktnotation zum Aufrufen von Eigenschaftsaccessoren innerhalb einer Instanzmethode zu verwenden, sollte das Schlüsselwort "self" verwendet werden:

- (void)introduceMyselfWithProperties:(BOOL)useGetter {
  NSLog(@"Hi, my name is %@.", (useGetter ? self.name : name));
  // NOTE: getter vs. ivar access
}

Die Eigenschaften einer Klasse oder eines Protokolls können dynamisch introspektiert werden .

int i;
int 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 has a property: '%s'", propertyName);
}

Nicht-fragile Instanzvariablen

Objective-C 2.0 bietet nicht-fragile Instanzvariablen, wo dies von der Laufzeit unterstützt wird (dh beim Erstellen von Code für 64-Bit-MacOS und alle iOS). Unter der modernen Laufzeit wird dem Instanzvariablenzugriff eine zusätzliche Indirektionsebene hinzugefügt, die es dem dynamischen Linker ermöglicht, das Instanzlayout zur Laufzeit anzupassen. Diese Funktion ermöglicht zwei wichtige Verbesserungen des Objective-C-Codes:

  • Es beseitigt das fragile Problem der binären Schnittstelle ; Superklassen können die Größe ändern, ohne die Binärkompatibilität zu beeinträchtigen.
  • Es ermöglicht die Synthese von Instanzvariablen, die die Unterstützung für Eigenschaften bereitstellen, zur Laufzeit, ohne dass sie in der Schnittstelle der Klasse deklariert werden.

Schnelle Aufzählung

Anstatt ein NSEnumerator-Objekt oder Indizes zu verwenden, um eine Auflistung zu durchlaufen, bietet Objective-C 2.0 die schnelle Enumerationssyntax. In Objective-C 2.0 sind die folgenden Schleifen funktional äquivalent, haben jedoch unterschiedliche Leistungsmerkmale.

// Using NSEnumerator
NSEnumerator *enumerator = [thePeople objectEnumerator];
Person *p;

while ((p = [enumerator nextObject]) != nil) {
  NSLog(@"%@ is %i years old.", [p name], [p age]);
}
// Using indexes
for (int i = 0; i < [thePeople count]; i++) {
  Person *p = [thePeople objectAtIndex:i];
  NSLog(@"%@ is %i years old.", [p name], [p age]);
}
// Using fast enumeration
for (Person *p in thePeople) {
  NSLog(@"%@ is %i years old.", [p name], [p age]);
}

Die schnelle Aufzählung generiert effizienteren Code als die Standardaufzählung, da Methodenaufrufe zum Aufzählen von Objekten durch Zeigerarithmetik unter Verwendung des NSFastEnumeration-Protokolls ersetzt werden.

Klassenerweiterungen

Eine Klassenerweiterung hat dieselbe Syntax wie eine Kategoriedeklaration ohne Kategorienamen, und die darin deklarierten Methoden und Eigenschaften werden direkt der Hauptklasse hinzugefügt. Es wird meistens als Alternative zu einer Kategorie verwendet, um einer Klasse Methoden hinzuzufügen, ohne sie in den öffentlichen Headern anzukündigen, mit dem Vorteil, dass der Compiler bei Klassenerweiterungen überprüft, ob alle privat deklarierten Methoden tatsächlich implementiert sind.

Auswirkungen auf die Kakaoentwicklung

Alle für macOS entwickelten Objective-C-Anwendungen, die die oben genannten Verbesserungen für Objective-C 2.0 verwenden, sind mit allen Betriebssystemen vor 10.5 (Leopard) nicht kompatibel. Da die schnelle Aufzählung nicht genau die gleichen Binärdateien wie die Standardaufzählung generiert, führt ihre Verwendung zum Absturz einer Anwendung unter Mac OS X Version 10.4 oder früher.

Blöcke

Blocks ist eine nicht standardmäßige Erweiterung für Objective-C (und C und C++ ), die eine spezielle Syntax verwendet, um Closures zu erstellen . Blöcke werden nur in Mac OS X 10.6 "Snow Leopard" oder höher, iOS 4 oder höher und GNUstep mit libobjc2 1.7 und Kompilieren mit clang 3.1 oder höher unterstützt.

#include <stdio.h>
#include <Block.h>
typedef int (^IntBlock)();

IntBlock MakeCounter(int start, int increment) {
  __block int i = start;

  return Block_copy( ^ {
    int ret = i;
    i += increment;
    return ret;
  });

}

int main(void) {
  IntBlock mycounter = MakeCounter(5, 2);
  printf("First call: %d\n", mycounter());
  printf("Second call: %d\n", mycounter());
  printf("Third call: %d\n", mycounter());

  /* because it was copied, it must also be released */
  Block_release(mycounter);

  return 0;
}
/* Output:
  First call: 5
  Second call: 7
  Third call: 9
*/

Modernes Ziel-C

Apple hat Objective 2.0 im Laufe der Zeit um einige zusätzliche Funktionen erweitert. Die Ergänzungen gelten nur für den "Apple LLVM Compiler", dh das Clang-Frontend der Sprache. Verwirrenderweise unterscheidet sich die von Apple verwendete Versionierung von der des LLVM-Upstreams; Siehe Xcode § Toolchain-Versionen für eine Übersetzung in Open-Source-LLVM-Versionsnummern.

Automatische Referenzzählung

Automatic Reference Counting (ARC) ist eine Funktion zur Kompilierzeit, die es Programmierern überflüssig macht, Retain-Zählungen mit retainund manuell zu verwalten release. Im Gegensatz zur Garbage Collection , die zur Laufzeit stattfindet, eliminiert ARC den Overhead eines separaten Prozesses, der Retain-Zählungen verwaltet. ARC und manuelle Speicherverwaltung schließen sich nicht gegenseitig aus; Programmierer können weiterhin Nicht-ARC-Code in ARC-aktivierten Projekten verwenden, indem sie ARC für einzelne Codedateien deaktivieren. Xcode kann auch versuchen, ein Projekt automatisch auf ARC zu aktualisieren.

ARC wurde in LLVM 3.0 eingeführt. Dies entspricht Xcode 4.2 (2011) oder Apple LLVM-Compiler 3.0.

Literale

NeXT- und Apple Obj-C-Laufzeiten bieten seit langem eine Kurzform zum Erstellen neuer Strings unter Verwendung der Literalsyntax @"a new string"oder zum Ablegen auf CoreFoundation-Konstanten kCFBooleanTrueund kCFBooleanFalsefür NSNumbermit booleschen Werten. Die Verwendung dieses Formats erspart dem Programmierer die Verwendung längerer initWithStringoder ähnlicher Methoden bei bestimmten Operationen.

Wenn Sie Apple LLVM Compiler 4.0 (Xcode 4.4) oder höher verwenden, können Arrays, Wörterbücher und Zahlen ( NSArray, NSDictionary, NSNumberKlassen) auch mit Literal-Syntax anstelle von Methoden erstellt werden. (Apple LLVM Compiler 4.0 übersetzt in Open Source LLVM und Clang 3.1.)

Beispiel ohne Literale:

NSArray *myArray = [NSArray arrayWithObjects:object1,object2,object3,nil];
NSDictionary *myDictionary1 = [NSDictionary dictionaryWithObject:someObject forKey:@"key"];
NSDictionary *myDictionary2 = [NSDictionary dictionaryWithObjectsAndKeys:object1, key1, object2, key2, nil];
NSNumber *myNumber = [NSNumber numberWithInt:myInt];
NSNumber *mySumNumber= [NSNumber numberWithInt:(2 + 3)];
NSNumber *myBoolNumber = [NSNumber numberWithBool:YES];

Beispiel mit Literalen:

NSArray *myArray = @[ object1, object2, object3 ];
NSDictionary *myDictionary1 = @{ @"key" : someObject };
NSDictionary *myDictionary2 = @{ key1: object1, key2: object2 };
NSNumber *myNumber = @(myInt);
NSNumber *mySumNumber = @(2+3);
NSNumber *myBoolNumber = @YES;
NSNumber *myIntegerNumber = @8;

Im Gegensatz zu Zeichenfolgenliteralen , die in der ausführbaren Datei zu Konstanten kompiliert werden, kompilieren diese Literale jedoch zu Code, der den obigen Methodenaufrufen entspricht. Insbesondere bei der manuellen Speicherverwaltung mit Referenzzählung werden diese Objekte automatisch freigegeben, was zusätzliche Sorgfalt erfordert, wenn sie zB mit funktionsstatischen Variablen oder anderen Arten von Globals verwendet wird.

Abonnieren

Wenn Sie Apple LLVM Compiler 4.0 oder höher verwenden, können Arrays und Wörterbücher ( NSArrayund NSDictionaryKlassen) mithilfe von Indizes bearbeitet werden. Subskription kann verwendet werden, um Werte aus Indizes (Array) oder Schlüsseln (Wörterbuch) abzurufen, und mit veränderlichen Objekten kann auch verwendet werden, um Objekte auf Indizes oder Schlüssel zu setzen. Im Code wird die Indizierung durch Klammern dargestellt [ ].

Beispiel ohne Subskription:

id object1 = [someArray objectAtIndex:0];
id object2 = [someDictionary objectForKey:@"key"];
[someMutableArray replaceObjectAtIndex:0 withObject:object3];
[someMutableDictionary setObject:object4 forKey:@"key"];

Beispiel mit Subskription:

id object1 = someArray[0];
id object2 = someDictionary[@"key"];
someMutableArray[0] = object3;
someMutableDictionary[@"key"] = object4;

"Moderne" Objective-C-Syntax (1997)

Nach dem Kauf von NeXT durch Apple wurde versucht, die Sprache für Programmierer akzeptabler zu machen, die mit Java vertrauter sind als mit Smalltalk. Einer dieser Versuche war die Einführung von Objective-C, die damals als "moderne Syntax" bezeichnet wurde (im Gegensatz zur aktuellen "klassischen" Syntax). Es gab keine Verhaltensänderung, dies war lediglich eine alternative Syntax. Anstatt einen Methodenaufruf wie zu schreiben

    object = [[MyClass alloc] init];
    [object firstLabel: param1 secondLabel: param2];

Es wurde stattdessen geschrieben als

    object = (MyClass.alloc).init;
    object.labels ( param1, param2 );

Ebenso gingen Erklärungen aus dem Formular

    -(void) firstLabel: (int)param1 secondLabel: (int)param2;

zu

    -(void) labels ( int param1, int param2 );

Diese "moderne" Syntax wird in aktuellen Dialekten der Objective-C-Sprache nicht mehr unterstützt.

mulle-objc

Das Mulle-Objc- Projekt ist eine weitere Neuimplementierung von Objective-C. Es unterstützt GCC- oder Clang / LLVM- Compiler als Backends. Es unterscheidet sich von anderen Laufzeiten in Bezug auf Syntax, Semantik und ABI-Kompatibilität. Es unterstützt Linux, FreeBSD und Windows.

Tragbarer Objekt-Compiler

Neben der GCC / NeXT / Apple- Implementierung, die der ursprünglichen Stepstone- Implementierung mehrere Erweiterungen hinzugefügt hat, existiert auch eine weitere freie Open-Source- Objective-C-Implementierung namens Portable Object Compiler. Die vom Portable Object Compiler implementierten Erweiterungen unterscheiden sich von der GCC/NeXT/Apple-Implementierung; insbesondere enthält es Smalltalk- ähnliche Blöcke für Objective-C, während Protokolle und Kategorien fehlen, zwei Funktionen, die in OpenStep und seinen Derivaten und Verwandten ausgiebig verwendet werden. Insgesamt stellt POC eine ältere, vor-NEXT-Stufe in der Evolution der Sprache dar, die in etwa dem Buch von Brad Cox von 1991 entspricht.

Es enthält auch eine Laufzeitbibliothek namens ObjectPak, die auf der ursprünglichen ICPak101-Bibliothek von Cox basiert (die wiederum von der Smalltalk-80-Klassenbibliothek abgeleitet ist) und sich radikal vom OpenStep FoundationKit unterscheidet.

GEOS Ziel-C

Das PC GEOS- System verwendete eine Programmiersprache, die als GEOS Objective-C oder goc bekannt ist ; Trotz der Namensähnlichkeit sind sich die beiden Sprachen nur im Gesamtkonzept und der Verwendung von Schlüsselwörtern mit vorangestelltem @-Zeichen ähnlich.

Klingeln

Die Compiler-Suite Clang , Teil des LLVM- Projekts, implementiert Objective-C und andere Sprachen. Nachdem GCC 4.3 (2008) auf GPLv3 umgestellt hatte, gab Apple es zugunsten von clang auf, einem Compiler, der mehr rechtliche Befugnisse hat, ihn zu ändern. Daher werden viele der modernen Objective-C-Sprachfunktionen nur von Clang unterstützt.

Apples Versionierungsschema für seinen Clang-basierten "LLVM-Compiler" unterscheidet sich von der Open-Source-Versionierung des LLVM. Siehe Xcode § Toolchain-Versionen für eine Übersetzung

GNU, GNUstep und WinObjC

Das GNU-Projekt ist seit langem an einer Plattform interessiert, auf die NeXT- und Obj-C-Programme portiert werden können. Das ChangeLog für das libobjc- Verzeichnis im GCC legt nahe, dass es vor 1998 existierte (GCC 2.95), und seine README-Datei weist weiter auf eine Neufassung im Jahr 1993 (GCC 2.4) hin.

Der NeXT-Frontend-Quellcode wurde veröffentlicht, da er als Teil der GCC, der veröffentlichten GNU Public License, erstellt wurde, die diejenigen dazu zwingt, abgeleitete Werke zu erstellen. Apple setzte diese Tradition fort und veröffentlichte seinen GCC-Fork bis 4.2.1, woraufhin der Compiler aufgegeben wurde. GCC-Maintainer nahmen die Änderungen auf, investierten jedoch nicht viel in die Unterstützung neuerer Funktionen wie der Sprache Objective-C 2.0.

Die GNUstep-Entwickler, die an der neuen Sprache interessiert waren, haben die GCC libobjc 2009 in ein von GCC unabhängiges Projekt namens libobjc2 gegabelt . Sie haben auch dafür gesorgt , dass die Laufzeit mit Clang verwendet wird, um die neue Sprachsyntax zu nutzen. GCC bewegte sich gleichzeitig langsam, aber mit GCC 4.6.0 (2011) sind sie auch in ihrer libobjc zu Objective-C 2.0 übergegangen. Die GNUstep-Dokumentation legt nahe, dass die GCC-Implementierung immer noch keine Unterstützung für Blöcke, nicht-fragile Variablen und das neuere ARC bietet.

Microsoft hat libobjc2 2015 in einen Teil von WinObjC , der iOS-Brücke für die universelle Windows-Plattform , gespalten . In Kombination mit seiner eigenen Implementierung von Cocoa Touch und den zugrunde liegenden APIs ermöglicht das Projekt die Wiederverwendung von iOS-Anwendungscode innerhalb von UWP-Apps.

Unter Windows werden Objective-C-Entwicklungstools zum Download auf der GNUStep-Website bereitgestellt. Das GNUStep Development System besteht aus den folgenden Paketen: GNUstep MSYS System, GNUstep Core, GNUstep Devel, GNUstep Cairo, ProjectCenter IDE (wie Xcode, aber nicht so komplex), Gorm (Interface Builder wie Xcode NIB builder). Diese binären Installer wurden seit 2016 nicht mehr aktualisiert, daher könnte es eine bessere Idee sein, stattdessen einfach unter Cygwin oder MSYS2 zu installieren .

Bibliotheksnutzung

Objective-C wird heute oft zusammen mit einer festen Bibliothek von Standardobjekten (oft als "Kit" oder "Framework" bekannt) wie Cocoa , GNUstep oder ObjFW verwendet . Diese Bibliotheken werden oft mit dem Betriebssystem geliefert: Die GNUstep-Bibliotheken kommen oft mit Linux- basierten Distributionen und Cocoa kommt mit macOS. Der Programmierer ist nicht gezwungen, Funktionalität von der vorhandenen Basisklasse (NSObject / OFObject) zu erben. Objective-C ermöglicht die Deklaration neuer Root-Klassen, die keine vorhandene Funktionalität erben. Ursprünglich boten Objective-C-basierte Programmierumgebungen typischerweise eine Object-Klasse als Basisklasse an, von der fast alle anderen Klassen erben. Mit der Einführung von OpenStep erstellte NeXT eine neue Basisklasse namens NSObject, die zusätzliche Funktionen gegenüber Object bot (z. B. ein Schwerpunkt auf der Verwendung von Objektreferenzen und Referenzzählung anstelle von Rohzeigern). Fast alle Klassen in Cocoa erben von NSObject.

Die Umbenennung diente nicht nur dazu, das neue Standardverhalten von Klassen innerhalb der OpenStep-API zu unterscheiden, sondern ermöglichte es auch Code, der Object verwendet – die ursprüngliche Basisklasse, die in NeXTSTEP (und mehr oder weniger anderen Objective-C-Klassenbibliotheken) verwendet wurde –, um koexistieren in derselben Laufzeit mit Code, der NSObject verwendet (mit einigen Einschränkungen). Die Einführung des zweibuchstabigen Präfixes wurde auch zu einer vereinfachten Form von Namensräumen, die Objective-C fehlt. Die Verwendung eines Präfixes zum Erstellen einer informellen Verpackungskennung wurde zu einem informellen Codierungsstandard in der Objective-C-Community und dauert bis heute an.

In jüngerer Zeit erscheinen Paketmanager wie CocoaPods , die sowohl ein Paketmanager als auch ein Repository von Paketen sein sollen. Viel Open-Source-Objective-C-Code, der in den letzten Jahren geschrieben wurde, kann jetzt mit CocoaPods installiert werden.

Analyse der Sprache

Objective-C-Implementierungen verwenden ein in C geschriebenes dünnes Laufzeitsystem , das die Größe der Anwendung nur wenig erhöht. Im Gegensatz dazu verwendeten die meisten objektorientierten Systeme zu der Zeit, als sie erstellt wurden, große Laufzeiten virtueller Maschinen . Programme, die in Objective-C geschrieben wurden, sind in der Regel nicht viel größer als ihr Code und die der Bibliotheken (die im Allgemeinen nicht in die Softwareverteilung aufgenommen werden müssen), im Gegensatz zu Smalltalk-Systemen, bei denen viel Speicher vorhanden war nur zum Öffnen eines Fensters verwendet. Objective-C-Anwendungen sind in der Regel größer als ähnliche C- oder C++-Anwendungen, da die dynamische Typisierung von Objective-C es nicht zulässt, dass Methoden entfernt oder integriert werden. Da der Programmierer eine solche Freiheit hat, Aufrufe zu delegieren, weiterzuleiten, Selektoren im laufenden Betrieb zu erstellen und sie an das Laufzeitsystem zu übergeben, kann der Objective-C-Compiler nicht davon ausgehen, dass es sicher ist, nicht verwendete Methoden zu entfernen oder Inline-Aufrufe durchzuführen.

Ebenso kann die Sprache auf bestehenden C-Compilern (in GCC zuerst als Präprozessor, dann als Modul) statt als neuer Compiler implementiert werden . Dadurch kann Objective-C die riesige vorhandene Sammlung von C-Code, Bibliotheken, Tools usw. nutzen. Vorhandene C-Bibliotheken können in Objective-C- Wrapper verpackt werden , um eine Schnittstelle im OO-Stil bereitzustellen. In diesem Aspekt ähnelt es der GObject- Bibliothek und der Vala- Sprache, die bei der Entwicklung von GTK- Anwendungen weit verbreitet sind .

All diese praktischen Veränderungen senkten die Eintrittsbarriere , wahrscheinlich das größte Problem für die weit verbreitete Akzeptanz von Smalltalk in den 1980er Jahren.

Ein häufiger Kritikpunkt ist, dass Objective-C keine Sprachunterstützung für Namespaces bietet . Stattdessen sind Programmierer gezwungen, ihren Klassennamen Präfixe hinzuzufügen, die traditionell kürzer als Namensraumnamen und daher anfälliger für Kollisionen sind. Ab 2007 wird allen macOS-Klassen und -Funktionen in der Cocoa- Programmierumgebung das Präfix "NS" (zB NSObject, NSButton) vorangestellt, um sie als zum macOS- oder iOS-Kern gehörend zu kennzeichnen; das "NS" leitet sich von den Namen der Klassen ab, die während der Entwicklung von NeXTSTEP definiert wurden .

Da Objective-C eine strikte Obermenge von C ist, behandelt es primitive C-Typen nicht als erstklassige Objekte .

Im Gegensatz zu C++ unterstützt Objective-C das Überladen von Operatoren nicht . Im Gegensatz zu C++ erlaubt Objective-C einem Objekt, nur von einer Klasse direkt zu erben (und verbietet Mehrfachvererbung ). In den meisten Fällen können jedoch Kategorien und Protokolle als alternative Wege verwendet werden, um dieselben Ergebnisse zu erzielen.

Da Objective-C dynamische Laufzeittypisierung verwendet und alle Methodenaufrufe Funktionsaufrufe (oder in einigen Fällen Systemaufrufe) sind, können viele allgemeine Leistungsoptimierungen nicht auf Objective-C-Methoden angewendet werden (z. und skalarer Ersatz von Aggregaten). Dies begrenzt die Leistung von Objective-C-Abstraktionen im Vergleich zu ähnlichen Abstraktionen in Sprachen wie C++, wo solche Optimierungen möglich sind.

Speicherverwaltung

Die ersten Versionen von Objective-C unterstützten keine Garbage Collection . Zu dieser Zeit war diese Entscheidung umstritten, und viele Leute hielten lange "Totzeiten" (wenn Smalltalk die Sammlung durchführte) für unbrauchbar. Einige Implementierungen von Drittanbietern haben diese Funktion hinzugefügt (vor allem GNUstep mit Boehm ), und Apple hat sie ab Mac OS X v10.5 implementiert . In neueren Versionen von macOS und iOS wurde die Garbage Collection jedoch zugunsten des 2011 eingeführten Automatic Reference Counting (ARC) eingestellt.

Mit ARC fügt der Compiler Retain- und Release-Aufrufe basierend auf statischer Codeanalyse automatisch in Objective-C-Code ein . Die Automatisierung entlastet den Programmierer vom Einschreiben von Speicherverwaltungscode. ARC fügt auch schwache Verweise auf die Objective-C-Sprache hinzu.

Philosophische Unterschiede zwischen Objective-C und C++

Das Design und die Implementierung von C++ und Objective-C repräsentieren grundlegend unterschiedliche Ansätze zur Erweiterung von C.

Zusätzlich zum C-Stil der prozeduralen Programmierung unterstützt C++ direkt bestimmte Formen der objektorientierten Programmierung , der generischen Programmierung und der Metaprogrammierung . C++ wird auch mit einer großen Standardbibliothek geliefert , die mehrere Containerklassen enthält . In ähnlicher Weise fügt Objective-C objektorientierte Programmierung , dynamische Typisierung und Reflexion zu C hinzu. Objective-C bietet keine Standardbibliothek an sich , aber an den meisten Orten, an denen Objective-C verwendet wird, wird es mit einem OpenStep- ähnlichen verwendet Bibliothek wie OPENSTEP , Cocoa oder GNUstep , die ähnliche Funktionen wie die Standardbibliothek von C++ bietet.

Ein bemerkenswerter Unterschied besteht darin, dass Objective-C Laufzeitunterstützung für reflektierende Funktionen bietet, während C++ C nur eine geringe Laufzeitunterstützung hinzufügt. In Objective-C kann ein Objekt nach seinen eigenen Eigenschaften abgefragt werden, z eine bestimmte Botschaft. In C++ ist dies ohne die Verwendung externer Bibliotheken nicht möglich.

Die Verwendung von Reflektion ist Teil der breiteren Unterscheidung zwischen dynamischen (Laufzeit-)Eigenschaften und statischen (Kompilierungszeit-)Eigenschaften einer Sprache. Obwohl Objective-C und C++ jeweils eine Mischung aus beiden Funktionen verwenden, ist Objective-C entschieden auf Laufzeitentscheidungen ausgerichtet, während C++ auf Entscheidungen zur Kompilierzeit ausgerichtet ist. Die Spannung zwischen dynamischer und statischer Programmierung beinhaltet viele der klassischen Kompromisse bei der Programmierung: dynamische Funktionen erhöhen die Flexibilität, statische Funktionen erhöhen die Geschwindigkeit und die Typprüfung.

Generische Programmierung und Metaprogrammierung können in beiden Sprachen durch Laufzeitpolymorphismus implementiert werden . In C++ geschieht dies in Form von virtuellen Funktionen und der Identifizierung von Laufzeittypen , während Objective-C dynamische Typisierung und Reflexion bietet. Sowohl Objective-C als auch C++ unterstützen den Polymorphismus zur Kompilierzeit ( generische Funktionen ), wobei Objective-C diese Funktion erst 2015 hinzufügte.

Siehe auch

Verweise

Weiterlesen

  • Cox, Brad J. (1991). Objektorientierte Programmierung: Ein evolutionärer Ansatz . Addison Wesley. ISBN 0-201-54834-8.

Externe Links