Sie sind auf Seite 1von 224

OReillys Taschenbibliothek

Objective-C
kurz & gut

OREILLY

Lars Schulten

Objective-C
kurz & gut

Lars Schulten

Beijing Cambridge Farnham Kln Sebastopol Tokyo

Die Informationen in diesem Buch wurden mit grter Sorgfalt erarbeitet. Dennoch knnen Fehler nicht vollstndig ausgeschlossen werden. Verlag, Autoren und bersetzer bernehmen keine juristische Verantwortung oder irgendeine Haftung fr eventuell verbliebene Fehler und deren Folgen. Alle Warennamen werden ohne Gewhrleistung der freien Verwendbarkeit benutzt und sind mglicherweise eingetragene Warenzeichen. Der Verlag richtet sich im Wesentlichen nach den Schreibweisen der Hersteller. Das Werk einschlielich aller seiner Teile ist urheberrechtlich geschtzt. Alle Rechte vorbehalten einschlielich der Vervielfltigung, bersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen. Kommentare und Fragen knnen Sie gerne an uns richten: OReilly Verlag GmbH & Co. KG Balthasarstr. 81 50670 Kln E-Mail: kommentar@oreilly.de

Copyright der deutschen Ausgabe:  2013 OReilly Verlag GmbH & Co. KG 1. Auflage 2013

Die Darstellung eines Steppenfuchses im Zusammenhang mit dem Thema Objective-C ist ein Warenzeichen von OReilly Media, Inc. Bibliografische Information Der Deutschen Nationalbibliothek Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet ber http://dnb.d-nb.de abrufbar. Lektorat: Alexandra Follenius, Kln Fachgutachten: Ingo Dellwig, Hannover Korrektorat: Sibylle Feldmann, Dsseldorf Produktion: Andrea Mi, Kln Umschlaggestaltung: Ellie Volckhausen, Sebastopol & Michael Oreal, Kln Satz: Reemers Publishing Services GmbH, Krefeld, www.reemers.de Druck: fgb freiburger graphische betriebe; www.fgb.de ISBN: 978-3-86899-373-8 Dieses Buch ist auf 100% chlorfrei gebleichtem Papier gedruckt.

Inhalt

hrung ..................................................................... Einfu 1 Grundlagen ............................................................... Ein erstes Beispiel .......................................................... Code schreiben und kompilieren .......................................... Headerdateien .............................................................. Frameworks ................................................................ Syntax Klassen und Objekte ....................................................... Nachrichten ................................................................. Compilerdirektiven ......................................................... Literaldeklarationen ........................................................ Blocks ....................................................................... Ein zweites Beispiel ........................................................ Objekte .................................................................... Objektvariablen und Zeiger ................................................ Objekte erstellen ........................................................... Mit Objekten interagieren ................................................. Objekte vergleichen ........................................................ Objekt-Enumeration ........................................................ Objektindizierung .......................................................... Klassen .................................................................... Klassendeklaration und -definition ........................................ Felder ....................................................................... Methoden .................................................................. Eigenschaften...............................................................

1 5
5 6 8 9

11
11 12 13 14 18 19

23
23 27 28 29 30 30

33
33 35 38 47

| III

Vererbung und Polymorphie............................................... Typumwandlungen ........................................................ 5 6 Kategorien und Klassenerweiterungen .............................. Klassenerweiterungen ...................................................... Protokolle................................................................. Ein Protokoll deklarieren ................................................... Protokolle implementieren ................................................ Optionale Methoden aufrufen ............................................. Protokolle als Referenztypen .............................................. Informelle Protokolle....................................................... Fehler und Ausnahmen ................................................. Fehler ....................................................................... NSError-Referenz ........................................................... Ausnahmen................................................................. NSException-Referenz ...................................................... NSObject .................................................................. Objektlebenszyklus ...................................................... Objekte erstellen ........................................................... Objekte kopieren ........................................................... Objektvernichtung..........................................................

56 61

63
65

67
67 69 70 71 71

73
74 76 78 83

8 9

85 87
88 95 99

10 Speicherverwaltung ..................................................... 101 Was Speicherverwaltung ist ............................................... 101 Speicherverwaltungstechnologien unter Objective-C...................... 102 ARC (Automatic Reference Counting)...................................... 104 MRC (Manuel Reference Counting) ........................................ 110 11 Laufzeitinformationen .................................................. 115 Objektinformationen ....................................................... 116 Klasseninformationen ...................................................... 117 Funktionsinformationen .................................................... 123 Protokollinformationen .................................................... 124 12 Messaging ................................................................ 127 Kompilierung ............................................................... 128 Selektoren .................................................................. 129
IV | Inhalt

Implementierungen ........................................................ Selektoren dynamisch ausfu hren .......................................... Implementierungen cachen ................................................ Dynamische Methodenimplementierung .................................. Nachrichtenweiterleitung ..................................................

130 131 135 137 139

13 Key/ Value-Coding ....................................................... 143 t ........................................................... 143 KVC-Konformita Schlu sselbasierte Interaktion mit Objekten ................................ 144 Schlu sselpfade .............................................................. 145 Virtuelle Schlu ssel .......................................................... 146 KVC-Validierung ............................................................ 147 Schlu sselpfadoperatoren ................................................... 148 14 Objektarchivierung ...................................................... 151 Sequenzielle und schlu sselbasierte Archive ............................... 153 Die NSCoding-Methoden implementieren ................................. 153 Coder-Objekte .............................................................. 156 15 Blocks 159 Blocks definieren ........................................................... 159 Blocks als Objekte .......................................................... 161 Zugriff auf den Kontext .................................................... 161 Lokale Variablen ........................................................... 165 Globale und kontextgebundene Blocks .................................... 168 16 NSObject-Referenz ....................................................... 173 17 Compilerdirektiven ...................................................... 185 18 Alternative Plattformen ................................................ 195 Compiler .................................................................... 196 Clang/LLVM ................................................................ 196 Laufzeitumgebungen und Plattformen .................................... 197 19 Clang-Optionen .......................................................... 201 Index ............................................................................ 205

Inhalt | V

hrung Einfu

Objective-C ist die Programmiersprache der letzten Jahre. Die Beliebtheit von Apples Plattformen, iOS und Mac OS X, sorgt dafr, dass die Sprache heute aktueller ist als je zuvor. Objective-C kurz & gut ist ein kompaktes Handbuch zur Programmiersprache Objective-C. Es illustriert die grundlegenden Aspekte der Sprache mit kurzen Erklrungen, die von kleineren Beispielen aufgelockert werden, und bietet einen berblick ber die wichtigsten Technologien, Klassen, Nachrichten und Funktionen. Es richtet sich an alle, die bereits fr andere Plattformen programmiert haben und sich nun in diese spannende Sprache einarbeiten wollen.
Sie sollten mit mindestens einer Sprache vertraut sein, die eine C-artige Syntax nutzt. Objective-C setzt auf C auf und nutzt viele Elemente seiner Syntax unverndert. Das heit, elementare Syntaxformen, wie Schleifen und Bedingungsanweisungen, sowie die elementaren C-Datentypen werden in diesem Buch nicht erlutert. Das bedeutet aber nicht, dass Sie C selbst beherrschen mssen. Eine beliebige an C angelehnte Programmiersprache mit quivalenten Sprachelementen, Java oder C# beispielsweise, reicht vollkommen aus. Wenn Sie bislang nur mit dynamisch typisierten Sprachen wie z. B. JavaScript gearbeitet haben, sollten Sie sich allerdings unbedingt mit statischen Typsystemen vertraut machen.

| 1

Objective-C
Objective-C ist ein C-Dialekt. Es bildet eine vollstndige Obermenge zu C und bietet alles, was auch C bietet. Es besitzt die gleichen Syntaxstrukturen und -elemente, nutzt die gleichen Datentypen und kann sogar auf die gleichen Bibliotheken zugreifen. Wenn Sie sich Objective-C von C her nhern, werden Ihnen also viele Dinge bekannt vorkommen. Sie knnten einem ObjectiveC-Compiler sogar Ihren alten C-Quellcode vorsetzen, und dieser wrde ihn anstandslos kompilieren. Haben Sie keine Erfahrung mit C, mssen Sie sich zustzlich zum eigentlichen Objective-C-Kern noch mit gewissen Aspekten von C auseinandersetzen, die bei der Arbeit mit Objective-C unumgnglich sind. Auf einige dieser Aspekte wie C-Zeiger werden wir (kurz) eingehen, da ansonsten ein Verstndnis von Objective-C unmglich ist. Andere wie die diversen C-Datentypen und C-Syntaxstrukturen werden wir hier nicht beschreiben, da wir davon ausgehen, dass Sie bereits mit Sprachen wie Java, C# usw. gearbeitet haben, die hnliche Features bieten. Sollte das nicht der Fall sein, mssen Sie die entsprechenden Informationen in einem geeigneten C-Buch nachschlagen. Objective-C ist aber auch eine objektorientierte Programmiersprache, die alle Features bietet, die Ihnen aus anderen objektorientierten Programmiersprachen vertraut sein sollten: Objekte, Klassen, Schnittstellen, Vererbung, Polymorphie usw. Dazu erweitert es C um die entsprechenden Strukturen und Syntaxformen: Compilerdirektiven, Syntaxstrukturen zur Definition von Klassen und ihren Elementen, Syntaxstrukturen zur Arbeit mit Klassen und ihren Elementen und einige neue Datentypen. Das Buch erlutert die wichtigsten Aspekte der objektorientierten Programmierung und ihre spezifische Implementierung in Objective-C und geht dabei auch explizit auf die Punkte ein, an denen sich die objektorientierte Programmierung unter Objective-C von der in z. B. Java unterscheidet.

2 | Einfhrung

Werkzeuge und Plattformen


Objective-C ist eigentlich plattformunabhngig. Es gibt eine Reihe von Compilern, die Objective-C-Code kompilieren knnen, und verschiedene Laufzeitumgebungen, die in Objective-C-Code eingebunden werden knnen. In diesem Buch werden wir uns auf die Werkzeuge und die Laufzeitumgebung fr Apples Plattformen konzentrieren. Einen kurzen berblick ber andere verfgbare Werkzeuge und Bibliotheken finden Sie in Kapitel 18, Alternative Plattformen. Da Apple die Sprache in den letzten Jahren umfassend erweitert hat, kann es sein, dass diese die neuesten Sprach-Features nicht immer vollstndig untersttzen.
Wir werden hier Objective-C in der Version behandeln, in der es von den Werkzeug- und Betriebssystemversionen untersttzt wird, die aktuell waren, als dieses Buch geschrieben wurde: XCode 4.6 mit Clang 4.2 und LLVM 3.2 sowie Mac OS X 10.8 und iOS 6. Das heit auch, dass als Entwicklungsplattform ein System erforderlich ist, auf dem mindestens OS X 10.7 (Lion) luft, da die bentigten Versionen von Xcode und Clang fr frhere Betriebssystemversionen nicht verfgbar sind.

Die Beispiele
Die Beispiele sind eher knapp und nicht vollstndig. In der Regel dienen sie nur der Illustration eines einzigen Problems und sind deshalb so einfach wie mglich gehalten. Sie machen extensiven Gebrauch von den neuesten Objective-C-Technologien und gehen, soweit nicht anders vermerkt, davon aus, dass der Code mit ARCUntersttzung kompiliert wird.

Die Beispiele | 3

KAPITEL 1

Grundlagen

Objective-C bildet eine vollstndige Obermenge zu C. Es nutzt nicht nur die gleichen elementaren Syntaxstrukturen, Datentypen und Schlsselwrter wie C, sondern die Programmierung in Objective-C weist auch die gleichen Grundstrukturen wie die Programmierung in C auf.

Ein erstes Beispiel


Ein primitives Objective-C-Programm sieht fast genau so aus wie ein quivalentes C-Programm und weist nur unerhebliche Unterschiede zu einem quivalenten Java- oder C#-Programm auf:
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { NSLog(@"Die Welt ist alles, was der Fall ist!"); } return 0; }

Folgende Elemente sollten Sie aus C oder einer von C abgeleiteten Sprache kennen: Die main()-Funktion dient als Einsprungpunkt. Die Ausfhrung jedes Objective-C-Programms beginnt mit dieser Funktion. Beachten Sie, dass main() eine klassische Funktion ist, keine Methode wie in Java oder C#.

| 5

Die Elemente, mit denen der Code operiert (Funktionen, Variablen usw.), haben stets einen Typ (int main(), int argc usw.). Anweisungsblcke stehen in geschweiften Klammern. Anweisungen werden mit einem Semikolon abgeschlossen. Unbekannt knnten Ihnen die #import-, @autorealesepool- und @""-Konstruktionen sein, da das Objective-C-eigene Syntaxelemente sind: #import ist eine Objective-C-spezifische Prprozessordirektive, die eine Headerdatei importiert. @autoreleasepool ist eine Objective-C-Compilerdirektive. @"" definiert ein Objective-C-Stringliteral. Die beiden @-Formen werden in Kapitel 2, Syntax, ausfhrlicher erlutert, #import im Abschnitt Headerdateien auf Seite 8.
NSLog() ist eine Protokollierungsfunktion, die Nachrichten mit

einem Zeitstempel versehen auf der Konsole ausgibt. Als erstes Argument erwartet sie ein Objective-C-Stringliteral. Das Stringliteral kann Formatangaben enthalten, die durch die nachfolgenden Argumente ersetzt werden, zum Beispiel:
NSLog(@"Das Leben ist %@", 1 > 0 ? @"scho n" : @"be");

Die Formatangabe %@ formatiert ein Objective-C-Objekt, hier das Objective-C-Stringobjekt, zu dem der Bedingungsausdruck ausgewertet wird. Daneben untersttzt Objective-C die von der printfSpezifikation definierten Formatzeichen.

Code schreiben und kompilieren


Unter Mac OS X haben Sie zwei Mglichkeiten, Objective-C-Code zu schreiben und zu erstellen. Sie knnen beides mit Apples IDE Xcode tun, oder Sie schreiben mit einem Programm Ihrer Wahl und erstellen mit Apples Kommandozeilenwerkzeugen. Xcode erhalten Sie im Mac App Store oder auf Apples Entwicklerseiten unter https://developer.apple.com/downloads/. Die Kommandozeilenwerkzeuge knnen Sie aus Xcode (unter
6 | Kapitel 1: Grundlagen

Preferences/Downloads/Components/Command Line Tools) installieren oder separat von Apples Entwicklerseiten fr Ihr System herunterladen. Beachten Sie, dass Downloads und viele andere Ressourcen nur Mitgliedern in Apples Entwicklerprogramm zugnglich sind. Eine kostenlose Mitgliedschaft gengt.

Xcode
Der Code in diesem Beispiel ist eine nur minimal angepasste Fassung des Inhalts der main.m-Datei, die die Xcode-Projektvorlage fr ein Kommandozeilenprojekt generiert. Sie knnen diesen Code kompilieren und ausfhren, indem Sie in Xcode ein neues Kommandozeilenprojekt anlegen (File/New/Project/OS X/Application/ Command Line Tool), dann nach Belieben vor dem Ausfhren links im Projektnavigator main.m anwhlen und im Editor die nderungen vornehmen. Oder Sie klicken gleich auf den Run-Button, den Sie in Abbildung Abbildung 1-1 oben links sehen, um die Kompilation und Ausfhrung des Programms anzustoen.

Abbildung 1-1: Apples IDE, Xcode

Code schreiben und kompilieren | 7

Kommandozeilenkompilation
Alternativ knnen Sie die Datei ber die Kommandozeile kompilieren und ausfhren. Geben Sie den Code in einen Texteditor Ihrer Wahl ein und speichern Sie die Datei unter einem Namen, der die Dateinamenserweiterung .m trgt, z.B. welt.m. Das ist die bliche Dateinamenserweiterung fr Objective-C-Dateien. ffnen Sie die Terminalanwendung und navigieren Sie in das Verzeichnis, in dem Sie Ihre Datei gespeichert haben. Geben Sie dann den folgenden Befehl ein (vorausgesetzt, Sie haben den gleichen Namen gewhlt wie wir), um den Code zu kompilieren:
clang -fobjc-arc -framework Foundation -o Welt welt.m

clang ist Apples Objective-C-Compiler. Der Kommandozeilenschalter -fobjc-arc fordert Automatic Reference Counting (siehe Kapitel 10, Speicherverwaltung) an, -framework gibt die Frameworks an, gegen die gelinkt werden soll, -o den Namen der Ausgabedatei. Das Kompilat

knnen Sie einfach mit folgender Anweisung ausfhren:


./Welt

Headerdateien
Header enthalten Deklarationen (von Klassen und Funktionen beispielsweise) und Definitionen (von Typen und Makros). Es sind echte physische Dateien, die blicherweise die Dateinamenserweiterung .h tragen. Sie werden in Codedateien eingelesen, um die in ihnen enthaltenen Deklarationen und Definitionen in diesem Code verfgbar zu machen. Das Codebeispiel importiert den Header Foundation/Foundation.h, damit der Compiler wei, wie er mit den beiden @-Konstrukten umzugehen hat. Eigene Headerdateien schreiben Sie bei Objective-C in der Regel, um Klassen und ihre Schnittstellen zu deklarieren (diesen Aspekt werden wir uns im Kapitel 4, Klassen, ansehen).

Header importieren
#import ist eine Objective-C-spezifische Prprozessordirektive zum

Importieren von Headerdateien. Der Unterschied zum in C blichen


8 | Kapitel 1: Grundlagen

#include ist, dass #import sicherstellt, dass Header nur einmal importiert werden. Anders als Javas import oder C#s using macht #import nicht Namen bekannt, sondern liest Deklarationen und

eventuell auch Definitionen ein.


#import kann auf zweierlei Weise verwendet werden: zum Import systemweiter Headerdateien und zum Import anwendungsspezifischer Headerdateien:

#import <> importiert einen Systemheader. #import "" importiert einen anwendungsspezifischen Header. Zwischen den spitzen Klammern bzw. Anfhrungszeichen steht der Name bzw. Pfad zu einer Headerdatei. Die verwendete Form steuert, wo nach der entsprechenden Datei gesucht wird, wenn der Name kein absoluter Pfad ist. Bei der ersten Form wird in einer (ber Compilerschalter und Umgebungsvariablen erweiterbaren) Liste von Standardverzeichnissen gesucht. Bei der zweiten Form wird zuvor noch im aktuellen Verzeichnis gesucht. blicherweise gibt es zu jeder Codedatei, deren Definitionen in anderen Codedateien genutzt werden sollten, eine eigene Headerdatei. Daneben gibt es bergeordnete Header, sogenannte Umbrella-Header, die viele in Zusammenhang stehende Header importieren und so gemeinsam importierbar machen. Der in unserem Beispiel verwendete Foundation.h-Header ist der Umbrella-Header fr das FoundationFramework, das das Fundament der Cocoa-Objective-C-API bildet.

Frameworks
Unter OS X und iOS werden Programmbibliotheken und andere Ressourcen zu sogenannten Frameworks gebndelt. Diese werden blicherweise in /System/Library/Frameworks und /Library/Frameworks gespeichert und stehen allen Anwendungen zur Verfgung. Frameworks knnen unter anderem Headerdateien mit den Deklarationen fr die Features enthalten, die sie bereitstellen. Ein Framework kann mehrere Headerdateien enthalten. Folgendermaen binden Sie einen Header aus einem Framework ein:
#import <Framework/Header.h>

Frameworks | 9

Der Foundation/Foundation.h-Header, den unser Programm nutzt, ist ein Beispiel fr einen solchen Framework-Header. Foundation ist das Framework, Foundation.h der Name der Headerdatei im Framework. Die Headerangaben mit #import werden vom Compiler zur entsprechenden Ressource im Framework-Bundle aufgelst. Das heit, dass der Pfad in der Headerangabe nicht dem tatschlichen Dateipfad der Headerdatei entsprechen muss. Der Foundation.h-Header ist der Umbrella-Header fr das Foundation-Framework. Ein Umbrella-Header ist ein Metaheader, der (meist) keine eigenen Deklarationen enthlt, sondern die Header fr die verschiedenen Elemente eines Frameworks zusammenfasst. Wenn Sie eine Komponente eines Frameworks nutzen, sollten Sie in der Regel einen solchen bergeordneten Header verwenden, nicht die Header fr einzelne Komponenten. Bei der Kommandozeilenkompilation geben Sie Frameworks, gegen die gelinkt werden soll, mit dem Schalter -framework an. Wenn Sie Xcode nutzen, werden die Frameworks, die standardmig fr eine bestimmte Art von Anwendung genutzt werden, automatisch eingebunden. Mssen Sie zustzliche Frameworks einbinden, knnen Sie das fr das jeweilige Build-Target im Link Binary With Libraries-Abschnitt der Registerkarte Build Phases tun, den Sie in der Abbildung Abbildung 1-2 sehen:

Abbildung 1-2: Frameworks einbinden

10 | Kapitel 1: Grundlagen

KAPITEL 2

Syntax

Objective-C erweitert C um eine Reihe von Syntaxstrukturen und Konzepten zur Untersttzung seiner dynamischen Variante der objektorientierten Programmierung. Dieser Abschnitt stellt sie kurz vor, damit Sie den Beispielen in den nachfolgenden Teilen des Buchs folgen knnen. Komplexere Elemente wie Objekte und Klassen werden an anderer Stelle noch ausfhrlicher betrachtet.

Klassen und Objekte


Objective-C untersttzt alle C-Datentypen, elementare (z. B. im ersten Beispiel oben char und int) wie komplexe (z. B. Structs oder Arrays). Daneben integriert es Strukturen zur Darstellung von Klassen und Objekten. Klassen sind Anleitungen fr den Aufbau von Gegenstnden, Objekte sind die konkreten Gegenstnde, mit denen Objective-C-Code arbeitet. Objective-C-Objekte referenzieren Sie in Ihrem Code ber Variablen, die Zeiger auf einen Wert des entsprechenden Typs, d. h. der Klasse, sind, oder den generischen Objekttyp id haben, zum Beispiel:
NSString *string = @"Kansas";

oder:
id objekt = @"Kaninchenbau";

Objekte knnen mit Literalausdrcken (siehe Abschnitt Literaldeklarationen auf Seite 14) definiert, von anderen Objekten geliefert oder mithilfe von Klassen konstruiert werden. Ausfhrlicher behandeln wir Objekte und Klassen in den Kapiteln 3 und 4.
| 11

Nachrichten
Die Interaktion mit Objekten und Klassen erfolgt ber Nachrichten. Sie senden einem Empfnger (einer Klasse oder einem Objekt) eine Nachricht. Dieser prft, ob er mit dieser Nachricht etwas anfangen kann, und reagiert gegebenenfalls entsprechend. Die Syntax zum Senden von Nachrichten hat folgende elementare Gestalt:
[Empfa nger Nachricht]

Der Empfnger ist entweder eine im Kontext gltige Klasse oder eine im Kontext gltige Objektreferenz. Die Nachricht ist der Name einer Methode samt aller eventuellen Parameter, die auf dem Empfnger aufgerufen wird. Konkret knnte das so aussehen:
[kaffeeMaschine kaffeeKochen] [Auto neuesAuto]

Da der erste Buchstabe von kaffeeMaschine ein Kleinbuchstabe ist, wre Ersteres konventionsgem eine Nachricht an das Objekt, das von der zuvor deklarierten und initialisierten Objektvariablen kaffeeMaschine referenziert wird. Das Zweite hingegen wre eine Nachricht an die Klasse Auto, da der erste Buchstabe von Auto ein Grobuchstabe ist. Nachrichten knnen parameterlos sein (wie die beiden Nachrichten oben), und sie knnen Parameter erwarten. Nachrichtenparameter folgen im Nachrichtennamen nach einem Doppelpunkt, wobei der Doppelpunkt Teil des Nachrichtennamens ist. Zum Beispiel sendet
[ich trinken: kaffee]

dem von ich referenzierten Objekt die Nachricht trinken: und bergibt dabei das von kaffee referenzierte Objekt. Wenn eine Nachricht mehrere Parameter hat, folgen diese nach weiteren Namensteilen, die ebenfalls jeweils mit einem Doppelpunkt abgeschlossen werden:
[ich trinken: kaffee essen: kuchen];

oder:
[ich trinken: kaffee essen: kuchen mitPerson: mrPresident];

12 | Kapitel 2: Syntax

Beachten Sie, dass zwischen den Parametern keine Kommata stehen. Mehr zu den Regeln fr die Namen und die Nutzung von Nachrichten erfahren Sie im Kapitel 12, Messaging, bei der Beschreibung der Definition eigener Nachrichten. Nachrichten sind Ausdrcke, die entweder zu einem Wert (einer Objektreferenz oder einem elementaren C-Wert, z. B. int) ausgewertet werden oder leer (void) sind. Nachrichten, die nicht void sind, knnen geschachtelt werden, zum Beispiel:
if ([kaffeeMaschine kannKaffeeKochen]) { [ich trinken: [kaffeeMaschine kaffeeKochen]]; }

Funktionell knnen Sie sich das Senden einer Nachricht als quivalent zum Aufruf einer Methode vorstellen. Eine Nachricht an eine Klasse entspricht dem Aufruf einer Klassenmethode, eine Nachricht an ein Objekt dem Aufruf einer Instanzmethode. [objekt machDas] entsprche also einem objekt.machDas() in Java und hnlich strukturierten Programmiersprachen. Das vorangehende Codefragment knnte in einer solchen Programmiersprache also folgendermaen aussehen:
if(kaffeeMaschine.kannKaffeeKochen()) { ich.trinken(kaffeeMaschine.kaffeeKochen()); }

Wie Sie eigene Nachrichten definieren, erfahren Sie in Kapitel 4 im Abschnitt Methoden auf Seite 38, wie der Benachrichtigungsmechanismus intern funktioniert und wie Sie in ihn eingreifen knnen, in Kapitel 12, Messaging.

Compilerdirektiven
Compilerdirektiven sind Anweisungen an den Compiler, bestimmten Code zu generieren. Sie haben immer folgende Gestalt:
@name

Compilerdirektiven werden zu den unterschiedlichsten Zwecke eingesetzt: zur Deklaration und Definition von Klassen, zur Deklaration von Protokollen, fr die Ausnahmeverarbeitung, fr die Speicherverwaltung usw. Beispiele sind unter anderem die Direktiven
Compilerdirektiven | 13

@implementation und @end, die eine Klassendefinition einrahmen, @class, die einen Klassennamen bekannt macht, oder @private, @public usw., die die Sichtbarkeit von Instanzvariablen steuern.

Die @autoreleasepool-Direktive aus dem einleitenden Beispiel bezieht sich beispielsweise auf den nachfolgenden Block (den gesamten Code in den geschweiften Klammern) und dient der Speicherverwaltung. Sie weist den Compiler an, Code zu generieren, der dafr sorgt, dass fr den Block ein Auto-Release-Pool verfgbar ist. Dass ein solcher verfgbar ist, wird von den Cocoa-Bibliotheken vorausgesetzt, auf die der Code zurckgreift. Wre kein AutoRelease-Pool verfgbar, wrde das zu Speicherlchern fhren. Wir werden die verschiedenen Compilerdirektiven in den Abschnitten zu den entsprechenden Themen behandeln. Eine Aufstellung aller Compilerdirektiven finden Sie im Kapitel 18, Alternative Plattformen.

Literaldeklarationen
Objective-C definiert ein paar neue Syntaxformen fr Literale, mit denen einige hufig genutzte Objekttypen leichter erstellt werden knnen. Wie Compilerdirektiven werden auch Literaldarstellungen mit dem @-Zeichen eingeleitet. Von einfachen Compilerdirektiven unterscheiden sie sich dadurch, dass auf das @-Zeichen ein weiteres Nicht-Buchstabenzeichen (also [, {, ") oder ein gewhnliches Zahlliteral, folgt. Objective-C definiert Literale fr Strings (@""), Arrays (@[]), Dictionaries (@{}) und fr Zahlobjekte (z. B. @2.1415). Alle Literale erstellen Objekte, die unvernderlich sind. Diese Arten von Literalen werden wir uns in den folgenden Unterabschnitten kurz ansehen.
Array-, Dictionary- und Zahlliterale gibt es erst seit Mac OS X 10.8 bzw. iOS 6.0, und sie werden von den Entwicklungswerkzeugen erst seit Clang 4.0 untersttzt.

14 | Kapitel 2: Syntax

Stringliterale
@"" definiert ein Objective-C-Stringliteral. Objective-C-Stringlite-

rale werden vom Compiler standardmig in Objective-C-Objekte des Typs NSString umgewandelt. @""-Stringliterale sind also im Gegensatz zu C-Strings (denen kein @-Zeichen vorangestellt wird, z. B. "Ein C-Stringliteral") Objekte und verhalten sich deswegen wie die Stringobjekte anderer objektorientierter Programmiersprachen. Die Klasse NSString bringt bereits eine Menge von Methoden mit, mit denen Sie Objective-C-Strings verarbeiten knnen.
Die vom Compiler fr Objective-C-Stringliterale zu verwendende Klasse kann mit dem Compilerschalter -fconstant-string-class eingestellt werden.

Array-Literale
@[] definiert ein Array-Literal. Die Elemente des Arrays werden

durch Kommata getrennt zwischen den eckigen Klammern angegeben:


@[@"eins", @"zwei", @"drei"]

Array-Literale werden vom Compiler in NSArray-Objekte bersetzt. NSArray-Objekte knnen nur Objekte enthalten, aber diese Objekte knnen beliebigen Typs sein, d. h., ein Array kann Instanzen verschiedener Klassen enthalten. Folgendermaen knnte man ein Array deklarieren, das einen String und ein weiteres Array mit zwei Zahlen enthlt:
@[@"12-12-12", @[@48.857731, @2.339407]];

Das zweite Array enthlt zwei Zahlen, die als Zahlobjekte angegeben werden mssen, da NSArray-Objekte nur Objekte enthalten knnen. Literale fr Zahlobjekte werden weiter unten in diesem Abschnitt beschrieben. Literaldeklarationen knnen zur Verbesserung der Lesbarkeit wie im folgenden (etwas extremen) Beispiel ber mehrere Zeilen aufgeteilt werden:

Literaldeklarationen | 15

NSArray *position = @[ @"12-12-12", @[ @48.857731, @2.339407 ] ];

Dictionary-Literale
@{} definiert ein Dictionary-Literal. Dictionaries oder Wrterbcher verknpfen einen Schlssel, der ein beliebiger Objektwert sein kann, mit einem Wert, der ebenfalls ein beliebiger Objektwert sein kann. Zwischen Schlssel und Wert steht jeweils ein Doppelpunkt (Schlu ssel:Wert). Mehrere Schlssel/Wert-Paare werden als kommaseparierte Liste angegeben:
@{ @"datum" : @"12-12-12", @"koords" : @[@48.857731, @2.339407] };

Dictionary-Literale werden vom Compiler in NSDictionary-Objekte bersetzt. Als Dictionary-Schlssel knnen normalerweise beliebige Objektwerte verwendet werden, vorausgesetzt, die Objekt untersttzen das Protokoll NSCopying (das in Kapitel 9, Objektlebenszyklus, im Abschnitt Objekte kopieren auf Seite 95 beschrieben wird). Die Schlssel mssen Strings sein, wenn das Dictionary Key/Value-Coding konform sein soll (siehe Kapitel 13, Key/ ValueCoding).

r Zahlobjekte Literale fu
In Objective-C knnen die skalaren C-Zahltypen, z. B. int oder float, genutzt werden. Auerdem definieren die Cocoa-Bibliotheken eigene skalare Zahltypen, z. B. NSInteger oder CGFloat, die eine konsistente Zahldarstellung auf unterschiedlichen Plattformen (32 Bit vs. 64 Bit) gewhrleisten. Daneben bieten die Cocoa-Bibliotheken den Objekttyp NSNumber, der als Objekt-Wrapper fr die verschiedenen Zahltypen dient. Dieser wird z. B. bentigt, wenn

16 | Kapitel 2: Syntax

Zahlen in einem NSArray-Objekt gespeichert werden sollen, da die Cocoa-Collection-Typen nur Objekte aufnehmen knnen. Zahlobjekte knnen durch eine der vielen NSNumber-Klassennachrichten oder mithilfe von numerischen Objektliteralen erstellt werden. Ein numerisches Objektliteral wird deklariert, indem einem gewhnlichen Zahlliteral ein @-Zeichen vorangestellt wird, zum Beispiel:
@3 @3.1415 @YES @'a' // // // // Entspricht Entspricht Entspricht Entspricht dem dem dem dem int-Wert 3 double-Wert 3.1415 BOOL-Wert YES char-Wert 'a'

Wenn der gewnschte reprsentierte skalare Wert vom Standardtyp fr Zahlliterale des entsprechenden Typs abweicht, kann dieser durch ein Postfix (U, L, F) angegeben werden, zum Beispiel:
@3L // long-Wert @3U // unsigned int-Wert @3.1415F // float-Wert

Seit Mac OS X 10.7 nutzt Objective-C fr NSNumber-Objekte sogenannte Tagged Pointer. Das sind markierte Zeiger, die in den freien Bits der Zeigeradresse direkt den Wert und einen Typcode fr das Objekt enthalten. Da Objective-C damit die teurere Objekterstellung und -auflsung spart, knnen die NSNumberZahlobjekte erheblich leichtgewichtiger und schneller als die Wrapper-Klassen anderer Programmiersprachen sein.

Auf das @-Zeichen muss ein Zahlliteral folgen, d. h., Folgendes geht nicht:
int zahl = 3; NSNumber *zahlobjekt = @zahl; // Geht nicht

Nutzen Sie stattdessen die im nchsten Abschnitt vorgestellten Wrapper-Ausdrcke.

Literaldeklarationen | 17

cke Wrapper-Ausdru
Wrapper-Ausdrcke (Apple bezeichnet sie als Boxed Expressions) ermglichen Ihnen, die Werte von C-Ausdrcken in den korrespondierenden Objective-C-Objekttyp umzuwandeln. Wrapper-Ausdrcke nutzen die folgende Syntax:
@()

Einen Wrapper-Ausdruck knnten Sie folgendermaen nutzen, um einen skalaren Zahlwert in ein Zahlobjekt umzuwandeln:
int zahl = 3; NSNumber *zahlobjekt = @(zahl); // Geht

Wrapper-Ausdrcke untersttzen neben numerischen Ausdrcken Enums, BOOL und C-Strings, zum Beispiel:
int zahl = 3; NSNumber *istUngerade = @((BOOL)zahl%2); char *cString = "Hallo"; NSString *objcString = @(cstring);

Blocks
Blocks ist eine Syntax zur Definition von Funktionsausdrcken. Sie wurden von Apple zur bernahme in den C-Standard eingereicht und kommen unter Objective-C immer hufiger zum Einsatz. Sie werden unter anderem unter GCD (Grand Central Dispatch), der von Apple eingefhrten Parallelisierungsinfrastruktur fr Cocoa, genutzt, aber auch zur Definition einfacher Delegate-Funktionen, z.B. beim Vergleich oder bei der Enumeration von Objekten. Blocks knnen inline definiert oder Blockvariablen zugewiesen werden. Eine Blockvariable wird folgendermaen deklariert:
Ru ckgabetyp (^Variablenname)(Parameterliste)

Eine Blockdefinition hat folgende Gestalt:


^(Parameterliste) {Anweisungen}

Folgendermaen knnte man einen einfachen Block definieren, der zwei Strings verkettet:

18 | Kapitel 2: Syntax

NSString * (^str_concat)(NSString *, NSString *) = ^(NSString *head, NSString *tail) { return [NSString stringWithFormat: @"%@%@", head, tail]; }

Die Deklaration der Blockvariablen str_concat sagt, dass dieser Block zwei NSStrings erwartet und einen NSString liefert. Diesen Block wrden Sie folgendermaen nutzen:
str_concat(@"Kopf und ", @"Schwanz");

Das diesen Abschnitt abschlieende Beispiel enthlt ein Blockliteral, das eine Funktion beschreibt, die bei der Enumeration eines Arrays auf allen Elementen aufgerufen wird. Eine ausfhrlichere Beschreibung des Blocks-Konzepts finden Sie im Abschnitt Blocks auf Seite 18.

Ein zweites Beispiel


Schauen wir uns nun ein nicht mehr ganz so primitives Beispiel an, in dem diese Syntaxstrukturen zur Anwendung kommen. Sie sollten jetzt in der Lage sein, diesem Code zu folgen:
#import <Foundation/Foundation.h> int main(int argc, const char *argv[]) { @autoreleasepool { id preise = @[@"ein Zeitungsabo", @"eine Kaffeemaschine", @"einen Computer", @"eine Reise", @"ein Auto" ]; 1 NSUInteger anzahl = [preise count];2 int zufall = arc4random_uniform((UInt32)anzahl);3 id verlosung = [NSMutableString new];4 [verlosung appendString: @"Sie ha tten"];5 [preise enumerateObjectsUsingBlock: 6 ^(id obj, NSUInteger idx, BOOL *stop) { id format = idx == 0 ? @"%@" : idx < anzahl - 1 ? @", %@" : @" oder %@";7 [verlosung appendFormat: format, obj];8 }];9 [verlosung appendString: @" gewinnen ko nnen."];:

Ein zweites Beispiel | 19

NSLog(@"%@", verlosung);; if (zufall < anzahl - 2) {< NSLog(@"Sie haben %@ gewonnen.", [preise objectAtIndex: zufall]);= } else { NSLog(@"Sie haben leider nichts gewonnen."); } } return 0; }

1 2

Erstellt mithilfe eines Literalausdrucks ein Array und weist es der Variablen preise zu, deren deklarierter Typ id ist. Ruft mit der Nachricht count die Anzahl an Elementen im Array preise ab. Der Wert wird in der Variablen anzahl gespeichert, die den Typ NSUInteger hat. Das ist der Rckgabetyp fr count. Erzeugt mithilfe der C-Funktion arc4random_uniform() eine Zufallszahl zwischen 0 und der Anzahl an Array-Elementen. Der Cast auf (UInt32) ist erforderlich, weil die Funktion einen int erwartet und liefert, anzahl aber den Typ NSUInteger hat. Sendet der Klasse NSMutableString die new-Nachricht. Auf diese erhalten wir ein Objekt, das einen vernderbaren String reprsentiert. Wir werden es nutzen, um einen Satz aufzubauen, der die Elemente des Arrays aufzhlt. Sendet dem gerade erstellten Objekt ber die Referenz verlosung die Nachricht appendString: mit dem Text @"Sie ha tten " als Argument. Das bewirkt, dass der Text an den, noch leeren, String angehngt wird. Sendet dem preise-Objekt die Nachricht enumerateObjects UsingBlock: mit einer Blockdefinition als Argument. Diese Nachricht bewirkt, dass das Array durchlaufen und alle seine Elemente mit dem Block verarbeitet werden. Der Block, der auf die Array-Elemente angewandt wird. Der Block erhlt drei Parameter, von denen uns nur die beiden ersten interessieren: obj, das aktuelle Array-Element, und idx, der Index des Elements.

20 | Kapitel 2: Syntax

Ermittelt mithilfe des ternren Bedingungsoperators einen fr die Position des aktuellen Array-Elements geeigneten Formatstring. Sendet verlosung die Nachricht appendFormat: mit dem ermittelten Format und dem aktuellen Objekt als Argument. Hngt den Wert des aktuellen Array-Elements mit dem Format formatiert an den String an. Schliet den String, nachdem die Elemente des Arrays in ihn eingebaut wurden, mit einer weiteren appendString:-Nachricht. Protokolliert den String mit der Bibliotheksfunktion NSLog() auf der Konsole. Prft, ob die Zufallszahl einen Gewinn darstellt, und sorgt dafr, dass die entsprechende Ausgabeanweisung zur Ausfhrung kommt. (Sie fragen sich, was das -2 soll? Haben Sie sich noch nie gewundert, warum Sie den Hauptgewinn nicht bekommen haben?) Nutzt einen Objektindexer, um ein Element aus einem ArrayObjekt abzurufen.

; <

Die Ausgabe knnte folgendermaen aussehen (wir haben den Text etwas formatiert, um ihn besser lesbar zu machen):
2012-12-08 17:13:29.233 Gewinnspiel[20151:303] Sie ko nnen ein Zeitungsabo, eine Kaffeemaschine, einen Computer, eine Reise oder ein Auto gewinnen. 2012-12-08 17:13:29.236 Gewinnspiel[20151:303] Sie haben eine Kaffeemaschine gewonnen.

Wenn Sie so ungefhr erkennen konnten, wie der Code in diesem Beispiel funktioniert, sollten Sie dazu in der Lage sein, dieses Buch zu nutzen. Ist das jedoch nicht der Fall, sollten Sie sich zunchst mit einem geeigneten Buch hinreichende Kenntnisse einer anderen Programmiersprache aneignen (am besten C oder eine Programmiersprache, deren Syntax an C angelehnt ist).

Ein zweites Beispiel | 21

KAPITEL 3

Objekte

Objekte sind der Angelpunkt der objektorientierten Programmierung. Sie sind der Stoff, aus dem Ihre Programme gewebt sind. Fr die, die noch nie mit einer objektorientierten Programmiersprache gearbeitet haben: Alle Programme bestehen aus Daten und Operationen. Bei prozeduralen Programmiersprachen wie C sind das jeweils eigenstndige Programmkomponenten: Daten, die in einem Speicher festgehalten und ber Variablen im Programm referenziert werden, und Funktionen, die auf diese Daten zugreifen. In objektorientierten Programmiersprachen werden Daten und Operationen zu Einheiten zusammengefasst, die als Objekte bezeichnet werden. Wie in den meisten anderen objektorientierten Programmiersprachen sind Objekte auch in Objective-C opake Strukturen, die dynamisch in einem speziellen Speicherbereich, dem Heap, angelegt und verwaltet werden. Sie knnen nur ber Referenzen festgehalten, manipuliert und zwischen Programmteilen ausgetauscht werden.

Objektvariablen und Zeiger


In Objective-C werden Objektreferenzen durch klassische C-Zeiger reprsentiert und implementiert. Ein C-Zeiger ist eine Variable, die die Adresse eines Speicherorts festhlt. Zeigervariablen werden folgendermaen deklariert:
Typ *Variablenname;

| 23

Der Zeigerindikator * zeigt an, dass eine Variable deklariert wird, die einen Zeiger auf einen Wert des angegebenen Typs enthlt. Der * wird entweder nach dem Typ oder vor dem Variablennamen angegeben (in Objective-C ist die Stellung vor dem Variablennamen blicher). Es gibt zwei Operatoren zur Interaktion mit Zeigervariablen: & ist der Adressoperator, * der Dereferenzierungsoperator. &Variable liefert die Speicheradresse einer Variablen, *Variable den Wert an der Speicheradresse, die eine Zeigervariable festhlt. Einen Zeiger, der die Adresse eines int-Werts festhlt, wrde man in C also folgendermaen deklarieren:
int *zahlzeiger;

Den Wert einer anderen Variablen weisen Sie dem Zeiger mithilfe des Adressoperators zu:
int zahl = 42; int *zahlzeiger = &zahl;

Den Wert, auf den ein Zeiger zeigt, rufen Sie mithilfe des Dereferenzierungsoperators ab:
int zahl = 42; int *zahlzeiger = &zahl; NSLog(@"Die Antwort ist %i", *zahlzeiger);

Schauen wir uns den Unterschied zwischen einem Zeiger auf einen Wert eines bestimmten Typs (int *) und einem Wert eines bestimmten Typs (int) anhand eines Beispiels an:
int a = 1; int b = a; int *zeiger = &a; a = 2; NSLog(@"b ist %i, zeiger zeigt auf %i",b , *zahl); // Liefert "b ist 1, zeiger zeigt auf 2"

a und b sind gewhnliche Werttypvariablen. Wird b der Wert von a zugewiesen, wird einfach der Wert von a kopiert. Sptere nderungen von a wirken sich nicht auf den Wert von b aus. b behlt also den Wert 1, den a hatte, als sein Wert b zugewiesen wurde.

24 | Kapitel 3: Objekte

zeiger hingegen ist ein Zeiger auf einen Wert des Typs int. Einer

solchen Variablen wird kein Wert, sondern die Adresse eines Werts zugewiesen. ndert sich der Wert an jener Adresse, ist das ber die Zeigervariable sichtbar. zeiger zeigt auf die Adresse, an der a gespeichert ist, und die Dereferenzierung von zeiger liefert stets den aktuellen Wert von a. Zeiger auf Zeiger bieten eine weitere Ebene der Indirektion. Ein Zeiger auf einen Zeiger wird deklariert, indem der * verdoppelt wird. Folgender Code macht zeigerzeiger zu einem Zeiger auf einen Zeiger auf einen int-Wert und weist ihm die Adresse des int-Zeigers zeiger zu:
int zahl = 42; int *zeiger = &zahl int **zeigerzeiger = &zeiger;

In C (und C++) werden Zeiger auf Zeiger z. B. hufig zur Realisierung mehrdimensionaler Arrays eingesetzt. In Objective-C (d. h. Cocoa) werden sie eingesetzt, um Zeiger auf Objekte zu bergeben. Ein Beispiel dafr und eine Illustration dieser Technik finden Sie in Kapitel 7, Fehler und Ausnahmen.
Beachten Sie, dass viele der in C blichen Zeigermanipulationen unter Objective-C bei Zeigern auf Objective-C-Objekte nicht zulssig sind.

Dynamisch typisierte Variablen: der Typ id


Objective-C definiert einen speziellen statischen Typ fr Objektvariablen: id. id ist als Zeiger auf ein C-Struct definiert, das nur ein einziges Feld, ein isa-Feld des Typs Class, hat. -Class ist ein weiterer Struct-Typ, der als eine opake Referenz auf eine Klasse dient. Mit einer Variablen des Typs id knnen Sie beliebige Arten von Objekten festhalten, zum Beispiel:
id text = @"Die Welt ist alles, was der Fall ist"; NSLog(@"%@", text);

Objektvariablen und Zeiger | 25

Weisen Sie einer id-Variablen ein Objekt zu, sorgt die ObjectiveC-Laufzeitumgebung dafr, dass deren Class-Feld entsprechend initialisiert wird. Nutzen Sie ein ber eine id-Referenz festgehaltenes Objekt, sorgt die Laufzeitumgebung dafr, dass die entsprechenden Objektoperationen angefordert werden. Bei Variablen des Typs id prft der Compiler nicht, ob die angeforderten Operationen untersttzt werden. Sie knnen auf einer id-Referenz also beliebige Operationen anfordern, ohne dass Sie die id-Referenz zuvor in eine Referenz eines Typs umwandeln mssen, fr den eine entsprechende Operation tatschlich definiert ist.
id ist also nicht mit Javas Object vergleichbar, sondern eher mit dem dynamic-Typ von C#.

Statisch typisierte Variablen


Objekte knnen auch durch typisierte klassische C-Zeiger reprsentiert werden. Wenn ein Objekttyp namens Objekt definiert ist, knnen Sie also, analog zur *zahl-Deklaration oben, folgendermaen eine *objekt-Variable definieren:
Objekt *objekt = //Irgendwie ein Objekt erzeugen

Diese Variable vom Typ Objekt * speichert also eine Speicheradresse, an der ein Objekt des Typs Objekt gespeichert wird. Eigentlich interessiert Sie daran aber nur, dass sie Ihnen als Referenz auf den dort gespeicherten Objektwert dient: Dass Ihre Variable ein Zeiger ist, ist, wenn Sie im Objective-C-Kontext bleiben, nur bei der Deklaration sichtbar. In Ihrem Code gehen Sie mit ihr (in der Regel) nicht anders um als mit einer gewhnlichen Referenzvariablen in anderen objektorientierten Programmiersprachen. Statisch mit einem konkreten Typ typisierte Variablen haben den Vorteil, dass der Compiler statisch prfen kann, ob die auf der Referenz angeforderten Operationen von der entsprechenden Art von Objekt untersttzt werden. Der Eigenschaftszugriff ber die Punktnotation und der direkte Feldzugriff werden nur bei statisch typisierte Variablen untersttzt.

26 | Kapitel 3: Objekte

Objekte erstellen
Will ein Programm mit einem Objekt arbeiten, muss das System im Speicher Platz dafr schaffen und diesen ordentlich initialisieren. In Java, C# und vielen anderen Sprachen werden all diese Operationen angefordert und im Hintergrund abgewickelt, wenn eine Konstruktorfunktion mit dem new-Operator aufgerufen wird. Objective-C kennt keinen new-Operator. Stattdessen werden die notwendigen Arbeiten explizit angefordert, indem Nachrichten eingesetzt werden, die konventionsgem bestimmte Operationen wie die Reservierung und Initialisierung des Speichers fr ein Objekt vornehmen. Diese Nachrichten sind die Klassennachrichten +alloc und +new (diese Nachricht ist Ihnen in unserem zweiten Beispiel bereits begegnet) und die Objektnachricht init (bzw. Objektnachrichten, deren Name mit init beginnt). alloc sorgt dafr, dass der Speicherplatz fr ein Objekt bereitgestellt wird, init dafr, dass dieser Speicherplatz korrekt initialisiert wird. blicherweise werden diese Methoden folgendermaen gemeinsam verwendet:
id ding = [[Ding alloc] init];

new ist einfach ein Alias fr diese Operation, d. h., folgende Zeile ist

der vorangegangenen vollkommen quivalent:


id ding = [Ding new];

Die Basisklasse der Cocoa-Plattform, NSObject, stellt die Grundimplementierungen fr diese Nachrichten bereit. Eine ausfhrliche Beschreibung finden Sie in Kapitel 9, Objektlebenszyklus. Wenn Objekte bei der Erstellung mit bestimmten Werten initialisiert werden sollen, knnen Klassen Initialisierungsmethoden anbieten, die Parameter erwarten. Diese haben konventionsgem einen Namen, der mit init beginnt. Beispielsweise bietet die Klasse NSString eine initWithFormat:-Methode, mit deren Hilfe ein Stringobjekt auf Basis eines Formatstrings initialisiert werden kann. Wie Sie die Initialisierungsmethoden Ihrer eigenen Klassen gestalten, wird in Kapitel 9, Objektlebenszyklus, beschrieben.

Objekte erstellen | 27

Mit Objekten interagieren


In Objective-C haben Objekte Felder und Eigenschaften und verstehen Nachrichten. Gemeinsam bilden diese Komponenten die Schnittstelle, ber die Ihr Code mit Objekten interagieren kann. Felder stellen die Daten Ihres Objekts dar und sind einfach an spezifische Objekte gebundene Variablen. Eigenschaften sind Konstrukte, bei denen der Zugriff auf ein Feld ber Codeeinheiten gesteuert und so von der Implementierung der Datenspeicherung isoliert wird. Die Nachrichten, die ein Objekt versteht, sind die Operationen, die ein Objekt beherrscht, und entsprechen in der Regel einer Codeeinheit, die mit dem Objekt als Kontext zur Ausfhrung kommt. Das C-Erbe fhrt dazu, dass in Objective-C drei verschiedene Syntaxformen fr diese drei Elemente der Schnittstelle von Objekten existieren: 1. Mit der Pfeilnotation, dem klassischen Dereferenzierungsoperator von C, greifen Sie auf die Felder des referenzierten Objekts zu:
objekt->nr = objekt->nr + 1;

2. Mit der Punktnotation interagieren Sie mit seinen Eigenschaften:


objekt.anzahl = objekt.anzahl + 5;

3. ber die Nachrichtensyntax mit eckigen Klammern senden Sie dem Objekt eine Nachricht, was in der Regel dazu fhrt, dass der Code in einer Methode ausgefhrt wird:
Objekt *kopie = [objekt kopieren];

Beachten Sie, dass der Feld- und der Eigenschaftszugriff nur dann mglich ist, wenn die jeweiligen Referenzen statisch typisiert sind und der Compiler statisch prfen kann, ob die entsprechenden Operationen untersttzt werden. Das bedeutet, dass Sie ber Referenzen des Typs id nicht (direkt) auf Felder zugreifen knnen, whrend der Eigenschaftszugriff ber den direkten Aufruf der Accessor-Methoden weiterhin mglich bleibt.

28 | Kapitel 3: Objekte

Ein Beispiel:
Schiff *titanic = schiff_bauen(@"Titanic");

Der Schiff *-Zeiger titanic zeigt jetzt auf ein Schiff-Objekt vorausgesetzt, die C-Funktion schiff_bauen() liefert einen Zeiger auf ein Objective-C-Objekt mit dem Typ Schiff. Wir knnen ber diesen Zeiger nun alle Operationen anfordern, die fr den Typ Schiff definiert sind ganz wie man es erwartet, wenn man mit streng typisierten Programmiersprachen wie C oder Java vertraut ist.
NSString *name = titanic->name; [titanic auslaufen]; titanic.schubProzent = 50;

Nutzen Sie hingegen eine id-Referenz, um ein Objekt festzuhalten, knnen Sie auf dieser Referenz nur noch Operationen vornehmen, die der Compiler nicht statisch prfen muss:
id titanic = schiff_bauen(@"Titanic"); [titanic auslaufen]; [titanic setSchubProzent: 50];

Bei einer id-Referenz haben Sie keinen Zugriff auf das name-Feld. Auf die schubProzent-Eigenschaft knnen Sie zugreifen, mssen dabei aber die Zugriffsmethoden nutzen, hier den Setter -setSchub Prozent:.

Objekte vergleichen
Objekte werden ber Zeiger festgehalten. Werden Zeiger ber den ==-Operator verglichen, werden einfach die Werte der Zeiger, d. h. die Speicheradressen, die sie speichern, verglichen. Zwei Objektzeiger werden gem ==-Operator also nur dann als gleich betrachtet, wenn sie auf dasselbe Objekt zeigen, zum Beispiel:
id text = @"123"; id gleicherText = [NSString stringWithFormat:@"%@", @"123"]; id gleicheReferenz = text; BOOL test 1 = text == gleicherText; // NO BOOL test 2 = text == gleicheReferenz; // YES

Objekte vergleichen | 29

Wenn die Objekte einer Klasse ein anderes Vergleichsverhalten aufweisen sollen, muss die Klasse entsprechende Einrichtungen bereitstellen. Alle Objekte besitzen eine isEqual:-Methode, die standardmig das gleiche Verhalten aufweist wie der ==-Operator. Klassen knnen diese berschreiben, um ein anderes Vergleichsverhalten zu definieren (siehe Kapitel 11, Laufzeitinformationen). Manche Klassen definieren darber hinaus spezielle Vergleichsmethoden. Beispielsweise definiert die Klasse NSString eine isEqualToString:-Nachricht, die Stringvergleiche schneller durchfhrt, wenn sichergestellt ist, dass beide Objekte Strings sind.

Objekt-Enumeration
Wenn ein Objekt ein Container, d. h. ein Behlter ist, der andere Objekte enthlt, knnen seine Elemente mit der for (Typelement in container)-Schleifenstruktur enumeriert werden. Die Schnittstelle fr diese schnelle Enumeration wird im Framework-Protokoll NSFastEnumeration definiert. container kann nur ein Objekt sein, dessen Klasse dieses Protokoll implementiert. NSFastEnumeration wird z. B. von NSArray, NSDictionary und NSSet sowie von NSEnumerator implementiert. Zum Beispiel knnten wir die [preise enumerateObjectsUsingBlock: ]-Nachricht im abschlieenden Beispiel in Kapitel 2, Syntax, folgendermaen durch eine schnelle Enumeration ersetzen:
NSUInteger idx = 0; for (id obj in preise) { id format = idx == 0 ? @"%@" : idx < anzahl - 1 ? @", %@" : @" oder %@"; [verlosung appendFormat: format, obj]; idx++; }

Objektindizierung
Die von Objective-C seit Xcode 4.4 (LLVM 4.0) untersttzte Objektindizierung gestattet die Referenzierung der Elemente eines Col-

30 | Kapitel 3: Objekte

lection-Objekts oder eines Collection-hnlichen Objekts ber die klassische Array-Indizierungssyntax:


Objekt[Index]

Die Objektindizierung wird von den Cocoa-Klassen NSArray, NSSet und NSDictionary sowie ihren vernderlichen Unterklassen untersttzt.
Objekt[Index] kann als Ausdruck eingesetzt werden, der zum Wert des entsprechenden Elements ausgewertet wird, oder als Lvalue, um den Wert des entsprechenden Elements zu setzen (wenn Objekt eine vernderliche Collection reprsentiert). Zum Beispiel werden im nachfolgenden Code die Elemente des vernderlichen Arrays quadrate auf das Quadrat des Werts des entsprechenden Elements im Array zahlen gesetzt:
id zahlen = @[@0, @1,@2]; id quadrate = [NSMutableArray new]; for (int i = 0; i < [zahlen count]; i++) { quadrate[i] = @( [zahlen[i] integerValue]*[zahlen[i] integerValue]); }

Die Array-Werte mssen fr die arithmetische Operation erst mit der Nachricht -integerValue in elementare Zahlwerte umgewandelt werden (da die numerischen Operatoren fr Objekttypen nicht untersttzt werden). Das Ergebnis hingegen muss vor der Zuweisung in ein Objekt umgewandelt werden, wozu hier ein WrapperAusdruck genutzt wird, da die Collection-Typen nur Objektwerte speichern. Aus gleichem Grund werden bei Indizierungsausdrcken die zusammengesetzten Zuweisungsoperatoren nicht untersttzt.
Index kann (wie oben) ein ganzzahliger Wert oder ein Objektwert

sein. Ist der Index ein ganzzahliger Wert, wird eine Array-Indizierung genutzt, ist er ein Objektwert, eine Dicitionary-Indizierung. Beachten Sie, dass sich die Unvernderlichkeit der unvernderlichen Collection-Klassen nicht nur auf die Lnge bezieht. Dass sie unvernderlich sind, heit auch, dass sie keine Nachrichten zur nderung ihrer Elemente untersttzen. Folgende scheinbar unschuldige und vom Compiler anstandslos akzeptierte Alternative zu unserem obi-

Objektindizierung | 31

gen Code wird zur Laufzeit fehlschlagen, weil die gesendete Nachricht nicht untersttzt wird:
id zahlen = @[@0, @1,@2]; for (int i = 0; i < [zahlen count]; i++) { // Funktioniert nicht!!! zahlen[i] = @( [zahlen[i] integerValue]*[zahlen[i] integerValue]); }

Der Compiler kann die Operation nur prfen, wenn die Variable mit einem konkreten statischen Typ typisiert ist. ndern Sie den Typ von zahlen in NSArray *, meldet der Compiler einen Fehler, wenn der Indizierungsausdruck als Lvalue eingesetzt wird. Wie bei Objective-C blich, sind auch Indizierungsausdrcke nur syntaktischer Zucker, der vom Compiler zu den entsprechenden Nachrichten aufgelst wird. Bei der Array-Indizierung sind das objectAtIndexedSubscript: und setObject:atIndexedSubscript:, bei der Dictionary-Indizierung objectForKeyedSubscript: und setObject:forKeyedSubscript:. Sie knnen die Objektindizierung also auch auf Ihren eigenen Klassen untersttzen, indem Sie die entsprechenden Methoden implementieren. Es gibt allerdings kein Protokoll, das die entsprechende Schnittstelle beschreibt.

32 | Kapitel 3: Objekte

KAPITEL 4

Klassen

Objective-C nutzt das klassenbasierte Modell der objektorientierten Programmierung. Jedes Objekt ist eine Instanz einer Klasse; anonyme, allgemeine Objekte wie z. B. in JavaScript gibt es nicht. In logischer Hinsicht sind Klassen Typdefinitionen, die die Eigenschaften und das Verhalten der Objekte bestimmen, die Instanzen dieser spezifischen Klasse sind. Strukturell stellen sie die Einheiten dar, in die Sie Ihren Code gliedern. Sie arbeiten in Ihrem Code mit Objekten, aber wie sich die Objekte verhalten und wozu sie genutzt werden, bestimmen Sie in den Klassen, die Sie schreiben. In Ihrem Code sind Klassen Objekte, mit denen Sie ebenso arbeiten knnen wie mit den Objekten, die Instanzen von Klassen sind. In diesem Abschnitt werden wir uns mit den ersten beiden Aspekten befassen. In Objective-C sind die Deklaration und die Definition einer Klasse zwei separate Einheiten. Zunchst wird die Schnittstelle einer Klasse deklariert, d. h., es wird bekannt gemacht, was nach auen und innen sichtbar ist und fr den Code der Klasse bzw. fr Code, der Objekte des durch die Klasse definierten Typs nutzt, sichtbar ist.

Klassendeklaration und -definition


Objective-C nutzt Compilerdirektiven, um die Syntaxstrukturen fr die Deklaration und die Definition von Klassen zu markieren. Eine Klassendeklaration wird von der Compilerdirektive @interface eingeleitet, die Klassendefinition von der Compilerdirektive

| 33

@implementation. Dementsprechend bezeichnet man die Deklaration als die Schnittstelle und die Definition als die Implementierung.

Schnittstelle und Implementierung stehen blicherweise jeweils in eigenen Dateien. Das ist blich, aber nicht notwendig.

Schnittstellendeklaration
Die Schnittstelle enthlt die Deklaration der Klasse, bietet aber keine Implementierung. Objective-C-Schnittstellen sind wie C-Headerdateien und haben nichts mit Javas Interfaces zu tun. Sie machen die Struktur einer Klasse bekannt, damit ihre Instanzen von anderem Code genutzt werden knnen, der ihre Implementierung nicht kennt. Eine Klassendeklaration hat in Objective-C folgende Struktur:
#import "Oberklasse.h" @interface Klasse : Oberklasse { // In diesem Abschnitt stehen die Felddeklarationen } // Hier stehen die Eigenschafts- und Methodendeklarationen @end

Eine Klassendeklaration muss die Deklaration der Oberklasse einschlieen. Das erfolgt wie hier mit der Prprozessordirektive #import. Die eigentliche Deklaration wird von den Objective-C-Compilerdirektiven @interface und @end eingerahmt. Sie beginnt mit der Angabe des Namens der neuen Klasse. Auf ihn folgen ein Doppelpunkt und der Name der Oberklasse. Die Oberklasse ist die Klasse, die die neu definierte Klasse erweitert. Doppelpunkt und Oberklassenname entfallen nur, wenn Sie eine neue Basisklasse deklarieren wollen. Der Abschnitt Vererbung und Polymorphie auf Seite 56 geht ausfhrlicher auf diese Aspekte ein. Die anschlieenden geschweiften Klammern sind der Bereich, in dem die Felder oder Instanzvariablen der Klasse deklariert werden.

34 | Kapitel 4: Klassen

Methoden- und Eigenschaftsdeklarationen folgen nach der schlieenden geschweiften Klammer. Schnittstellendateien tragen den Namen der Klasse, die darin definiert wird, und die Dateinamenserweiterung *.h. blicherweise werden Klassenschnittstellen in eigenstndigen Dateien gespeichert theoretisch kann eine Schnittstellendateien aber beliebig viele @interface-Abschnitte enthalten.

Klassenimplementierung
Die Implementierung bietet die Definitionen zu den Deklarationen der Schnittstelle; in ihr wird der Code fr die Methoden formuliert. Implementierungen stehen in Dateien, die blicherweise die Dateinamenserweiterung .m tragen (.mm oder .M bei Objective-C++). Eine Implementierungsdatei enthlt blicherweise nur die Implementierung einer Klasse, kann theoretisch aber beliebig viele Klassenimplementierungen enthalten. Auch fr Klassen, die keine Methoden enthalten, muss eine (leere) Implementierungsdatei angegeben werden. Wird keine Implementierungsdatei angegeben, erzeugt der Compiler keinen Untersttzungscode fr die Klasse. Der Form nach sieht eine Klassenimplementierung folgendermaen aus:
#import "Klasse" @implementation Klasse // Hier steht die Implementierung @end

Eine Implementierung muss immer die eigene Schnittstelle importieren. Der Definitionscode steht zwischen den Compilerdirektiven @implementation und @end.

Felder
Felder halten die Daten eines Objekts fest. Es sind an die jeweilige Klasseninstanz gebundene Variablen. In Objective-C werden Felder hufig als Instanzvariablen oder kurz Ivars bezeichnet.

Felder | 35

Felder deklarieren
Felder werden in der Schnittstelle in einem in geschweiften Klammern stehenden Block deklariert.
@interface Kind : Elter { int alter; NSString* name; } @end

Felder knnen privat, geschtzt, ffentlich oder paketgebunden sein. Die Sichtbarkeit von Feldern wird bei der Felddeklaration mit den Compilerdirektiven, @private, @protected, @public und @package deklariert.
@private

Privat. Felder sind im Code der Klasse auf der Instanz selbst und auf anderen Instanzen sichtbar.
@protected

Geschtzt. Felder sind im Code der Klasse selbst und im Code von ihr abgeleiteter Klassen auf der Instanz selbst und auf anderen Instanzen sichtbar.
@public

ffentlich. Felder sind berall sichtbar.


@package

Paketsichtbarkeit. Felder sind innerhalb des Programms oder der Bibliothek sichtbar, in dem bzw. der die Klasse implementiert wird. Fr paketinternen Code verhalten sich die entsprechenden Felder wie ffentliche Felder, fr paketexternen Code wie private Felder. Diese Sichtbarkeit entspricht der internalSichtbarkeit in C# und der Standard- oder Package-Sichtbarkeit in Java. Eine Direktive gilt jeweils fr alle auf sie folgenden Felddeklarationen, bis sie von der nchsten Direktive berschrieben wird. Die Sichtbarkeit wird also in Blcken und nicht eigens fr jedes Feld deklariert, wie es z. B. in Java blich ist. Deklarationen, denen keine Sichtbarkeitsdirektive voransteht, erhalten die Sichtbarkeit @protected.

36 | Kapitel 4: Klassen

@interface Agent : Beamter { Auftrag *auftrag; //Implizit @protected @private Ort *position; @protected NSString *codeName; @public NSString *name; Agent *gegner; } @end

Felder referenzieren
Felder knnen mit dem ->-Operator referenziert werden, wenn die Referenz statisch typisiert ist. Beispielsweise greift agent->name auf das name-Feld eines (fiktiven) Agent-Objekts agent zu. Auf die Felder der aktuellen Instanz kann im Code der Klasse ohne Qualifikation durch das Objekt zugegriffen werden. Das bedeutet, self->name und name sind im Klassencode quivalent. Objekte erhalten alle Felder, die in den Schnittstellen der Klasse selbst und ihrer Oberklassen deklariert sind. Neben den in der eigenen Schnittstelle deklarierten Feldern knnen auch alle nicht als @private markierten Felder auf diese Weise referenziert werden.
Das heit natrlich auch, dass es unmglich ist, ber Referenzen, die nicht mit einem konkreten statischen Typen deklariert sind, auf Objektfelder zuzugreifen.

Statische Felder
Objective-C bietet keine spezielle Syntax fr statische Felder oder Klassenvariablen. Sie werden blicherweise emuliert, indem in der Implementierungsdatei eine Variable deklariert wird, die nur in der Kompilationseinheit sichtbar ist.
#import "Klasse.h" int anzahlObjekte;

Felder | 37

@implementation Klasse @end

Da eine solche Variable nach auen unsichtbar ist, mssen Sie den Zugriff darauf ber Methoden steuern.

Methoden
Methoden sind Funktionen, die mit einem Objekt oder einer Klasse verknpft sind. Objekt- oder Instanzmethoden werden genutzt, um Operationen auszufhren, die auf den Feldern der jeweiligen Instanz operieren. Sie knnen den von den Feldern bestimmten Zustand eines Objekts abfragen oder ndern. Klassenmethoden oder statische Methoden operieren auf der Klasse selbst und haben keinen Zugriff auf Felder eines spezifischen Objekts. In Objective-C definieren Methoden die Nachrichten, die ein Objekt versteht. Eine Methode aufrufen und einem Objekt eine Nachricht senden sind in Objective-C quivalente Ausdrcke. Der Empfnger fr eine Instanznachricht (Instanzmethode) ist eine Klasseninstanz, der Empfnger einer Klassennachricht (Klassenmethode) das Klassenobjekt, das die Klasse reprsentiert. Welche Nachrichten ein Objekt versteht und wie ein Objekt auf eine Nachricht reagiert, wird in Objective-C nicht vollstndig durch die Methoden bestimmt, die fr eine Klasse deklariert und definiert sind. Objective-C bietet Mechanismen, die es ermglichen, dynamisch zur Laufzeit festzulegen, wie ein Objekt auf eine Nachricht reagiert. Diese werden im Kapitel 12, Messaging, beschrieben. Anders als bei Feldern gibt es bei Methoden keine Mglichkeit, die Sichtbarkeit zu steuern. In der Schnittstelle deklarierte Methoden sind grundstzlich ffentlich. Trotzdem kann man Methoden definieren, die eine begrenzte Sichtbarkeit haben. Das beste und dazu geeignetste Mittel sind private Klassenerweiterungen (siehe Kapitel 5, Kategorien und Klassenerweiterungen, Abschnitt Klassenerweiterungen auf Seite 65).

38 | Kapitel 4: Klassen

Methoden deklarieren
Methoden werden blicherweise in der Schnittstelle deklariert. Bei der Deklaration wird der Empfnger (die Klasse oder eine Instanz), der Rckgabetyp, der Methodenname und eine Folge eventueller Parameter angegeben. Die allgemeine Syntax hat folgende Form:
Art [(Ru ckgabetyp)] Namensteil[: (Paramtyp) Paramname] [Namensteil: (Paramtyp) Paramname]

Art Methoden knnen Instanz- oder Klassenmethoden sein. Ob der Empfnger ein Objekt oder die Klasse ist, wird angegeben, indem der Deklaration bei einer Instanzmethode ein --Zeichen vorangestellt wird und bei einer Klassenmethode ein +-Zeichen. Einer der beiden Indikatoren muss angegeben werden. Rckgabetyp Gibt den Typ des Werts an, den eine Methode zurckliefert. Dieser wird gem der in Sprachen der C-Familie blichen Syntax fr Typumwandlungen in einer Klammer angegeben. Der Typ kann void sein (die Methode liefert nichts zurck) oder ein beliebiger von Objective-C untersttzter Datentyp bzw. ein Zeiger auf einen solchen. Wird kein Rckgabetyp angegeben, wird der Methode der Standardrckgabetyp id zugewiesen. Namensteil Der Name einer Methode kann aus einem oder mehreren Namensteilen bestehen. Ein Namensteil muss vorhanden sein. Wenn eine Methode einen Parameter hat, wird dieser im Namen durch einen Doppelpunkt eingeleitet. Weiteren Parametern gehen weitere mit weiteren Doppelpunkten abgeschlossene Namensteile voran. Paramtyp Gibt den erwarteten Typ des Parameters an und steht wie der Rckgabetyp in Klammern. Paramname Der Name des Parameters.

Methoden | 39

Konkret sieht das so aus: Methoden ohne Parameter Die allereinfachste Methodendeklaration knnte folgende Gestalt haben:
-ding;

Diese Deklaration besteht nur aus einer Empfngerangabe und einem Methodennamen. Sie deklariert eine Instanzmethode mit dem Namen ding, die keine Parameter erwartet und einen Wert des Typs id zurckliefert. Im Sinne der Transparenz sollten Sie auf die Angabe des Rckgabetyps nicht verzichten. Die quivalente vollstndige Deklaration sieht also so aus:
-(id) ding;

Methoden mit einem Parameter


Wenn eine Methode einen Parameter erwartet, folgt dieser nach einem Doppelpunkt auf den Methodennamen:
-(void) machWasMit: (id) ding;

Diese Deklaration deklariert eine Instanzmethode mit dem Namen machWasMit, die einen Parameter des Typs id erwartet und keinen Wert zurckliefert. Der Doppelpunkt ist Teil des Methodennamens (hier lautet dieser also machWasMit:). Der Parametertyp wird wie der Rckgabetyp in Klammern angegeben.

Methoden mit mehreren Parametern


Methodenname und Parameterliste sind in Objective-C keine separaten Einheiten, sondern werden ineinander verwoben.
- (void) machWasMit: (id) ding und: (id) nochEinDing;

Der vollstndige Methodenname lautet dann machWasMit:und:. Es ist nur ein Namensteil notwendig, d. h., die Namensteile, aber nicht die Doppelpunkte vor weiteren Parametern, knnen weggelassen werden. Eine Methode, die zwei Parameter erwartet, knnte also auch so deklariert werden:

40 | Kapitel 4: Klassen

- (void) machWasMitDingUndDing: (id) ding1 : (id) ding2;

Der Methodenname wre dann machWasMitDingUndDing::. Von dieser Form wird abgeraten. Da die Parameterliste Teil des Methodennamens ist, gibt es in Objective-C keine Methodenberladung. Ein alternatives Konzept sind die sogenannten Methodenfamilien wie z. B. die Familie der init-Methoden. Eine Methodenfamilie umfasst Methoden mit hnlichem Funktionsbereich, deren Name durch das gleiche Schlsselwort eingeleitet werden.

Methodenimplementierung
Jeder Nachrichtendeklaration in der Schnittstelle sollte eine Nachrichtendefinition in der Implementierung entsprechen. Wenn der Compiler in der Implementierung keine kompatible Definition finden kann, warnt er, dass die Implementierung unvollstndig ist. Eine Definition beginnt mit einer Methodensignatur, die der in der Deklaration entspricht. Auf diese folgt ein Paar geschweifter Klammern, in dem der eigentliche Methodeninhalt definiert wird, zum Beispiel:
@interface Zahl : NSObject { @public int zahl; } -(int) malInt: (int) faktor; @end @implemention Zahl -(int) malInt: (int) faktor { return zahl * faktor; } @end

Der Compiler ist nicht immer dazu in der Lage, unvollstndige oder falsche Implementierungen zu erkennen und zu melden. Abweichende Rckgabe- oder Parametertypen meldet er nur, wenn einer der Typen ein Objective-C-Typ (int, float usw.) ist. Beispielsweise wrde folgende Implementierung zu zwei Warnungen fhren, die Sie darauf aufmerksam machen, dass Rckgabetyp und Parametertyp nicht der Deklaration entsprechen, weil fr beides statt int long als Typ angegeben wird:
Methoden | 41

@implemention Zahl -(long) malInt: (long) faktor { return zahl * faktor; } @end

Wenn die Typen in Deklaration und Definition jeweils Objektzeiger sind, meldet der Compiler keinerlei Abweichungen. Folgende Deklaration
+(Zahl *) zahlAusString: (NSString *) text;

wrde fr den Compiler auch durch eine Definition wie


+(NSNumber *) zahlAusString: (NSArray *) text { ... }

erfllt. Da das zur Laufzeit im besten Fall zum unmittelbaren Programmabbruch, im schlimmsten Fall zu unvorhersagbaren Ergebnissen fhrt, sollten Sie derartige Fehler vermeiden.

self
Der Methodeninhalt definiert die Operation, die eine Methode fr die Klasse oder das Objekt stellt. Er kann auf alle Strukturen zugreifen, die im Code der Klasse bekannt sind (weil sie in der Kompilationseinheit selbst definiert werden oder mit #import in die Kompiliationseinheit importiert werden). Ein entscheidender Unterschied zu C und den Methoden in anderen objektorientierten Programmiersprachen ist, dass Objective-C-Methodendefinitionen die Nachrichten definieren, die eine Klasse bzw. die Objekte einer Klasse verstehen, und keine einfachen, an ein Objekt oder eine Klasse gebundenen Funktionen darstellen. Wenn Sie dem aktuellen Objekt eine Nachricht senden, brauchen Sie dazu also einen Empfnger.
self ist ein spezieller Empfnger, der in allen Methodendeklarationen definiert ist. Was self reprsentiert und welche Operationen mit self mglich sind, ist allerdings von der Art der Methode abhngig. In Code, der ARC-basiert ist (siehe Kapitel 10, Speicherverwaltung), gelten folgende Regeln:

42 | Kapitel 4: Klassen

In einer Klassenmethode (im Beispiel unten +zahlMitInt:) ist self ein konstanter Wert des Typs Class, der die Klasse selbst reprsentiert. In einer gewhnlichen Instanzmethode (im Beispiel unten z. B. -alsInt) ist self ein konstanter Zeiger auf einen Wert des Klassentyps, mit dem interagiert, der aber nicht gendert werden kann. In einer Instanzmethode der init-Familie (im Beispiel unten -initMitInt:) ist self ein Zeiger auf einen Wert des Klassentyps, der gendert werden kann. Schauen wir uns zur Illustration die Implementierung der folgenden Schnittstelle an:
@interface Zahl : NSObject { int zahl; } +(id) zahlMitInt: (int) wert; -(id) initMitInt: (int) wert; -(void) malInt: (int) faktor; -(void) malZahl: (Zahl *) andereZahl; -(void) zumQuadrat; -(int) alsInt; @end

Da die Ivar zahl in allen Instanzmethoden zugreifbar ist, knnen wir -alsInt einfach implementieren, indem wir den Wert von zahl zurckliefern:
-(int) alsInt { return zahl; }

Der Zugriff auf -zahl kann wie oben unqualifiziert oder mit selfqualifiziert erfolgen, da die Ivar auf dem aktuellen Objekt gelesen werden soll:
-(int) alsInt { return self->zahl; }

Beide Formen sind im Prinzip quivalent. Die Qualifizierung mit self verhindert, dass versehentlich auf eine lokale Variable zuge-

Methoden | 43

griffen wird, die das Feld verdeckt. Der Compiler warnt, wenn eine lokale Variable erstellt wird, die ein Feld verdeckt. Ebenso knnen wir -malZahl: implementieren, indem wir den Wert von zahl mit dem Wert des Arguments multiplizieren:
-(void) malInt: (int) faktor { self->zahl *= faktor; }

Aber wir knnten -zumQuadrat nicht wie z. B. in Java implementieren, indem wir unqualifiziert eine malInt()-Methode aufrufen. -malInt ist eine Nachricht und muss an einen explizit anzugebenden Empfnger gesendet werden. Im Klassencode wird die aktuelle Instanz mit self angegeben:
-(void) zumQuadrat { [self malInt: self->zahl]; }

Da zumQuadrat eine gewhnliche Instanzmethode ist, kann self nur als Empfnger fr Nachrichten bzw. als Objektzeiger zur Interaktion mit Ivars genutzt werden. In Methoden der init-Familie (mehr Informationen dazu finden Sie in Kapitel 9, Objektlebenszyklus, im Abschnitt Objektinitialisierung auf Seite 89) sind darber hinaus auch Zuweisungen an self erlaubt:
-(id) initMitInt: (int) wert { self = [self init]; self->zahl = wert; return self; }

Anders als z. B. this in Java ist self auch in Klassenmethoden definiert. In Klassenmethoden reprsentiert self allerdings nicht die Instanz, sondern das Class-Objekt, das der Compiler auf Basis der Klassendefinition bereitstellt. ber das Class-Objekt knnen Sie Informationen zur Klasse erhalten und neue Klasseninstanzen erstellen. Aber da es keine Instanz ist, knnen Sie auf ihm nicht auf Instanzvariablen oder -methoden zugreifen, sondern nur auf Klassenmethoden bzw. auf Funktionen, Variablen usw., die in der Kompilationseinheit deklariert sind.

44 | Kapitel 4: Klassen

Diese self-Referenz knnen Sie in Klassenmethoden beispielsweise nutzen, um sicherzustellen, dass stets ein Objekt mit dem richtigen Laufzeittyp erstellt und geliefert wird:
+(id) zahlMitInt: (int)wert { return [[self alloc] initMitZahl: wert]; }

Wird diese Klassennachricht einer UngeradeZahl-Unterklasse von Zahl gesendet, wird eine UngeradeZahl-Instanz erstellt, da self das Klassenobjekt reprsentiert, das Empfnger der Nachricht ist. Wre hier fest der Typ der Klasse vorgegeben worden, in der die Methode definiert wird (Zahl), wrde ein Zahl-Objekt erstellt.

Methoden aufrufen
In Objective-C werden die Begriffe Methode aufrufen und einem Objekt eine Nachricht senden hufig synonym verwendet, obwohl der zweite Begriff eigentlich weiter ist. In statischen Programmiersprachen bewirkt der Aufruf einer Methode (oder Funktion), dass vorab festgelegter Code ausgefhrt wird. Der Aufruf wird statisch, d. h. bei der Kompilierung aufgelst. Objective-C hingegen ist eine dynamische Programmiersprache, in der Aufrufe zur Laufzeit aufgelst werden. Objective-C-Objekte empfangen Nachrichten und entscheiden zur Laufzeit, welcher Code aufgerufen wird. Das kann der Code einer vorab definierten Methode sein, knnte aber auch Code sein, der erst zur Laufzeit bestimmt wird. Die definierten Methoden stellen also nicht notwendigerweise alle Nachrichten dar, die ein Objekt kennt (siehe Kapitel 12, Messaging). Da Sie das nun wissen: In der Regel definieren Sie die Nachrichten, die ein Objekt versteht, indem Sie Methoden als Funktionen implementieren, die an ein Objekt gebunden sind. Und wenn Sie einem Objekt eine Nachricht senden, fhrt das in der Regel dazu, dass eine Methode aufgerufen wird, die auf diese Weise implementiert wurde. Aber weder das eine noch das andere muss der Fall sein. Wie in Kapitel 2, Syntax, bereits erlutert, wird eine Methode aufgerufen, indem einem Empfnger eine Nachricht gesendet wird:
[Empfa nger Nachricht]

Methoden | 45

Nachfolgend finden Sie zur Auffrischung noch ein paar Beispiele dafr, wie Sie einige der Methoden aufrufen, die wir im Abschnitt Methoden deklarieren auf Seite 39 deklariert haben:
// Eine Nachricht ohne Parameter [ding ding]; // Eine Nachricht mit einem erwarteten Parameter [zahl zahlMalInt: 3]; // Eine Klassennachricht mit mehreren Parametern [Person initMitName: @"Hugo" alter: 33 augenfarbe: @"Gru n"];

Die [ding ding]-Zeile wird Ihnen vielleicht seltsam erscheinen. In Objektive-C gibt es unabhngige Namensbereiche fr Instanzvariablen und Nachrichten. Eine Instanzvariable, die den gleichen Namen wie eine Nachricht hat, fhrt deswegen nicht zu Konflikten.

Varargs-Methoden
Objective-C nutzt die gleiche Syntax und Infrastruktur fr Methoden mit Untersttzung fr eine beliebige Anzahl an Argumenten wie C. Die Deklaration in der Schnittstellendatei hat folgende Gestalt.
-(void) verarbeiteDinge: (id) ding1, ...;

Der Indikator , ... zeigt an, dass hier ein Varargs-Parameter folgt. Einem Varargs-Parameter muss immer mindestens ein benannter Parameter vorangehen, der in der Regel (aber nicht notwendigerweise) wie oben das erste Element in der Liste der erwarteten Argumente reprsentiert. Zur Implementierung von Varargs-Methoden werden in Objective-C der Typ va_list und die Makros va_start, va_args und va_end genutzt, die alle in der __stdargs.h__-Headerdatei der C-Standardbibliothek definiert sind.
va_start initialisiert eine va_list-Struktur in Bezug auf den letzten benannten Parameter in der Parameterliste. va_args ruft das nchste Argument mit dem angegebenen Typ aus der Liste ab. va_end

schliet die angegebene Argumentliste, wenn sie nicht mehr bentigt wird.

46 | Kapitel 4: Klassen

Mchten Sie einen Varargs-Parameter nutzen, mssen Sie irgendwie in Erfahrung bringen knnen, wie lang die Liste ist bzw. wann sie zu Ende ist. In der Cocoa-Bibliothek werden hufig nil-terminierte Listen genutzt. Eine solche knnten Sie folgendermaen implementieren:
-(void) machWasMit: (id) ding1, ... { va_list args; va_start(args, ding1); // Was mit ding1 machen id ding; while ((ding = va_arg(args, id))) { //Was mit den restlichen u bergebenen Argumenten machen } va_end(args); }

Der Header fr die Varargs-Infrastruktur muss gewhnlich nicht eigens importiert werden, da er bereits von den wahrscheinlich zum Einsatz kommenden Cocoa-Headern importiert wird. Eine Varargs-Methode wie diese rufen Sie auf, indem Sie VarargsArgumente durch Kommata getrennt angeben und das Ende der Argumentliste mit nil anzeigen:
[machWasMit: ding1, ding2, ding3, nil];

Varargs-Parameterlisten mssen nicht notwendigerweise nil-terminiert sein. Es gibt z. B. Framework-Methoden wie stringWithFormat, die kein nil am Ende der Argumentliste erwarten:
[NSString stringWithFormat: @"Zahl %i und Wort %@", 1, @"Eins"]

Bei dieser Form gibt es nichts, was das Ende der Argumentliste anzeigt. Der Compiler ist aber in der Regel dazu in der Lage, Sie zu warnen, wenn Anzahl und Typ der Parameter falsch sein knnten.

Eigenschaften
Eigenschaften bzw. Properties kapseln und schtzen den Zugriff auf die Daten von Objekten. Implementiert werden sie in Objective-C wie in anderen Programmiersprachen ber ein privates Feld und zwei Zugriffsmethoden, eine zum Setzen und eine zum Abrufen des

Eigenschaften | 47

Feldwerts. In Objective-C haben diese Zugriffsmethoden konventionsgem folgende Signaturen:


-( Typ ) Name (Getter) -(void) setName : ( Typ ) Variable (Setter).

In Objective-C sollten Sie anders als bei vielen anderen Programmiersprachen blich den Namen des Getters nicht mit get beginnen. Dieses Prfix zeigt gem den Cocoa-Konventionen Methoden an, die Werte ber Referenzen bearbeiten. Da die Namen von Feldern und Methoden in Objective-C separat verwaltet werden, kann der Getter den gleichen Namen wie das Hintergrundfeld haben. Theoretisch knnte auch das set in Settern weggelassen werden, da der Doppelpunkt, der das Argument anzeigt, Teil des Namens ist aber diese Verwendung gehrt nicht zu den gngigen Objective-C-Konventionen.

Manuell wrde man eine anzahl-Eigenschaften also auf folgende Weise deklarieren und implementieren:
@interface Dinge : NSObject { @private int _anzahl; } -(void) setAnzahl: (int) anz; -(int) anzahl; @end @implementation Dinge -(void) setAnzahl: (int) anz { _anzahl = anz; } -(int) anzahl { return _anzahl; } @end

48 | Kapitel 4: Klassen

Eigenschaften deklarieren
Objective-C vereinfacht den Einsatz von Eigenschaften durch eine integrierte Syntax fr ihre Deklaration und Definition. Sie knnen mit der @property-Direktive in der Schnittstelle deklariert werden. Diese wird folgendermaen verwendet:
@property [(Attribut, )] Typ Eigenschaft;

Attribut

Die Attribute der Eigenschaftsdeklaration bestimmen Aussehen und Verhalten der Eigenschaft. Eine vollstndige Liste finden Sie in Kapitel 10, Speicherverwaltung, im Abschnitt Eigenschaftsattribute auf Seite 106.
Typ

Gibt den Datentyp der Eigenschaft an.


Eigenschaft

Der Name der Eigenschaft. In der einfachsten Verwendung hat die @property-Direktive folgende Form:
@interface Dinge : NSObject { } @property int anzahl; @end

Diese @property-Deklaration ist der manuellen Deklaration im letzten Abschnitt mit Zugriffsmethoden mit den Standardsignaturen fr Getter und Setter quivalent. Alle Eigenschaften werden mit einer eigenen, mit einem Semikolon abgeschlossenen @property-Direktive deklariert. In der Schnittstelle deklarierte Eigenschaften sind wie Methoden grundstzlich ffentlich sichtbar. Nur die Les- und Schreibbarkeit kann ber Eigenschaftsattribute gesteuert werden. Private Eigenschaften knnen wie private Methoden mithilfe von Klassenerweiterungen (siehe Kapitel 5, Kategorien und Klassenerweiterungen, Abschnitt Klassenerweiterungen auf Seite 65) erstellt werden.

Eigenschaften | 49

Synthetisierung deklarierter Eigenschaften


Deklarierte Eigenschaften knnen explizit synthetisiert oder manuell implementiert werden. Gibt es weder eine explizite Synthetisierung noch eine manuelle Implementierung, werden deklarierte Eigenschaften seit Xcode 4.4 automatisch synthetisiert.

Automatische Synthetisierung
Automatische Synthetisierung heit, dass der Compiler fr eine @property-Deklaration in der Schnittstellendatei das Hintergrundfeld und die erforderlichen Zugriffsmethoden in der Implementierungsdatei erzeugt. Fr eine Eigenschaft, die mit der Direktive @property (Typ) eigenschaft deklariert wurde, erzeugt der Compiler: ein privates Hintergrundfeld mit dem Namen _eigenschaft einen Getter mit der Signatur - (Typ) eigenschaft einen Setter mit der Signatur - (void) setEigenschaft: (Typ)
wert

Anders gesagt: Wenn Sie eine Eigenschaft nicht weiter konfigurieren mssen, reicht es aus, sie in der Schnittstelle mit @property zu deklarieren. Die erforderlichen Einrichtungen zum Arbeiten mit der Eigenschaft erhalten Sie dann automatisch. Bei der oben deklarierten Eigenschaft anzahl hiee das, dass ein int-Feld des Namens _anzahl und zwei Methoden mit den Signaturen -(int) anzahl (Getter) und -(void) setAnzahl: (int) wert definiert sind und genutzt werden knnen.

Explizite Synthetisierung
Zur expliziten Synthetisierung knnen Sie in der Implementierungsdatei die @synthesize-Direktive nutzen. Eine explizite Synthetisierung ist bei neueren Werkzeugversionen in der Regel berflssig. @synthesize wird folgendermaen verwendet:
@synthesize eigenschaft[=feld], ...;

@synthesize weist den Compiler an, Zugriffsmethoden mit den Namen eigenschaft und setEigenschaft: sowie ein Hintergrund-

50 | Kapitel 4: Klassen

feld mit dem Namen eigenschaft oder, wenn =feld angegeben ist, mit dem Namen feld zu synthetisieren, falls in der Schnittstelle kein entsprechendes Feld deklariert wurde. In der einfachsten Form hat eine @synthesize-Direktive folgende Form:
@synthesize anzahl;

Bei dieser expliziten Synthetisierung werden wie bei der automatischen Synthetisierung ein anzahl-Getter und ein setAnzahl:-Setter generiert. Anders als bei einer automatischen Synthetisierung wird dem Namen des Hintergrundfelds kein Unterstrich vorangestellt. Der Name ist anzahl, nicht _anzahl. Das ist etwas, das Sie bei der Umstellung von einer expliziten auf eine automatische Synthetisierung (oder umgekehrt) beachten mssen. Bei der Form
@synthesize anzahl=_menge;

wrde statt eines anzahl-Hintergrundfelds ein _menge-Hintergrundfeld synthetisiert, falls im Feldabschnitt der Schnittstelle kein entsprechendes Feld deklariert ist.
Beachten Sie, dass synthetisierte Hintergrundfelder mit deklarierten Ivars in Konflikt treten knnen.

Deklarierte Eigenschaften implementieren


Sie knnen Zugriffsmethoden auch manuell implementieren. Das ist z. B. sinnvoll, wenn Sie mehr Funktionalitt bentigen, als die Standard-Getter und -Setter bieten, zum Beispiel.:
@interface Dinge :NSObject { } @property (nonatomic) int anzahl; @end @implementation Dinge -(void) setAnzahl: (int) wert { if (wert > 0) _anzahl = wert;

Eigenschaften | 51

} @end

Der Compiler erkennt automatisch, dass -setAnzahl: ein explizit definierter Setter fr die deklarierte Eigenschaft anzahl sein soll, und synthetisiert nur noch den Getter und das Hintergrundfeld. Wenn Sie synthetisierte und selbst definierte Zugriffsmethoden kombinieren, mssen Sie die Eigenschaft mit dem Attribut nonatomic versehen. Eigenschaften sind standardmig atomar, und die synthetisierten Methoden werden entsprechend implementiert. Implementieren Sie eine der Zugriffsmethoden manuell, beschwert sich der Compiler, weil Sie atomare und nicht atomare Zugriffsmethoden nicht kombinieren knnen. Alternativ knnen Sie beide Zugriffsmethoden manuell definieren.

@dynamic
Die Direktive @dynamic garantiert dem Compiler, dass die Implementierung einer deklarierten Eigenschaft zur Laufzeit zur Verfgung stehen wird. Sie bewirkt, dass er nichts in Bezug auf die Eigenschaft unternimmt. Er generiert keine Zugriffsmethoden und Hintergrundfelder bzw. meldet keine Warnungen oder Fehler.
@dynamic wird wie @synthesize verwendet:
@dynamic Eigenschaft;

Wenn Sie @dynamic nutzen, mssen Sie dafr sorgen, dass Objekte zur Laufzeit auf die entsprechenden Nachrichten reagieren, z. B. indem Sie die Nachricht ber den Weiterleitungsmechanismus von einem anderen Objekt verarbeiten lassen (siehe Kapitel 12, Messaging).

Eigenschaftszugriff
Eigenschaften sind ein Konstrukt, dessen ffentliche Schnittstelle zwei Methoden sind. Diese knnen Sie jederzeit explizit nutzen:
Dinge *dinge = [Dinge new]; [dinge setAnzahl: 5]; int zahl = [dinge anzahl];

52 | Kapitel 4: Klassen

Neben der Nachrichtensyntax untersttzt Objective-C 2.0 die aus anderen objektorientierten Programmiersprachen bekannte Punktnotation fr den Zugriff auf Eigenschaften. Das heit, bei statisch typisierten Referenzen kann stattdessen folgender Code genutzt werden:
Dinge *dinge = [Dinge new]; dinge.anzahl = 5; int anzahl = dinge.anzahl;

Vom Compiler wird dieser Code zu den gleichen Aufrufen aufgelst wie der vorangehende nachrichtenbasierte Code. Die Punktnotation bietet den Vorteil, dass der Compiler statisch prfen kann, ob das Objekt die entsprechende Nachricht untersttzt. Auerdem lassen sich viele Operationen kompakter formulieren, da Zuweisungen ihre C-Semantik bewahren, d. h. Ausdrcke sind, die zum Wert der Eigenschaft ausgewertet werden. Der Code liee sich also noch weiter vereinfachen:
Dinge *dinge = [Dinge new]; int anzahl = dinge.anzahl = 5;

Auerdem sind neben der einfachen Zuweisungsanweisung auch die zusammengesetzten Zuweisungsanweisungen definiert. Das bedeutet,
[dinge setAnzahl: [dinge anzahl] + 5];

kann mithilfe der Punktnotation zu


dinge.anzahl += 5;

verkrzt werden. Komplexe Anweisungsfolgen wie


[dinge setAnzahl: 5]; [dinge setAnzahl: [dinge anzahl] * 5]; int anzahl = [dinge anzahl];

knnten zu Folgendem verkrzt werden:


int anzahl = dingeAnzahl *= dingeAnzahl = 5;

Eigenschaften | 53

Beachten Sie, dass die Punktnotation nur bei Objektreferenzen genutzt werden kann, die statisch mit dem Typ (oder einer Unterklasse des Typs) deklariert sind, fr den die Eigenschaft definiert wurde. Andernfalls (d. h. bei einer Referenz des Typs id oder einer Referenz des Typs einer Oberklasse des Typs, fr den die Eigenschaft definiert ist) schlgt die statische Typprfung des Compilers und damit die Kompilierung fehl, obwohl die dynamische Nachrichtenauflsung zur Laufzeit die entsprechenden Methoden finden wrde.

Die Punktnotation kann nicht nur verwendet werden, wenn die Eigenschaft explizit mit der @property-Direktive deklariert wurde, sondern auch, wenn die Eigenschaft vollstndig manuell mit anzahl- und setAnzahl:-Nachrichten implementiert wurde. Da der Compiler die Punktnotation in die entsprechenden Nachrichten an das Objekt bersetzt, knnte sie theoretisch auch fr Nicht-Eigenschaftsnachrichten verwendet werden. Von dieser Verwendung wird jedoch abgeraten.

Eigenschaftsattribute
Die @property-Direktive untersttzt eine Reihe von Attributen zur Steuerung des Zugriffs, der Setter-Operationsweise, der Namen der Zugriffsmethoden und zur Threading-Untersttzung. Diese Attribute werden in Klammern hinter der @property-Direktive angegeben. Mehrere Attribute werden durch Kommata getrennt:
@property (Attribut1, Attribut2, ...) Typ Eigenschaft;

Geben Sie hinter @property keine Attribute an, werden die Standardattribute genutzt. Eine attributlose Eigenschaftsdeklaration (@propertyTyp Eigenschaft) entspricht unter ARC der Deklaration @property (readwrite,strong) Eigenschaft. Eigenschaften sind standardmig atomar, d. h., Eigenschaftsoperationen werden als Einheit abgewickelt (siehe @nonatomic weiter unten).

54 | Kapitel 4: Klassen

Setter- und Getter-Namen definieren


Die folgenden Attribute knnen Sie verwenden, um die Namen der zu nutzenden Zugriffsmethoden manuell festzulegen.
setter= Name

Zeigt an, dass die Setter-Methode den Namen Name hat. Wenn in der Implementierungsdatei keine entsprechende Methode vorhanden ist, wird sie synthetisiert (es sei denn, die Direktive @dynamic wird verwendet).
getter= Name

Zeigt an, dass die Getter-Methode den Namen Name hat. Wenn in der Implementierungsdatei keine entsprechende Methode vorhanden ist, wird sie synthetisiert (es sei denn, die Direktive @dynamic wird verwendet). Die Attribute in den nachfolgenden Bereichen schlieen sich gegenseitig aus, d. h., es kann jeweils nur ein Attribut aus einem Bereich angegeben werden.

Zugriffsteuerung
Die folgenden Attribute steuern den Zugriff auf eine Eigenschaft.
readwrite

Zeigt an, dass eine Eigenschaft lesbar und schreibbar ist. In der Implementierungsdatei mssen entweder explizit eine Setterund eine Getter-Methode definiert werden, oder Setter- und Getter-Methoden werden synthetisiert (der Standardwert).
readonly

Zeigt an, dass eine Eigenschaft schreibgeschtzt ist. In der Implementierungsdatei muss nur eine Setter-Methode definiert sein, bzw. es wird nur eine Setter-Methode synthetisiert.

Setter-Operation
Alle nachfolgenden Attribute steuern die Zuweisung an die Eigenschaft und die Speicherverwaltung. retain und assign kommen bei Referenzzhlung zum Einsatz, strong und weak bei Automatic Reference Counting. Die Speicherverwaltung und die Bedeutung der

Eigenschaften | 55

Eigenschaftsattribute wird in Kapitel 10, Speicherverwaltung, genauer erlutert.


assign

Einfache Zuweisung. Wird blicherweise fr skalare Werte oder (bei Referenzzhlung) fr Objekte genutzt, die nicht in der Klasse erstellt wurden (der Standard).
retain

Bei der Zuweisung wird dem neuen Wert die retain-Nachricht gesendet, dem alten die release-Nachricht.
strong

Eine starke Referenz. Solange es eine starke Referenz gibt, wird das Objekt nicht dealloziert. Entspricht retain und ist der Standard bei Automatic Reference Counting.
weak

Eine schwache Referenz. Wird unter ARC genutzt und entspricht assign, sorgt aber dafr, dass die Referenz auf nil gesetzt wird, wenn das Objekt dealloziert wird.
copy

Fr die Zuweisung wird eine Kopie des bergebenen Objekts verwendet. Die Kopie wird mit der copy-Methode erstellt, d. h., dieses Attribut kann nur fr Objekte eingesetzt werden, die das NSCopying-Protokoll untersttzen. Dem alten Wert wird eine release-Nachricht gesendet.

t Atomarita
nonatomic

Zeigt an, dass die Zugriffsmethoden nicht atomar sind. Standardmig sind Zugriffsmethoden atomar, ein entsprechendes Attribut gibt es jedoch nicht.

Vererbung und Polymorphie


Die Klassen einer Programmierschnittstelle stehen in einem hierarchischen Verhltnis, das dem der Familien, Gattungen und Arten naturwissenschaftlicher Taxonomien vergleichbar ist. Wenn Sie

56 | Kapitel 4: Klassen

sich eine solche Klassenhierarchie wie einen Stammbaum vorstellen, bei dem die Wurzel oben ist, finden Sie unten engere oder spezifischere Klassen, die mit zunehmender Genauigkeit die spezifischen Eigenschaften einer immer kleiner werdenden Gruppe von Objekten beschreiben. Oben finden Sie weitere oder allgemeinere Klassen, die eine nach oben hin immer grer werdende Menge von Objekten unter einer stets schrumpfenden Anzahl von Gemeinsamkeiten beschreiben. An der Spitze dieser Hierarchie steht die Basisklasse (siehe Kapitel 8, NSObject), die die grundlegenden Gemeinsamkeiten aller Objekte beschreibt, die Exemplare der ihr untergeordneten spezielleren Klassen sind. Alle anderen Klassen sind erweiternde oder abgeleitete Klassen, die jeweils den Begriff ihrer Elternklassen um die fr ihre Exemplare spezifischen Eigenschaften erweitern. Mit Ausnahme der Basisklasse sind alle Klassen einer Klassenhierarchie abgeleitete Klassen. Ein Objekt, das eine Instanz einer abgeleiteten Klasse B ist, besitzt neben den speziellen Eigenschaften und Verhalten von Objekten der Klasse B alle speziellen und geerbten Eigenschaften und Verhalten von Objekten der Klasse A, wenn A die Elternklasse von B ist. Schauen wir uns zur Illustration zwei Klassen A und B an, die durch folgende Schnittstellen beschrieben werden (wie im Abschnitt Schnittstellendeklaration auf Seite 34 beschrieben, wird die Elternklasse in der Schnittstellendeklaration mit einem Doppelpunkt nach dem Klassennamen angegeben):
@interface A : NSObject @property int a; -(int) zumQuadrat; @end @interface B : A @property int b; -(int) aMalB; @end

Die Klasse A ist von NSObject, der Basisklasse der Cocoa-Klassenhierarchie, abgeleitet. In Objective-C muss die Basisklasse immer explizit angegeben werden. Wird in einer Klassendeklaration keine Elternklasse angegeben, wird eine neue Basisklasse deklariert. Die Klasse B ist von A abgeleitet.

Vererbung und Polymorphie | 57

Vererbung
Vererbung bedeutet, dass eine abgeleitete Klasse alles erbt, was ihre Elternklassen definieren. Die Deklarationen oben vorausgesetzt, heit das, dass B alle Eigenschaften und Verhalten von A und NSObject erbt, da A die Basisklasse NSObject erweitert und B seinerseits A erweitert. Konkret bedeutet es, dass im Code von B alle nicht privaten Ivars, Methoden und Eigenschaften von A (und NSObject) sichtbar sind. In B kann auf die geerbte Eigenschaft a also zugegriffen wie auf die eigene Eigenschaft b. Die Implementierung von B knnte z. B. so aussehen:
@implementation B -(int) aMalB { return self.b*self.a; } @end

Fr einen Nutzer von Objekten des Typs B gibt es keinen Unterschied zwischen den eigenen und den geerbten Eigenschaften und Methoden, zum Beispiel:
B *bObjekt = [B new]; // new wurde u ber A von NSObject geerbt if (!(bObjekt.a == bObjekt.b && [bObjekt zumQuadrat] == [bObjekt bMalA])) { NSLog(@"Groe Katastrophe: 0^2 != 0*0"); }

Vererbung ermglicht es, die Eigenschaften, die eine Gruppe von Klassen gemeinsam haben, in einer gemeinsamen Elternklasse zusammenzufassen. Beachten Sie an diesem Punkt, dass Klassen in Objective-C grundstzlich konkret sind. Es bietet keine Einrichtung zur Definition abstrakter Klassen. In Objective-C knnen Klassen anders als z. B. in C++ nur eine Elternklasse haben. Protokolle (siehe Kapitel 6, Protokolle) bieten eine zustzliche Mglichkeit Gemeinsamkeiten in der Schnittstelle zu beschreiben.

berschreiben Eigenschaften und Verhalten u


Wenn eine abgeleitete Klasse einen Aspekt des Verhaltens einer Elternklasse ndern will, kann sie die entsprechende Definition in einer beliebigen Elternklasse durch eine eigene Definition ber-

58 | Kapitel 4: Klassen

schreiben. Die Neudefinition in der abgeleiteten Klasse verdeckt dann nach auen hin die Definition in der Elternklasse. Zum Beispiel knnte B folgendermaen die -zumQuadrat-Methode von A neu definieren:
@implementation B -(int) zumQuadrat { return pow(self.b, 2); } @end

Die ursprngliche -zumQuadrat-Methode von A ist nun auf B-Objekten nicht mehr verfgbar. Im Unterschied zu Sprachen wie Java ist es in Objective-C auch mglich und blich, Klassenmethoden zu berschreiben:

super
In einer abgeleiteten Klasse kann auf Elternklassenelemente ber den speziellen Empfnger super zugegriffen werden. super sorgt dafr, dass mit der Suche nach einer Nachrichtendefinition bei der Elternklasse begonnen wird. Zum Beispiel knnte man folgendermaen mit super in der Implementierung von B auf die zumQuadratDefinition von A zugreifen:
-(int) aQuadrat { return [super zumQuadrat]; } @end

Ansonsten verhlt sich super fast genau so wie self (siehe self auf Seite 42). Der einzige Unterschied ist, dass auch in init-Methoden keine Zuweisung an super erlaubt ist.
Beachten Sie, dass super und die superclassNachricht unterschiedliche Funktionen haben. super sorgt dafr, dass die Methodenauflsung bei der Oberklasse ansetzt, verndert die Klasse von self aber nicht. superclass hingegen liefert das Klassenobjekt, das die Oberklasse reprsentiert.

Vererbung und Polymorphie | 59

Polymorphie
Der Begriff Polymorphie verweist darauf, dass eine Referenz des Typs T Objekte aller abgeleiteten Klassen X, Y, Z usw. festhalten kann und dass ein Objekt vom Typ T ber Referenzen aller Elternklassen A, B, C usw. festgehalten werden kann. Wenn ein Objekt den Typ B hat, der von einem Typ A abgeleitet ist, kann es als Objekt des Typs B oder als Objekt des Typs A behandelt werden. Wird es als Objekt des Typs A behandelt, werden von ihm nur Dinge verlangt, die Objekte des Typs A beherrschen. Da B alle Eigenschaften und Verhalten von A erbt, ist garantiert, dass ein Objekt des Typs B allen Anforderungen gengt, die an ein Objekt des Typs A gestellt werden knnen:
A *bObjekt = [B new]; bObjekt.a = 3; NSLog(@"%i", [bObjekt zumQuadrat]);

Ntzlich wird das, wenn man Objekte unterschiedlichen, aber verwandten Typs in Bezug auf bestimmte Operationen gemeinsam behandeln kann. Stellen Sie sich z. B. vor, Sie mssten eine TankwartKlasse fr eine Verkehrssimulation schreiben. Sie wrde nur interessieren, dass die Objekte, mit denen Sie zu tun haben, die -(void) tankFuellen: (float) liter-Nachricht verstehen. Ob ein Fahrzeug ein Ferrari, ein Reisebus, eine Harley oder selbst ein Ausflugsdampfer ist, ist fr einen Tankwart vollkommen irrelevant. Die betanken-Methode von Tankwart knnte also folgende Gestalt haben:
-(void) betanken: (Kfz *)kfz { while (kfz.benzin < kfz.tankvolumen) { [kfz tankFuellen: 1]; }

Und so knnte sie verwendet werden:


Ferrari *roterBlitz = [Ferrari new]; Bus *linie1 = [Bus new]; [tankwart betanken: roterBlitz]; [tankwart betanken: linie1];

In Objective-C ist diese Vielfrmigkeit nichts Besonderes, fr das von der Sprache spezielle Einrichtungen bereitgestellt werden. Objektreferenzen sind einfach mit Typinformationen versehene Zeiger, die fr alle Objekte gleich sein. Sie knnen deswegen von Haus aus
60 | Kapitel 4: Klassen

Werte beliebigen Typs referenzieren. Ein Zeiger auf einen Objekttyp kann beispielsweise in einen Zeiger auf jeden beliebigen anderen Objekttyp umgewandelt werden. Der Compiler htte gegen Folgendes in Objective-C kein Einwnde:
[tankwart betanken: (Kfz *)@"Auto"];

Die Typumwandlung selbst wrde auch zur Laufzeit noch gelingen, und ein Fehler wrde erst auftauchen, wenn in -betanken auf dem NSString-Objekt Operationen aufgerufen werden, die von NSStringObjekten nicht untersttzt werden (siehe Abschnitt Typumwandlungen unten). Objective-C untersttzt deswegen auch das sogenannte Duck Typing, das man blicherweise eher mit Skriptsprachen ohne statische Typprfung verbindet.

Typumwandlungen
Vererbungsbeziehungen spielen bei der Umwandlung von Zeigern auf Objekttypen nur insofern eine Rolle, als dass implizite Upcasts (Umwandlungen in einen Elterntyp) erkannt und akzeptiert werden. Ansonsten fhren implizite Typumwandlungen immer zu einer Compilerwarnung, whrend alle expliziten Typumwandlung unkommentiert akzeptiert werden. Beispielsweise fhrt folgende Zuweisung bei der Kompilierung nur zu einer Warnung, obwohl die Klassen NSArray und NSString nicht in einem Vererbungsverhltnis stehen:
NSArray *text = @"Riskant";

Mit einem expliziten Cast knnen wir sogar diese Warnung unterdrcken:
NSArray *text = (NSArray *)@"Riskant";

Bei einer Sprache wie Java wrde die implizite wie die explizite Typumwandlung zu einem Kompilierungsfehler fhren. Zur Laufzeit gelingen Typumwandlungen, explizite wie implizite, immer (so etwas wie eine ClassCastException kennt Objective-C nicht). Dass Zeiger- und Objekttyp inkompatibel sind, fllt erst auf, wenn zur Laufzeit auf dem Zeiger eine Operation angefordert wird, die das Objekt nicht untersttzt.
Typumwandlungen | 61

KAPITEL 5

Kategorien und Klassenerweiterungen

Kategorien bieten eine Mglichkeit, bestehenden Klassen, auch eingebauten Framework-Klassen oder anderen Klassen, ber deren Quellcode Sie nicht verfgen, nachtrglich Methoden hinzuzufgen. Sie ermglichen Ihnen also, die Funktionalitt einer Klasse nachtrglich zu erweitern eine Technik, die unter dem Namen Monkey Patching bekannt ist. Alle Methoden, die Sie in einer Kategorie auf einer Klasse definieren, werden zu einem Teil der ursprnglichen Klasse. Die Deklaration einer Kategorie wird angezeigt, indem bei der Schnittstellendeklaration hinter dem Klassennamen in Klammern der Name der Kategorie angegeben wird:
#import "Klasse.h" @interface Klasse (Kategorie) // Methodendeklarationen @end

Kategorien knnen nur Methodendeklarationen enthalten, keine Felder es gibt also keinen Abschnitt mit Felddeklarationen. Das heit auch, dass Sie in Kategoriedeklarationen zwar mit @property neue Eigenschaften deklarieren, diese in der Implementierungsdatei aber nicht mit @synthesize synthetisieren knnen Getter und Setter mssen manuell definiert werden und knnen nur die Einrichtungen, d. h. Felder nutzen, die von der ursprnglichen Klassendeklaration bereitgestellt werden. Tun Sie das nicht, meldet der Compiler nur eine Warnung, keinen Fehler. Kategorienamen haben

| 63

einen eigenen Namensraum. Sie knnten beispielsweise also eine Kategorie mit dem gleichen Namen wie die Klasse definieren. Wenn Sie die Kategorie im gleichen Header wie die Klasse selbst deklarieren, sind die darin deklarierten Methoden vollwertige Klassenmethoden und knnen genau so eingesetzt und aufgebaut werden wie die Methoden der Klasse selbst. Kategoriemethoden haben Zugriff auf alle deklarierten Einrichtungen der Klasse und knnen beispielsweise sogar private Felder lesen. Deklarieren Sie die Kategorie in einem eigenen Header, mssen Sie in diesen den Header fr die Klassendeklaration einschlieen. Auerdem mssen Sie in Code, der die Kategorie nutzt, zustzlich zum Klassenheader den Kategorieheader einbinden. Tun Sie das nicht, meldet der Compiler einen Fehler, wenn Sie ARC (siehe Kapitel 10, Speicherverwaltung) nutzen, oder eine Warnung, wenn Sie ARC nicht nutzen. Ansonsten unterscheiden sich die entsprechenden Methoden in keiner Weise von Kategoriemethoden, die in der Klassendatei selbst deklariert werden. Dateien fr Kategorien erhalten konventionsgem einen Namen der Form klasse+kategorie.h und klasse+kategorie.m. Wenn Sie in einer Kategorie eine Methode deklarieren, die den gleichen Namen wie eine Methode in der erweiterten Klasse hat, berschreibt die neue Deklaration die ursprngliche Version. Der super-Aufruf hat bei Kategoriemethoden die gleiche Semantik wie bei gewhnlichen Klassenmethoden und ruft eine eventuelle Oberklassenversion der Methode auf und nicht die Methode aus der erweiterten Klasse. Es gibt keine Mglichkeit, eine berschriebene Methode der erweiterten Klasse aufzurufen. Es ist nicht festgelegt, was passiert, wenn mehrere Kategorien, die die gleiche Klasse erweitern, eine Methode mit dem gleichen Namen definieren. Vermeiden Sie diese Situation. Eine Kategorieimplementierung wird angezeigt, indem in der Klassenimplementierung der Name der Kategorie in Klammern hinter dem Klassennamen angegeben wird:

64 | Kapitel 5: Kategorien und Klassenerweiterungen

#import "Klasse+Kategorie.h" @implementation Klasse (Kategorie) //Methodendefinitionen @end

Die Methoden einer Kategorie mssen nicht implementiert werden. Wenn ein Implementierungsabschnitt fr eine Kategorie vorhanden ist, in dem die in der Kategorie deklarierten Methoden nicht vollstndig definiert werden, meldet der Compiler eine Warnung. Gibt es keinen Implementierungsabschnitt, gibt es berhaupt keine Meldung. Eine deklarierte, aber nicht definierte Kategorie bezeichnet man als informelles Protokoll (siehe Kapitel 6, Protokolle).

Klassenerweiterungen
Klassenerweiterungen sind anonyme Kategorien. Sie werden deklariert, indem der Name zwischen den Klammern nach dem Klassennamen weggelassen wird:
interface Klasse ()

Der Unterschied zu einer gewhnlichen Kategorie ist, dass die in einer Klassenerweiterung deklarierten Methoden im eigentlichen Implementierungsabschnitt fr Klasse definiert werden mssen. Klassenerweiterungen knnen Sie also nur fr Klassen schreiben, bei denen Sie Zugriff auf den Quellcode haben. Ab Clang/LLVM 2.0 knnen Klassenerweiterungen im Gegensatz zu Kategorien auch Feld- und Eigenschaftsdeklarationen enthalten. Klassenerweiterungen werden vorwiegend eingesetzt, um die private Schnittstelle einer Klasse zu deklarieren. Dazu wird die Klassenerweiterung in der Implementierungsdatei, nicht der Schnittstellendatei deklariert:
#import "ding.h" @interface Ding () @end @implementation Ding @end

Klassenerweiterungen | 65

KAPITEL 6

Protokolle

Protokolle sind Zusammenstellungen von Methodendeklarationen, die nicht mit einer bestimmten Klasse verknpft sind. Sie sind das Objective-C-quivalent zu Javas Interfaces und erfllen die gleiche Funktion. (Genauer gesagt, entsprechen Javas Interfaces den Protokollen von Objective-C, da Javas Interfaces von Objective-C-Protokollen inspiriert wurden.) Ein Protokoll wird nicht instantiiert. Klassen deklarieren ihre Konformitt zu einem Protokoll und garantieren damit, dass ihre Instanzen auf die entsprechenden Nachrichten reagieren. Der Zweck von Protokollen ist es, Klassen, die nicht in einem Vererbungsverhltnis stehen, eine bereinstimmung in Bezug auf eine spezifische Methodenschnittstelle deklarieren zu lassen. Konzeptionell sind Protokolle als weniger problematische Alternative zur Mehrfachvererbung gedacht. Nutzen Sie Protokolle, wenn man Klassen aus den unterschiedlichsten Klassenhierarchien zu bestimmten Zwecken gleich behandeln knnen soll.

Ein Protokoll deklarieren


Protokolle werden mit der @protocol-Direktive deklariert:
@protocol Protokoll // Methodendeklarationen @end

Die Methodendeklarationen in einem Protokoll haben genau die gleiche Form wie bei einer Klassendeklaration, zum Beispiel:

| 67

@protocol Exportierbar -(id) exportieren; -(id) exportierenAls: (int) exportformat; -(NSArray *) exportformateAlsListe; @end

Standardmig sind alle Methoden erforderlich, die ein Protokoll deklarieren. Eine Klasse, die ein Protokoll adoptiert, muss alle Methoden implementieren, die von diesem Protokoll deklariert werden. Optionale Protokollmethoden, die implementiert werden knnen, aber nicht implementiert werden mssen, knnen mit der @optionalDirektive deklariert werden. Folgende Deklaration wrde beispielsweise die beiden letzten Methoden des Protokolls optional machen:
@protocol Exportierbar -(id) exportieren; @optional -(id) exportierenAls: (int) exportformat; -(NSArray *) exportformateAlsListe; @end

Die @optional-Direktive bleibt in Geltung, bis sie durch eine neue Direktive aufgehoben wird. In einem @protocol-Abschnitt knnen weitere erforderliche Methoden deklariert werden, indem die @required-Direktive vorgeschaltet wird, zum Beispiel:
@protocol Exportierbar @optional -(id) exportierenAls: (int) exportformat; -(NSArray *) exportformateAlsListe; @required -(id) exportieren; @end

Es ist empfehlenswert, die @required- und @optional-Direktiven einzusetzen, um erforderliche und optionale Methoden eindeutig zu kennzeichnen.

Protokollhierarchien
Ein Protokoll kann die bereinstimmung mit anderen Protokollen deklarieren:
@protocol Protokoll <Protokoll_x, Protokoll_y, >

68 | Kapitel 6: Protokolle

Die Methoden des adoptierten Protokolls mssen nicht neu deklariert werden. Eine Klasse, die das bergeordnete Protokoll adoptiert, muss alle nicht optionalen Methoden aller Protokolle in der Liste implementieren. Es ist blich, alle eigenen Protokolle das NSObject-Protokoll bernehmen zu lassen:
@protocol Protokoll <NSObject>

Das NSObject-Protokoll definiert die grundlegende Schnittstelle der NSObject-Klasse. Wenn Sie Ihre Protokolle die Konformitt zum NSObject-Protokoll deklarieren lassen, sorgen Sie dafr, dass Sie auf Referenzen, die auf den Protokolltyp eingeschrnkt werden (siehe unten), die NSObject-Methoden aufrufen knnen, die von den referenzierten Objekten als Instanzen von Klassen aus der NSObject-Hierarchie notwendigerweise untersttzt werden. Wie Sie aus der Existenz dieses NSObject-Protokolls schlieen knnen, gibt es einen eigenen Namensbereich fr Protokolle. Sie knnen fr Protokolle also die gleichen Namen nutzen wie fr Klassen oder Kategorien. blicherweise haben Protokolle wie Java-Interfaces aber Namen, die auf -ing enden.

rtsdeklaration Vorwa
Wenn Ihre Protokolldeklarationen ein anderes Protokoll als Typ fr die Parameter oder Rckgabewerte von Methoden nutzen, knnen Sie die @protocol-Direktive hnlich der @class-Direktive auch einsetzen, um ein Protokoll vorab zu deklarieren:
@protocol Protokoll;

Protokolle implementieren
Die bereinstimmung bzw. bernahme eines Protokolls wird in Objective-C mit spitzen Klammern angezeigt. Bei einer Klassendeklaration geben Sie die Namen der bernommenen Protokolle in spitzen Klammern hinter einem wahrscheinlichen Oberklassennamen an:
@interface Klasse: Oberklasse <Protokoll>

Protokolle implementieren | 69

Sie knnen Protokolle auch in Kategorien implementieren:


@interface Klasse (Kategorie) <Protokoll>

Zwischen den spitzen Klammern steht der Name eines Protokolls oder eine kommaseparierte Liste von Protokollnamen. Wenn das Protokoll in einer anderen Datei deklariert wird, mssen Sie gegebenenfalls den entsprechenden Header importieren, zum Beispiel:
#import "exportierbar.h" @interface Dokument : NSObject <Exportierbar>

Eine Klasse, die die bereinstimmung mit einem Protokoll deklariert, garantiert, dass sie alle nicht optionalen Methoden des Protokolls implementiert. Die Deklaration bewirkt, dass die im Protokoll deklarierten Methoden auch fr die Klasse deklariert sind. Die Protokollmethoden mssen in der Klasse also nicht noch einmal deklariert, sondern nur noch in der Implementierungsdatei definiert werden. Definiert die Klasse bzw. Kategorie nicht alle nicht optionalen Methoden, die in den Protokollen deklariert werden, meldet der Compiler eine Warnung. Beachten Sie, dass diese Warnung entfllt, wenn das Protokoll auf einer Kategorie deklariert wird, fr die es keinen Implementierungsabschnitt gibt.

Optionale Methoden aufrufen


Wenn Sie eine als @optional deklarierte Protokollmethode aufrufen wollen, sollten Sie zuvor unbedingt prfen, ob diese von der entsprechenden Klasse implementiert wird. Dazu knnen Sie die -respondsToSelector:-Nachricht des NSObject-Protokolls nutzen, zum Beispiel:
if([dokument respondsToSelector: @selector(exportierenAls:)]) { [dokument exportierenAls: (int) Exportformate.RTF]; }

Beachten Sie, dass Sie die respondsToSelector:-Nachricht an Empfnger, die mit dem Protokolltyp deklariert sind, nur dann senden knnen, wenn das Protokoll selbst die Konformitt zum NSObjectProtokoll deklariert, da die Nachricht fr den Empfnger andernfalls nicht deklariert ist.
70 | Kapitel 6: Protokolle

Protokolle als Referenztypen


Bei der Deklaration von Feldern, Eigenschaften und Variablen sowie den Rckgabe- und Parametertypen von Methoden knnen Sie angeben, dass die referenzierten Objekte einem oder mehreren Protokollen entsprechen:
id <Protokoll> @property Eigenschaft;

oder:
Klassenname <Protokoll> *Variable;

Beachten Sie, dass Sie das Protokoll nicht einfach anstelle eines Typs angeben knnen wie z. B. in Java (Protokoll *Variable versteht der Compiler also nicht). Ein Protokoll ist kein Typ und kann deswegen auch nur als zustzliche Information zu einem Variablentyp verwendet werden. Wie eine Klassenangabe ermglicht die Protokollangabe dem Compiler, statisch zu prfen, ob Operationen, die auf dem referenzierten Objekt vorgenommen werden, dem deklarierten Typ der Variablen entsprechen. Das heit, der Compiler meldet einen Fehler, wenn auf dieser Referenz eine Operation vorgenommen wird, die dem entsprechenden Protokoll nicht konform ist. Diese Technik bietet Ihnen sozusagen eine protokollbasierte Polymorphie. Sie ermglicht Ihnen, heterogene Objekte unter dem Aspekt ihrer Konformitt zu einem Protokoll einheitlich zu behandeln, ohne auf die Vorteile einer statischen Typisierung verzichten zu mssen.

Informelle Protokolle
Einige der Eigenschaften von Protokollen knnen Sie emulieren, indem Sie eine Kategorie deklarieren, aber nicht implementieren. Eine solche Kategorie bezeichnet man als informelles Protokoll, weil man nicht anzeigen kann, ob es implementiert wird oder nicht. Informelle Protokolle knnen also nicht genutzt werden, um den Typ einer Variablen genauer anzugeben, und bieten deswegen keine Mglichkeit fr eine protokollbasierte Polymorphie.

Informelle Protokolle | 71

Informelle Protokolle wurden genutzt, um Methoden zu deklarieren, die Unterklassen optional implementieren knnen. Eine Unterklasse, die ein informelles Protokoll implementiert, verweist nicht auf die ursprngliche Deklaration, sondern deklariert in der eigenen Schnittstelle, welche der Methoden sie implementiert, und implementiert diese dann wie gewhnlich in der Implementierungsdatei. Dieses Muster wurde in neueren Objective-C-Versionen von der @optional-Direktive abgelst.

72 | Kapitel 6: Protokolle

KAPITEL 7

Fehler und Ausnahmen

Objective-C und Cocoa unterscheiden Fehler und Ausnahmen. Fehler sind Probleme, die auf uere Umstnde zurckgehen, beispielsweise wenn eine Datei nicht lesbar, ein Netzwerk nicht erreichbar ist oder ein Dokument das falsche Format hat. Ausnahmen hingegen sind Probleme, die auf innere Umstnde, d. h. programminterne Bedingungen, zurckgehen, z. B. dass eine Methode einen Argumentwert erhlt, mit dem sie nichts anfangen kann, oder dass ein Array-Index die Array-Grenzen verletzt. Es gibt eigene Klassen, NSError und NSException, zur Beschreibung und Kapselung von Fehlern bzw. Ausnahmen, und es werden unterschiedliche Programmstrukturen und -konventionen zu ihrer Bearbeitung eingesetzt. Diese Unterscheidung von Fehler und Ausnahme und die Verwendung dieser Begriffe unterscheidet sich erheblich von den Konventionen in vielen anderen Programmiersprachen. In Objective-C sind Ausnahmen die Ausnahme und Fehler die Regel! Wenn ein Problem auftaucht, das sich im Kontext Ihres Programms beheben lsst, sollten Sie Fehler nutzen. Ausnahmen sollten Sie nur verwenden, wenn ein Problem auftaucht, das zum Abbruch des Programms fhren sollte. Wenn Sie eine Parallele zu Java suchen: Fehler entsprechen in der Regel einer checked Exception, Ausnahmen hingegen einem Error oder einer RuntimeException.

| 73

Fehler
Bei manchen Operationen muss man immer mit dem Auftreten von Fehlern rechnen und kann die Information zu Fehlern und eventuelle Wiederherstellungsoptionen gleich in das Programm einbauen. In Objective-C und Cocoa-Programmen nutzen Sie dazu NSErrorObjekte, die neben einem Fehlercode Informationstexte und sogar Button-Beschriftungen und damit verbundene Wiederherstellungsaktionen fr einen Dialog kapseln knnen, in dem der Fehler dem Benutzer prsentiert werden kann. NSError-Objekte werden von Methoden, die derartige Operationen unternehmen, hufig nicht ber den Rckgabewert geliefert, sondern ber einen Referenzparameter. Die Methode erwartet ein NSError **-Argument d. h. statt einer Kopie eines Zeigers einen Zeiger auf einen Zeiger. Betrachten wir beispielsweise die stringWithContentsOfURL:-Methode der Klasse NSString, mit der Sie den Inhalt einer ber eine URL identifizierten Ressource in einen String laden knnen. Diese hat folgende Signatur:
+ (id)stringWithContentsOfURL:(NSURL *)url encoding:(NSStringEncoding)enc error:(NSError **)error

Eine solche Methode nutzen Sie blicherweise folgendermaen


NSError * fehler; id inhalt = [NSString stringWithContentsOfURL: urlEinerDatei encoding: eineKodierung error: &fehler]; if (inhalt == nil) { // Fehlerverarbeitung, hier z. B. Protokollierung auf der Konsole NSLog(@"Fehler: %@", fehler); }

Zunchst deklarieren Sie einen NSError-Zeiger eine Referenz, die noch auf nichts zeigt , und dann bergeben Sie die Adresse dieses Zeigers an die Methode. Wenn es whrend der Ausfhrung der Methode zu einem Fehler kommt, wird an dieser Adresse der Zeiger auf ein passendes NSError-Objekt gespeichert. Ist die Ausfhrung der Methode beendet, zeigt fehler dann auf das in der Methode erstellte NSError-Objekt (falls in der Methode eines erstellt wurde).

74 | Kapitel 7: Fehler und Ausnahmen

Beachten Sie, dass man den Fehler blicherweise nicht vom Vorhandensein eines NSError-Objekts ableitet, sondern aus dem Rckgabewert der Methode, z. B. daraus, dass dieser nicht definiert ist.
NSError-Objekte bieten ber die Nachrichten -code und -domain

Zugriff auf den Fehlercode und die Fehlerdomain. Weitere Informationen werden in einem userInfo-Dictionary gespeichert. Einige NSError-Hilfsmethoden bieten Zugriff auf standardmig im user Info-Dict vorhandene Schlssel. localizedDescription, localiz edFailureReason und localizedRecoverySuggestion liefern Strings mit zunehmend detaillierteren Beschreibungen des Fehlers, localizedRecoveryOptions Buttonbeschriftungen fr einen NSAlertDialog und recoveryAttempter ein Delegate-Objekt, das mgliche Programmaktionen als Reaktion auf den Fehler definiert. Wenn Sie eigene Fehlerobjekte erstellen wollen, knnen Sie die Klassennachricht +errorWithDomain:code:userInfo oder die Instanznachricht initWithDomain:code:userInfo: nutzen.

Ein Beispiel
Der folgende Code deklariert eine Methode, die im Fehlerfall ein NSError-Objekt erstellt und dieses ber den Referenzparameter fehler zurckliefert. Das Dict fr den userInfo-Parameter wird hier nur vorab definiert, um die Anweisung, die den Fehler definiert, bersichtlicher zu halten:
- (NSString *) antwort: (NSError **) fehler { if ([self frage] == nil) { id info = @{ NSLocalizedDescriptionKey : @"Leider gibt es keine Antwort!", NSLocalizedRecoverySuggestionErrorKey : @"Stellen Sie eine ein_beispielFrage!" }; fehler = [[NSError alloc] initWithDomain: @"org.antworten.FrageApp.ErrorDomain" code: 404 userInfo: info]; return nil; } else { return @"42"; } }

Fehler | 75

Abbildung 7-1 zeigt, wie der Dialog aussehen wrde, der erschiene, wenn das so erstellte NSError-Objekt in einer AppKit-Anwendung mit [NSAlert alertWithError: fehler] zur Initialisierung eines Warndialogs verwendet wrde.

Abbildung 7-1: NSError-Warndialog

NSError-Referenz
Die folgende Aufstellung enthlt die wichtigsten NSError-Nachrichten:
- (NSInteger) code

Liefert den domainspezifischen Fehlercode.


- (NSString *) domain

Liefert die Fehlerdomain. Fehler werden in Domains gegliedert, die den verschiedenen Subsystemen zugeordnet sind, die an der Ausfhrung des Programms beteiligt sind. In NSError.h werden die vier Basisdomains NSMachErrorDomain (Kernelebene), NSPOSIXErrorDomain (Unix-Ebene), NSOSStatusErrorDomain (Carbon) und NSCocoaErrorDomain (Cocoa) deklariert. Frameworks knnen zustzlich ihre eigenen Domains definieren. Wenn Sie eigene NSError-Objekte nutzen wollen, mssen Sie einen eigenen, eindeutigen Wert fr die Domain whlen. Dieser sollte blicherweise die Struktur umgekehrte Internetadresse.App-Name.Error Domain haben, z. B. de.ich.Informationsvergabe.ErrorDomain.

76 | Kapitel 7: Fehler und Ausnahmen

+ (id)errorWithDomain:(NSString *)domain code:(NSInteger)code userInfo:(NSDictionary *)dict Erstellt und liefert ein NSError-Objekt, das mit den durch die

Argumente angegebenen Werte fr die Fehlerdomain, den Fehlercode und das userInfo-Dict initialisiert wird. Das Domainargument darf nicht nil sein, das userInfo-Argument darf nil sein.
- (NSString *) helpAnchor

Liefert einen String, der Text enthlt, der auf den Abschnitt der Systemhilfe verweist, der angezeigt wird, wenn in einem AlertView auf das Hilfesymbol geklickt wird. Im Alert-View wird ein Hilfesymbol angezeigt, wenn helpAnchor einen Wert ungleich nil liefert. Im userDict wird dieser Wert unter NSHelpAnchor ErrorKey festgehalten.
- (id)initWithDomain:(NSString *)domain code:(NSInteger)code userInfo:(NSDictionary *)dict Initialisiert und liefert ein alloziertes NSError-Objekt, das mit

den durch die Argumente angegebenen Werte fr die Fehlerdomain, den Fehlercode und das userInfo-Dict initialisiert wird. Das Domainargument darf nicht nil sein, das userInfoArgument darf nil sein.
- (NSString *) localizedDescription

Liefert einen String, der eine lokalisierte Beschreibung des Fehlers enthlt. Diese Daten knnen auch ber den userInfoSchlssel NSLocalizedDescriptionKey abgerufen werden.
- (NSString *) localizedFailureReason

Liefert einen String, der eine lokalisierte Angabe zum Grund des Fehlers enthlt. Entspricht dem Wert des userInfo-Schlssels NSLocalizedFailureReasonErrorKey.
- (NSArray *) localizedRecoveryOptions

Liefert ein Array, das von rechts nach links die lokalisierten Beschriftungen fr die Buttons in einem Warndialog enthlt, in dem die Fehlerdaten angezeigt werden. Diese Daten werden ber den Schlssel NSLocalizedRecoveryOptionsErrorKey aus

NSError-Referenz | 77

dem userInfo-Dict abgerufen. Liefert nil, wenn userInfo keinen entsprechenden Wert enthlt.
- (NSString *) localizedRecoverySuggestion

Liefert einen String, der zustzliche Textinformationen zum Fehler enthlt, die z. B. als weitergehende Hinweise oder Fehlerbehebungsanweisungen in einem Warndialog geeignet sind. Diese Daten werden ber den Schlssel NSLocalizedRecovery SuggestionErrorKey aus dem userInfo-Dict abgerufen.
- (id) recoveryAttempter

Liefert ein Objekt, das Aktionen definiert, die den Wahlmglichkeiten entsprechen, die dem Benutzer im Warndialog ber das localizedRecoveryOptions-Array angeboten werden. Das Objekt muss die in NSError.h deklarierte NSObject-Kategorie NSError RecoveryAttempting implementieren. Wenn ein NSError in einem Warndialog angezeigt wird und userInfo unter dem Schlssel NSRecoveryAttempterErrorKey ein entsprechendes Objekt enthlt, wird seine attemptRecoveryFromError:optionIndex:-Methode mit dem Index in localizedRecoveryOptions aufgerufen, der dem Button entspricht, der im Dialog gewhlt wurde.
-(NSDictionary *) userInfo

Ein Dictionary mit zustzlichen Informationen, die den Fehler beschreiben, und eventuell Objekten, die genutzt werden knnen, um nach einem Fehler eine Wiederherstellung zu versuchen. Der am hufigsten verwendete Schlssel in das user Info-Dict ist NSLocalizedDescriptionKey,der eine lokalisierte Beschreibung des Fehlers liefert.

Ausnahmen
Objective-C bietet seit Mac OS X 10.3 eine Ausnahmebehandlung der kanonischen try/catch/finally-Gestalt. Wie in der Einleitung zu diesem Abschnitt erwhnt, werden diese Strukturen aber anders verwendet als bei anderen Programmiersprachen. Ausnahmen sollten nicht zur Behandlung von Fehlern genutzt werden, die zur Laufzeit behoben werden knnen, sondern nur bei Problemen

78 | Kapitel 7: Fehler und Ausnahmen

verwendet werden, die normalerweise zum Abbruch des Programms fhren sollten. Wenn Sie ARC nutzen, sollten Sie auerdem beachten, dass ARC unter Objective-C Ausnahmen standardmig nicht bercksichtigt (aber unter Objective-C++). Starke Bindungen werden nicht freigegeben, wenn Ausnahmen auftreten. Das heit: Standardmig fhren Ausnahmen unter ARC zu Speicherlchern. Wenn Sie in Ihrem ARC-Code Ausnahmen nutzen und behandeln wollen, mssen Sie die Compileroption -fobjc-arc-exceptions verwenden, damit ARC Ausnahmen bercksichtigt. Das ist standardmig nicht der Fall, da das erhebliche Auswirkungen auf die Laufzeitleistung und den Codeumfang htte.
Es gibt in Objective-C also eine Ausnahmebehandlung, aber Apple rt Ihnen de facto, diese nicht zu verwenden. Nutzen Sie Ausnahmen, wenn ein Problem zum Abbruch des Programms fhren sollte (da das Speicherloch dann irrelevant ist); nutzen Sie die Ausnahmebehandlung, wenn Ausnahmen, die von anderen Programmteilen ausgelst werden, nicht zum Programmabbruch fhren sollen (und vergessen Sie dann nicht, ARC-Ausnahmen zu aktivieren bzw. ARC abzuschalten).

Nach diesen Vorbemerkungen: Zur Ausnahmebehandlung nutzen Sie die vier Compilerdirektiven @try, @catch(), @finally und @throw. @try, @catch und @finally werden eingesetzt, um eine Ausnahmeverarbeitung der kanonischen try/catch/finally-Struktur aufzubauen, z. B.:
@try { // Code, der Ausnahmen auslo sen kann } @catch (NSException *ex) { // Code, der eine auftretende Ausnahme verarbeitet } @finally { // Code, der immer ausgefu ngig davon, hrt wird, unabha // ob eine Aufnahme auftrat oder nicht }

Ausnahmen | 79

Der @catch()-Handler muss den Typ von Ausnahmeobjekt angeben, den dieser Handler verarbeitet. Dieser kann angegeben werden, indem ein Parameter angegeben wird, ber den im @catch()Handler auf das mit der Ausnahme verbundene Objekt zugegriffen werden kann. Wenn der Handler keinen Zugriff auf das mit der Ausnahme ausgelste Objekt bentigt, kann alternativ nur der Ausnahmeobjekttyp (z. B. NSString *) angegeben werden. Es knnen mehrere @catch()-Handler angegeben werden. Ausnahme-Handler fr speziellere Ausnahmeobjekte sollten AusnahmeHandlern fr allgemeinere Ausnahmeobjekte vorangehen. Es ist empfehlenswert, die Kette der Ausnahme-Handler mit einem allgemeinen Handler abzuschlieen, der als Typ id angibt.
@catch(NSException *ex) { // Verarbeitet Ausnahmen, mit denen ein NSException-Objekt // verbunden ist } @catch(id) { // Verarbeitet alle anderen Ausnahmen und fa ngt das // Ausnahmeobjekt nicht ein }

Die Ausnahmeinformationen werden blicherweise von NSExceptionObjekten gekapselt. NSException-Objekte bieten einige Methoden, mit denen Informationen zur Ausnahme abgerufen bzw. eigene Ausnahmen erstellt und ausgelst werden knnen. In Objective-C gibt es anders als in Java keine komplexe Hierarchie von Ausnahmeklassen. Ausnahmen werden blicherweise durch den Namen und die anderen Ausnahmeeigenschaften differenziert. Bei Bedarf knnen Sie jedoch jederzeit eigene NSException-Unterklassen definieren. Ausnahmen knnen mit @throw ausgelst werden oder ber eine der raise-Methoden von NSException. Mit @throw wird ein Objekt als Ausnahmeobjekt ausgelst. Dieses Objekt kann eine Instanz einer beliebigen von NSObject abgeleiteten Klasse sein, es muss nicht notwendigerweise eine NSException-Instanz oder eine Instanz einer von NSException abgeleiteten Klasse sein. Zum Beispiel wre Folgendes vollkommen zulssig:
@throw @"Dummer Fehler";

80 | Kapitel 7: Fehler und Ausnahmen

Wenn Sie Ausnahmeobjekte nutzen, die keine Instanzen von NSException oder einer NSException-Unterklasse sind, mssen Sie natrlich auch Handler nutzen, die die entsprechenden Objekttypen abfangen.
Beachten Sie, dass es sein kann, dass Framework-Methoden nur NSException-Objekte abfangen. Andererseits kann es jedoch sein, dass Framework-Methoden selbst andere Objekttypen als Ausnahmeobjekt verwenden. Ihrerseits sollten Sie also bereit sein, Objekte jeglicher Art als Ausnahmeobjekt zu bercksichtigen.

In einem @catch()-Handler kann @throw ohne Argument verwendet werden, um die aktuelle Ausnahme neu auszulsen.

Ein Beispiel
Der folgende Code unternimmt etwas, das zur Auslsung einer Ausnahme fhren kann. Wird eine Ausnahme ausgelst, wird sie vom @catch-Abschnitt verarbeitet, der sie entweder konsumiert (hier indem er auf der Konsole eine Nachricht protokolliert) oder neu auslst (weil er mit ihr nichts anfangen kann), damit sie von einem bergeordneten Ausnahme-Handler verarbeitet wird. @finally sorgt dafr, dass, unabhngig davon, ob eine Ausnahme auftrat oder nicht, aufgerumt wird auch wenn die abgefangene Ausnahme neu ausgelst wird:
// Zuna chst wird etwas Unordnung geschaffen Buch *buch = [Bibliothek buchMitName: @"Ulysses"]; @try { // Lo st eine Ausnahme aus, wenn eine Grundbedingung // nicht erfu llt ist if ([self lustUnlust] < 10) @throw [NSException exceptionWithName: @"NoBockException" reason: @"" userInfo: nil]; else [buch lesen]; // Ko nnte eine andere Ausnahme auslo sen }

Ausnahmen | 81

@catch (NSException *ex) { if ([[ex name] isEqualToString: @"NoBockException"]) // Manche Probleme mu ssen nicht das Ende sein NSLog(@"Lust nur %i", [self lustUnlust]); else // Eine lesen-Ausnahme hingegen wird neu ausgelo st [ex raise]; } @finally { [buch wegraeumen]; // Hinterher ordentlich aufra umen }

Beachten Sie, dass dieses Beispiel die Ausnahmeverarbeitung genau zu dem Zweck einsetzt, zu dem sie eigentlich nicht eingesetzt werden sollte.

r nicht abgefangene Exceptions Handler fu


Das Foundation-Framework bietet eine Mglichkeit, einen Handler fr Ausnahmen zu registrieren, die nicht abgefangen wurden. In diesem Handler knnen Sie Informationen zur Ausnahme protokollieren, bevor das Programm abgebrochen wird. Der Handler wird mit der Funktion NSSetUncaughtExceptionHandler() registriert (und kann mit der Funktion NSGetUncaughtException Handler() abgerufen werden). NSSetUncaughtExceptionHandler() erwartet als Argument einen Zeiger auf eine C-Funktion, die eine NSException-Referenz als Argument erwartet und den Rckgabetyp void hat. Der nachfolgende Code definiert die (recht primitive) HandlerFunktion UncaughtExceptionHandler() als Top-Level-C-Funktion und registriert diese zu Anfang des Programmcodes mit NSSet UncaughtExceptionHandler() als Handler fr nicht abgefangene Ausnahmen:
void UncaughtExceptionHandler(NSException *ex) { NSLog(@"Nicht abgefangene Ausnahme: %@", ex); } int main() {

82 | Kapitel 7: Fehler und Ausnahmen

@autoreleasepool { NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler); // } return 0; }

NSException-Referenz
Die folgende Aufstellung enthlt die wichtigsten NSException-Nachrichten:
- (NSArray *)callStackReturnAddresses

Liefert ein Array mit den Rcksprungadressen zum Callstack der Ausnahme in aufsteigender Reihenfolge (d. h., das erste Element ist die Position, an der die Ausnahme auftrat).
- (NSArray *)callStackSymbols

Liefert ein Array mit den Daten des Callstacks.


+ (NSException *)exceptionWithName:(NSString *)name reason:(NSString *)reason userInfo:(NSDictionary *)userInfo Erzeugt und liefert ein NSException-Objekt mit den angegebenen Werten fr -name und -reason. Als letzter Parameter kann ein userInfo-Dictionary angegeben werden. (id)initWithName:(NSString *)name reason:(NSString *)reason userInfo:(NSDictionary *)userInfo Initialisiert ein alloziertes NSException-Objekt mit den angegebenen Werten fr -name, -reason. Als letzter Parameter kann ein userInfo-Dictionary angegeben werden. - (NSString *)name

Liefert den (eindeutigen) Namen der Ausnahme.


+(void)raise: (NSString *)name format:(NSString *)format, ... +(void)raise: (NSString *)name format:(NSString *)format arguments:(va_list)args

Diese beiden Klassenmethoden lsen unmittelbar eine Ausnahme aus, mit der ein Name und ein Grund, aber kein userInfo-Dict

NSException-Referenz | 83

verbunden ist. Das erste Methodenargument ist ein NSString, der zum Namen der NSException wird, das zweite ein Formatstring, der fr den Aufbau des Werts genutzt wird, den -reason liefert. Der Formatstring wird mit den Argumenten gefllt, die (bei der ersten Form) als kommaseparierte Liste nach dem Formatstring oder (bei der zweiten Form) ber eine va_list angegeben werden.
-(void)raise

Lst das Ausnahmeobjekt aus, dem diese Nachricht gesendet wird. Das ist die Methode, die immer aufgerufen wird, wenn irgendwie eine Ausnahme ausgelst wird.
- (NSString *)reason

Liefert einen Text, der blicherweise den Grund fr das Auftreten der Ausnahme angeben sollte.
- (NSDictionary *)userInfo

Liefert das Dict mit den Daten und Informationen zur Ausnahme.

84 | Kapitel 7: Fehler und Ausnahmen

KAPITEL 8

NSObject

NSObject ist die Basisklasse, die von Apples Plattformen genutzt wird.

Sie bestimmt die Grundstruktur aller Klassen, die Sie bei der Programmierung mit Objective-C nutzen bzw. schreiben, und damit die Interaktionsschnittstelle aller Objekte, mit denen Sie arbeiten. Alle (fast alle) Klassen der Cocoa-Bibliotheken, die Sie bei der Objective-C-Programmierung nutzen werden, sind mittelbar oder unmittelbar Unterklassen von NSObject und erben die dort definierten Felder, Eigenschaften und Methoden. Weil diese so die Grundstruktur von Objective-C-Objekten bestimmen, ist die NSObjectSchnittstelle von elementarer Bedeutung bei der Programmierung in Objective-C. Anders als die Basisklassen von Sprachen wie Java oder C# hat die Basisklasse von Objective-C eine eher komplexe Schnittstelle (die unter Mac OS X ber 200 Nachrichten umfasst und unter iOS nur ein paar weniger). Sie bietet unter anderem Interaktionsmglichkeiten fr die hier aufgefhrten Aufgabenbereiche: Klassen- und Objektinitialisierung Erstellen, Kopieren und Zerstren von Objekten Identifikation von Klassen und Prfen der Eigenschaften von Klassen Senden, Weiterleitung und Auflsen von Nachrichten Objektarchivierung

| 85

Klassenattribute Scripting In den nachfolgenden Abschnitten werden wir Ihnen eine Einfhrung in die Operationen geben, die Ihnen die NSObject-Schnittstelle beim Umgang mit den Instanzen ihrer Klassen bietet. Wir werden uns dabei auf die Aufgabenbereiche konzentrieren, die unter Mac OS X und unter iOS untersttzt werden. Eine Referenz zu allen Nachrichten, die in diesen Abschnitten vorgestellt und erwhnt werden, finden Sie gegen Ende des Buchs in Kapitel 16, NSObjectReferenz. Bei Objective-C wird die Basisklasse anders als bei z. B. Java nicht von der Sprache vorgegeben. Das bedeutet, wenn Sie wollen, knnen Sie eine Klassenhierarchie schaffen, die auf einer eigenen Basisklasse basiert.

86 | Kapitel 8: NSObject

KAPITEL 9

Objektlebenszyklus

Wenn Sie sich ein Objekt beschaffen, muss im Hintergrund eine ganze Menge geschehen. Kommen Sie von einer Programmiersprache wie Java, werden Sie sich darber wahrscheinlich wenig Gedanken machen. Sie sagen new Objekt() und erhalten ein neues Objekt, frisch initialisiert und einsatzbereit. Vielen Dank, Runtime. Und wenn Sie es nicht mehr brauchen, vergessen Sie einfach, dass Sie es je erstellt haben im Vertrauen darauf, dass die Garbage Collection schon hinter Ihnen aufrumen wird. Da haben wir einige garstige berraschungen fr Sie parat: In Objective-C gibt es keinen new-Operator, keine Konstruktoren und auch eine Garbage Collection ist nicht (mehr) verfgbar. ObjectiveC-Klassen mssen folglich eigene Features anbieten, die sich mit diesen Aufgaben befassen. Konventionsgem wird dazu eine Gruppe von Methoden genutzt, die von der Framework-Basisklasse NSObject deklariert und implementiert werden: +initialize +alloc +new -init -dealloc Alle Klassen (die Teil der NSObject-Hierarchie sind) erben diese Methoden und nutzen sie zur Allozierung, Initialisierung und Deallozierung von Objekten. Diese Implementierungen in NSObject

| 87

bestimmen, wie der Speicher fr Objekte bereitgestellt wird und was unternommen wird, wenn er wieder freigegeben werden muss. Deswegen haben sie einen ganz wesentlichen Anteil daran, wie sich die Arbeit mit Objekten in Objective-C gestaltet konfigurieren sozusagen einen grundlegenden Aspekt der Sprache. Wenn Sie eigene Klassen schreiben, die eine spezielle Initialisierung verlangen, sollten Sie die Grundaufgaben stets an diese Basisversionen delegieren und die eigenen Einrichtungen in den entsprechenden Methoden bereitstellen.

Objekte erstellen
Die Erstellung von Objekten ist in Objective-C ein zweistufiger Prozess. Zunchst wird der Speicher alloziert, dann werden die Felder initialisiert. Von der Allozierung erhalten Sie einen Zeiger auf einen Speicherbereich, in dem das vorinitialisierte Objekt gespeichert ist. Mit der Initialisierung initialisieren Sie die Felder des neuen Objekt, wie es fr die jeweilige Klasse erforderlich ist. Die Allozierung erfolgt mit einer +alloc-Nachricht, die Initialisierung mit einer -init-Nachricht. Wie Sie an den Vorzeichen erkennen knnen, werden die beiden Nachrichten an unterschiedliche Empfnger gesendet: +alloc an die Klasse, -init an die Klasseninstanz:
id objekt = [Klasse alloc]; objekt = [objekt init];

Beide Nachrichten liefern ein Objekt des Typs id. Das von +alloc gelieferte Objekt ist noch nicht verwendungsfhig und muss erst mit -init initialisiert werden. (Beispielsweise wrde NSLog(@"%@", [NSString alloc]) zu einem Laufzeitfehler fhren, whrend NSLog(@"%@", [[String alloc] init] problemlos luft.) Da der von -init gelieferte Zeiger nicht mit dem identisch sein muss, der von +alloc geliefert wurde, sollten Sie immer den Rckgabewert von -init festhalten. Wenn Sie das nicht tun, kann es sein, dass Ihr Empfnger nicht korrekt initialisiert wird. Zum Beispiel:

88 | Kapitel 9: Objektlebenszyklus

id alloziert = [NSString alloc]; id initialisiert = [alloziert init]; if (alloziert != initialisiert) { NSLog(@"Ungleich!"); }

Dieser Code gibt Ungleich! aus, weil der Zeiger, den -init liefert, auf einen anderen Speicherbereich als den zeigt, den +alloc lieferte. blicherweise werden die +alloc- und -init-Nachrichten deswegen verkettet:
id objekt = [[Klasse alloc] init];

Alternativ knnen Sie der Klasse auch die +new-Nachricht senden, die nichts anderes als ein Alias fr [[Klasse alloc] init] ist:
id objekt = [Klasse new];

Allozierung
+alloc hat eine sehr wichtige Aufgabe: Es muss die interne Struktur eines Objekts im Speicher einrichten, bevor dieses initialisiert wird. +alloc ist allerdings nicht nur fr die Speicherbeschaffung verantwortlich. Es sorgt auerdem dafr, dass der Referenzzhler fr das Objekt auf eins gesetzt wird, dass das isa-Feld auf die Klasse des Objekts gesetzt wird und dass alle Felder des Objekts auf 0 bzw. den quivalenten Wert fr den entsprechenden Datentyp initialisiert werden. Erst die Initialisierung mit einer Initialisierungsnachricht macht das mit +alloc allozierte Objekt verwendbar.

berschreiben Sie alloc nicht. Alle eigenen Initialisierungsaufgaben sollten Sie in Initialisierungsmethoden stecken.

Objektinitialisierung
Die Aufgabe der -init-Methode ist mit der eines Konstruktors vergleichbar sie liefert eine konkrete, korrekt initialisierte Instanz der Klasse. Die -init-Methode von NSObject macht nichts. Wenn Ihre Klassen eine spezielle Initialisierung verlangen, mssen Sie eigene Initialisierungsmethoden schreiben. Sie knnen parameterlose
Objekte erstellen | 89

-init-Methoden schreiben, die die -init-Methode von NSObject

berschreiben, und Sie knnen Initialisierungsmethoden schreiben, die Parameter erwarten.


Es gibt zwei wesentliche Unterschiede zwischen den Initialisierungsmethoden von Objective-C und den Konstruktoren von beispielsweise Java: Konstruktoren haben keine Rckgabewerte. Konstruktoren werden nicht vererbt.

Eine Initialisierungsmethode sollte drei Kriterien gengen: 1. Der Methodenname beginnt mit init. 2. Die Methode liefert den Wert von self zurck. 3. Der Rckgabewert der Methode hat den Typ id. Die Struktur von Objective-C-Methodennamen sorgt dafr, dass Initialisierungsmethoden, die Parameter erwarten, jeweils unterschiedliche Namen haben. Die Namen dieser Methoden sollten klar zum Ausdruck bringen, dass es sich um Initialisierungsmethoden handelt. Beispielsweise knnte eine Haustier-Klasse die InitMethoden -initMitArt, -initMitArt:name:, -initMitArt:name:al ter bieten.
Anders als bei den Konstruktoren anderer Sprachen wird die Gestalt der Initialisierungsmethoden nicht von der Syntax vorgegeben, sondern allein durch Konventionen geregelt. Es ist wichtig, dass Sie sich an diese Konventionen halten.

Designierte Initialisierer
Wie bei Konstruktoren ist es wichtig, dass alle Schichten Ihrer Objekte korrekt und in der erforderlichen Reihenfolge initialisiert werden. Ihre Klassen mssen stets sicherstellen, dass ihre Initialisie-

90 | Kapitel 9: Objektlebenszyklus

rungsmethoden an den richtigen Punkten die richtige Initialisierungsmethode der Elternklasse aufrufen, damit von der Basisklasse ausgehend die Oberklassenschichten des Objekts initialisiert werden, bevor die speziellen Initialisierungsschritte der neuen Klasse zur Anwendung kommen. Kurz: Der erste Schritt einer ihrer Initialisierungsmethoden muss sein, die richtige Initialisierungsmethode der jeweiligen Elternklasse aufzurufen. Wenn Ihre Klasse mehrere Initialisierer hat, sollte sie einen designierten Initialisierer haben. Als designierten Initialisierer bezeichnet man unter Objective-C einen Initialisierer, der einen Initialisierer der Elternklasse aufruft. Dieser vorgezogene Initialisierer ist in der Regel der spezifischste Initialisierer, der also, der die meisten Parameter erwartet. Alle anderen Initialisierer sollten diesen designierten Initialisierer aufrufen. Schauen wir uns das anhand eines Beispiels an:
#import <Foundation/Foundation.h> @interface Tier : NSObject @property NSString *art; - (id)initMitArt:(NSString *)art; @end @interface Haustier : Tier @property NSString *name; @property NSUInteger *alter; - (id)initMitArt:(NSString *)art name:(NSString *)name; - (id)initMitArt:(NSString *)art name:(NSString *)name alter:(NSUInteger *)alter; @end

Tier definiert den Initialisierer -initMitArt:, der folglich auch der designierte Initialisierer sein muss. Haustier definiert die Initialisierer -initMitArt:name: und -initMitArt:name:alter. -initMit Art:name:alter: wird der designierte Initialisierer sein, weil er der

spezifischste Initialisierer ist. Wenden wir uns nun den Implementierungen zu:
@implementation Tier - (id)init { return [self initMitArt: @"Unbekannt"];

Objekte erstellen | 91

} - (id)initMitArt:(NSString *)art { if (self = [super init]) { self->_art = art; } return self; } @end

Tier implementiert -initMitArt: als designierten Initialisierer. Ein

designierter Initialisierer hat stets zwei Aufgaben: Er setzt self auf den Wert, den ihm der designierte Initialisierer der Oberklasse liefert. Er initialisiert die fr Instanzen dieser Klasse spezifischen Ivars oder Eigenschaften. Auerdem berschreibt Tier den designierten Initialisierer der Oberklasse, hier den vertrauten -init-Initialisierer von NSObject. Beachten Sie, dass in der -init-berschreibung nicht die berschriebene -init-Version aufgerufen wird, sondern der designierte Initialisierer der Klasse selbst, -initMitArt, der allein dafr verantwortlich ist, den Oberklasseninitialisierer aufzurufen. Die Implementierungen der Initialisierer der Unterklasse Haustier greifen dann auf die Initialisierer von Tier zurck:
@implementation Haustier - (id)initMitArt:(NSString *)art { return [self initMitArt: art name: @"Namenlos" alter: 0]; } - (id)initMitArt:(NSString *)art name:(NSString *)name { return [self initMitArt: art name: name alter: 0]; } - (id)initMitArt:(NSString *)art name:(NSString *)name alter:(int) alter{ if (self = [super initMitArt: art]) { self->_name = name; self->_alter = alter; } return self; } @end

92 | Kapitel 9: Objektlebenszyklus

Wie Tier berschreibt Haustier den designierten Initialisierer der Oberklasse. Tier berschreibt -init, den designierten Initialisierer von NSObject, Haustier berschreibt -initMitArt:, den designierten Initialisierer von Tier. Das stellt sicher, dass ein Aufruf des jeweiligen Initialisierers auf einer Instanz von Tier respektive Haustier ein vernnftiges Ergebnis liefert. Beide berschreibenden Initialisierer rufen nicht den entsprechenden Initialisierer der Elternklasse auf, sondern delegieren wie alle anderen gewhnlichen Initialisierer die Arbeit an den designierten Initialisierer der eigenen Klasse. Nur der designierte Initialisierer, hier -initMitArt:name:alter: ruft ber super den designierten Initialisierer der Eltern Klasse auf. Alle anderen Initialisierer delegieren die Initialisierung mit einem Aufruf auf self an den designierten Initialisierer. Diese Vorgehensweise sorgt dafr, dass die Initialisierer eine saubere Kette bilden und der Aufruf aller Initialisierer, auch aller Oberklasseninitialisierer, immer ordentlich initialisierte Objekte liefert. Wrde auf Haustier init aufgerufen, erhielten Sie beispielsweise ein Haustier mit der Art Unbekannt, dem Namen Namenlos und dem Alter 0.
Haustier hat keine init-Methode, also kommt die geerbte von Tier zum Einsatz. Diese ruft auf self -initMitArt: mit dem Wert @"Unbekannt auf. Da self hier ein Haustier-Objekt ist, wird die -initMitArt:-Methode von Haustier aufgerufen, die wieder auf self mit den Werten @"Unbekannt", @"Namenlos" und 0 den designierten Initialisierer -initMitArt:name:alter: aufruft. Dieser ruft auf der Elternklasse mit dem Argument @"Unbekannt den designierten Initialisierer -initMitArt: auf, setzt self auf den Wert, den dieser Initialisierer liefert, und richtet dann auf self die Werte der

anderen Felder ein.

Objekte erstellen | 93

r eine konsistente Regeln fu Initialisierungsstruktur


Eine konsistente Initialisierungsstruktur fr Ihre Klassen erhalten Sie, wenn Sie die folgenden drei Grundregeln im Blick halten: 1. Ihre Klasse sollte einen designierten Initialisierer haben, der den designierten Initialisierer der Elternklasse aufruft (Nachricht an super). 2. Ihre Klasse sollte den designierten Initialisierer der Oberklasse berschreiben, der den designierten Initialisierer der neuen Klasse aufruft (Nachricht an self). 3. Alle anderen Initialisierer einer Klasse sollten den designierten Initialisierer aufrufen (Nachricht an self).

Initialisierer und Fehler


Beachten Sie auerdem das if(self = [super init])-Muster in den designierten Initialisierern. Da Initialisierungsmethoden einen Rckgabewert haben, knnen sie im Unterschied zu Java-Konstruktoren eventuelle Fehler ber den Rckgabewert signalisieren. Die Initialsierer liefern immer self zurck, aber wenn self nicht richtig initialisiert werden konnte, ist self nil. Die Semantik des nil-Werts macht ihn zu einem geeigneten Kandidaten fr den Rckgabewert fr den Fall, dass ein Objekt nicht initialisiert werden konnte. Das knnen Sie auch nutzen, wenn Ihre Initialisierungsmethoden komplexere Operationen vornehmen, bei denen es zu Fehlern kommen kann. Wenn Ihre Initialisierungsmethode Daten aus dem Netzwerk lesen muss, aber kein Netzwerk verfgbar ist, liefern Sie z. B. also einfach nil.

Klasseninitialisierung
Die Laufzeitumgebung sorgt dafr, dass jeder Klasse, bevor sie oder eine ihrer Unterklassen genutzt wird, einmal die +initialize-Nachricht gesendet wird. Es ist garantiert, dass Elternklassen diese Nachricht vor ihren Kindklassen erhalten. Wenn eine Klasse nicht zum

94 | Kapitel 9: Objektlebenszyklus

Einsatz kommt (direkt und indirekt ber eventuelle Unterklassen), erhlt sie auch keine +initialize-Nachricht.
+initialize bietet Klassen eine Mglichkeit, ihre Laufzeitumgebung einzurichten, bevor sie verwendet werden. Eine +initialize-Methode ist beispielsweise ein geeigneter Ort zur Einrichtung von Klassenvariablen.

Beachten Sie jedoch, dass einer Klasse +initialize zwar nur einmal direkt gesendet wird, eine Klasse die Nachricht aber dennoch mehrfach erhalten kann. Wenn eine Klasse keine eigene +initializeImplementierung anbietet, wird in der Vererbungshierarchie aufgestiegen, bis eine Implementierung gefunden wird. Deswegen sollten Sie in +initialize-Methoden immer prfen, ob die Nachricht tatschlich fr diese Klasse gedacht ist. Dazu knnen Sie das folgende Muster nutzen:
@implementation Familie + (void)initialize { // Pru chlich diese Klasse ist ft, ob self tatsa if (self == [Familie class]) { nochErlaubteKinder = MAX_KINDER; } }

Verketten Sie initialize-Nachrichten nicht. Da die Laufzeitumgebung bereits dafr sorgt, dass Elternklassen diese Nachricht vor Kindklassen erhalten, ist es nicht erforderlich, eine empfangene initialize-Nachricht an die Elternklasse weiterzuleiten. Eine Verkettung von initialize-Nachrichten wrde das Muster aushebeln und dazu fhren, dass initialize doch mehrfach an eine Klasse gesendet wird.

Objekte kopieren
Wenn Sie einer Variablen a des Typs int den Wert einer anderen Variablen b des Typs int zuweisen, wird der Wert an der Speicherstelle fr b in die Speicherstelle fr a kopiert. ndern Sie spter den Wert von b, hat das keine Auswirkungen auf den Wert von a:
Objekte kopieren | 95

int b = 42; int a = b; b = 0; NSLog(@"%i", a) // liefert 42

Gleiches gilt fr den Wert von Objektvariablen:


Haustier *b = [[Haustier alloc] initMitArt: @"Ente"]; Haustier *a = b; //a und b zeigen auf die gleiche Speicherstelle b = [[Haustier alloc] initMitArt: @"Dinosaurier"]; //b zeigt auf ein neues Objekt, a a ndert sich nicht NSLog(@"%@", a.art); //Liefert Ente

Der Speicherwert einer Objektvariablen ist ein Zeiger auf ein Objekt. Wird a der Wert von b zugewiesen, wird wieder einfach nur der Wert an der Speicherstelle von b (der Zeiger) an die Speicherstelle von a kopiert. a und b zeigen nach der Neuzuweisung von b also auf unterschiedliche Objekte mit unterschiedlichen Werten fr die Eigenschaft art. Aber wenn Sie nicht den Speicherwert, d. h. die Referenz, sondern das Objekt ndern, auf das die Variable zeigt, indem Sie z. B. die Eigenschaft art auf einen anderen Wert setzen, sind diese nderungen ber beide Variablen sichtbar:
Haustier *b = [[Haustier alloc] initMitArt: @"Ente"]; Haustier *a = b; b.art = @"Dinosaurier"; NSLog(@"%@", a.art); //Liefert Dinosaurier

Weil das manchmal erwnscht ist und manchmal nicht, mssen Sie sich Gedanken ber das Kopieren, d. h. die Erstellung eigenstndiger Kopien, von Objekten machen. Die Klasse NSObject definiert zwei Methoden fr das Kopieren von Objekten: -(id)copy -(id)mutableCopy Beide Methoden delegieren die Arbeit aber einfach an zwei andere Methoden, die von NSObject nicht deklariert werden: -(id)copyWithZone:(NSZone *)zone -(id)mutableCopyWithZone:(NSZone *)zone

96 | Kapitel 9: Objektlebenszyklus

Diese beiden Methoden werden in den Protokollen NSCopying respektive NSMutableCopying deklariert. Keins davon wird von NSObject bernommen. Es gibt also keine Standardimplementierung von -copy oder -mutableCopy, die Ihre Klassen erben wrden. Der Versuch, einem Objekt, dessen Klasse das entsprechende Protokoll nicht bernimmt, eine dieser Nachrichten zu senden, fhrt zu einem Fehler. Wenn Ihre Objekte das Kopieren untersttzen sollen, muss die entsprechende Klasse die entsprechenden Protokolle bernehmen. Dass es zwei Protokolle und zwei Methoden gibt, ist fr Sie nur interessant, wenn es vernderliche und unvernderliche Varianten Ihrer Klassen gibt, wie beispielsweise bei NSArray und NSMutable Array. Gewhnlich werden Sie bei Ihren Klassen nur NSCopying adoptieren und -copyWithZone implementieren mssen, um die Instanzen dieser Klasse kopierbar zu machen. Nur wenn Ihre Klassen vernderliche und unvernderliche Werte untersttzen, mssen Sie beide Protokolle implementieren. -copyWithZone: sollte dann eine unvernderliche Kopie liefern, -mutableCopyWithZone eine vernderliche.
Ist der Unterschied zwischen vernderlichen und unvernderlichen Instanzen fr Ihre Klasse irrelevant, nutzen Sie stets nur NS Copying.

Kopiermethoden implementieren
Wenn die Oberklasse NSObject ist (oder eine andere Klasse, die NSCopying weder direkt noch indirekt implementiert), wird eine neue Objektinstanz erstellt, deren Felder dann mit den entsprechenden Werten des Originals gefllt werden. Schauen wir uns das anhand unserer Tier-Klasse an:
@implementation Tier -(id)copyWithZone:(NSZone *)zone { Tier *kopie = [[[self class] alloc] init]; kopie.art = self.art; return kopie; } @end

Objekte kopieren | 97

Wie in einer Initialisierungsmethode sollten Sie auch in Kopiermethoden zur Instanzallozierung nicht den statischen Klassennamen verwenden, sondern das ber self abgerufene Klassenobjekt, damit die Klasse bei einem Aufruf ber eine Unterklasse richtig initialisiert wird. Wenn die Oberklasse NSCopying implementiert, wird der Oberklassenteil durch eine Delegation der Nachricht an super initialisiert, wie Sie es hier am Beispiel der Klasse Haustier sehen:
@implementation Haustier -(id)copyWithZone:(NSZone *)zone { Haustier *kopie = [super copyWithZone:zone]; kopie.name = self.name; kopie.alter = self.alter return kopie; }

Beachten Sie, dass wir direkt [super copyWithZone:zone] aufrufen und nicht [super copy] nutzen, da das zu einer Endlosschleife fhren wrde.

Tiefe und flache Kopien


Beim Kopieren mssen Sie entscheiden, ob Sie eine flache oder eine tiefe Kopie erstellen wollen. Bei einer flachen Kopie zeigen die Objektinstanzvariablen der Kopie auf dieselben Objekte wie die Objektinstanzvariablen des Originals. Bei einer tiefen Kopie werden auch Kopien der Objektinstanzvariablen erstellt. Das Beispiel im letzten Abschnitt hat nur eine flache Kopie erstellt. (Lassen Sie sich von der Unvernderlichkeit von NSString nicht zu der Annahme verleiten, dass das hier keine Rolle spielt. Den nameund art-Eigenschaften knnen auch Instanzen der vernderlichen Unterklasse NSMutableString zugewiesen werden.) Eine tiefe Kopie von Tier wrden Sie wahrscheinlich folgendermaen erstellen:
-(id)copyWithZone:(NSZone *)zone { Tier *kopie = [[[self class] alloc] init]; kopie.art = [self.art copy]; return kopie; }

98 | Kapitel 9: Objektlebenszyklus

Beachten Sie jedoch, dass dieser Code zwar wahrscheinlich leistet, was er leisten soll, trotzdem aber nicht tut, was Sie vielleicht erwarten. Die -copy-Methode von NSString liefert self zurck und keine Kopie. Wenn self.art tatschlich ein NSString-Objekt referenziert, zeigen self.art und kopie.art also auf das gleiche Objekt was, da es tatschlich unvernderlich ist, kein Problem sein sollte. Wenn self.art ein NSMutableString-Objekt referenziert, wird hingegen eine unabhngige, unvernderliche Kopie erstellt genau wie wir es haben wollten. Wenn Sie tatschlich eine eigenstndige Kopie eines NSStringObjekts erstellen wollen, mssen Sie tricksen. Sie knnen z. B. zunchst eine vernderliche Kopie erstellen, von der Sie dann eine unvernderliche Kopie erstellen:
[[self.art mutableCopy] copy];

So erhalten Sie in beiden Fllen, bei einem NSString und einem NSMutableString, eine eigenstndige, unvernderliche Kopie. (Das ist ein spezielles Problem des NSString-Klassenclusters, das andere Framework-Objekte nicht aufweisen.)
NSObject.h-Header definieren auerdem eine Funktion namens NSCopyObject(), die eine flache Kopie eines beliebigen Objekts erstellt. Diese Funktion ist seit Mac OS X 10.8 und iOS 6 veraltet, und Ihr Einsatz ist unter ARC auch in frheren Versionen nicht gestattet. Apples Dokumentation warnt explizit, dass der Einsatz dieser Funktion gefhrlich ist. Insbesondere von ihrer Verwendung bei der Implementierung der Kopiermethoden wird abgeraten. Nutzen Sie diese Funktion also nicht.

Objektvernichtung
Wenn ein Objekt nicht mehr bentigt wird, sorgt die Runtime dafr, dass der mit ihm verbundene Speicher freigegeben wird. Wie das erfolgt, ist davon abhngig, welche Speicherverwaltungstechnologie zum Einsatz kommt. Bei der Speicherverwaltung mit
Objektvernichtung | 99

ARC sorgt die Runtime dafr, dass Objekte zur geeigneten Zeit die -dealloc-Nachricht erhalten. Bei der manuellen Speicherverwaltung mussten Sie das teilweise selbst bernehmen. -dealloc gibt dann den Speicher frei, der mit dem Objekt verknpft ist1. Vor ARC gehrte es zu den Grundaufgaben aller Objective-C-Programmierer, -dealloc-Implementierungen anzubieten, in denen Ivars freigegeben wurden, bevor die -dealloc-Nachricht an die Oberklasse weitergeleitet wurde. Teile davon sind unter ARC unntig, andere verboten. Ein Beispiel zur Verwendung von -dealloc finden Sie in Kapitel 10, Speicherverwaltung.

1 Unter der veralteten Garbage Collection erhielt das Objekt stattdessen eine -finalize-Nachricht.

100 | Kapitel 9: Objektlebenszyklus

KAPITEL 10

Speicherverwaltung

Wenn Sie von einer Programmiersprache wie Java kommen, werden Sie sich wahrscheinlich fragen, warum Sie sich mit so etwas wie der Verwaltung des Speichers befassen mssen. Ich lege meine Variablen an, weise ihnen Werte zu und arbeite mit ihnen; und wenn ich die Werte nicht mehr brauche, wird das System schon wissen, was es mit ihnen anfangen soll. So einfach ist das bei Objective-C nicht.

Was Speicherverwaltung ist


Ihre Programme fordern Speicher, andere Programme fordern Speicher, aber der Speicherplatz, den das System bietet, ist beschrnkt. Das kann nur gutgehen, wenn der Speicher, den ein Programm bentigt, irgendwann wieder freigegeben wird. Es gibt verschiedene Arten von Speicher: Speicher, der den jeweiligen Ausfhrungseinheiten (Methode, Funktion oder Blcken) zugeordnet ist, und Speicher, der fr das gesamte Programm verfgbar ist. Die erste Art Speicher ist der Stack (Stapel), die zweite der Heap (Halde). Der Stack wird immer automatisch verwaltet. Wenn eine neue Ausfhrungseinheit gestartet wird, wird auf dem Stack ein neuer Stack-Frame speziell fr diese Ausfhrungseinheit angelegt. Ist die Ausfhrung jener Einheit abgeschlossen, wird dieser Frame vom Stack genommen und entfernt, und mit ihm verschwinden alle dort gespeicherten Werte. Aus diesem Grund brauchen Programme eine zweite Art Speicher, den Heap, der nicht an eine Ausfhrungseinheit gebunden ist und Daten festhalten kann, die von mehreren Programmkomponenten bentigt werden.

| 101

Stack und Heap unterscheiden sich aber auch in Bezug auf die Daten, die dort gespeichert werden. Bei den meisten Programmiersprachen ist es so, dass auf dem Stack nur Werte gespeichert werden, die immer eine festgelegte Menge Speicher bentigen, ints, floats oder chars beispielsweise. Auf dem Heap hingegen werden Daten gespeichert, deren Gre vorab nicht festgelegt ist, wie es bei den meisten Objekttypen der Fall ist. Wenn wir von Speicherverwaltung reden, meinen wir damit also die Verwaltung der Objektdaten auf dem Heap. Diese Speicherverwaltung kann ganz unterschiedlicher Art sein. Sie kann vollkommen automatisch ablaufen, ohne dass der Programmierer dazu einen einzigen Handschlag unternehmen muss, wie bei Sprachen mit einer Garbage Collection. Sie kann aber auch vollkommen manuell erfolgen wie in C, wo der Programmierer den zu verwendenden dynamischen Speicher aufs Byte genau anfordern und wieder freigeben muss. Objective-C untersttzt zwei Speicherverwaltungstechnologien, die einen Mittelweg zwischen diesen beiden Extremen einschlagen.

Speicherverwaltungstechnologien unter Objective-C


Die von Objective-C genutzten Speicherverwaltungstechnologien sind referenzzhlungsbasiert, d. h., allen Objekten ist ein Referenzzhler zugeordnet, der anzeigt, wie viele Benutzer eines Objekts es gibt. Gibt es keine Benutzer mehr, wird das Objekt gelscht. Der ursprngliche Objective-C-Speicherverwaltungsmechanismus, die manuelle Referenzzhlung, erfordert, dass der Entwickler signalisiert, ob ein Objekt aufbewahrt werden soll bzw. dass ein Objekt gelscht werden kann. In den letzten Jahren hat man versucht, diesen Prozess mit anderen Technologien strker zu automatisieren. Zunchst wurde mit Objective-C 2.0 eine Garbage Collection (kurz GC) eingefhrt, die unter Mac OS X untersttzt wurde, von iOS hingegen nicht. 2011 wurde dann das sogenannte Automatic Reference Counting, kurz ARC, eingefhrt. ARC nimmt dem Programmierer groe Teile der Arbeiten ab, die bei der Spei-

102 | Kapitel 10: Speicherverwaltung

cherverwaltung anfallen. Anders als die GC wird ARC von Mac OS X und iOS untersttzt.1 Informationen zu den Speicherverwaltungstechnologien und ihrer jeweiligen Untersttzung bei den Betriebssystem- und Entwicklungswerkzeugversionen finden Sie in Tabelle 10-1:
Tabelle 10-1: ARC und GC-Untersttzung Mac OS X iOS Xcode ARC ab 10.6 (10.7) ab 4.0 (5.0) ab 4.2 GC 10.5 10.7 x 3.0 4.4

Seit Mac OS X 10.8 gilt die Garbage Collection als veraltet. Apple empfiehlt ARC nicht nur fr neue Projekte, sondern rt auch, alte Projekte auf ARC umzustellen, und bietet in seinen Entwicklungswerkzeugen Tools, die die Umstellung eines auf manueller Referenzzhlung oder Garbage Collection basierenden Projekts automatisieren. Der Garbage Collection-Mechanismus wird in diesem Buch deswegen nicht weiter erlutert. ARC und die (seit Einfhrung von ARC auch MRC abgekrzte) manuelle Referenzzhlung basieren darauf, dass mit Objekten ein sogenannter Referenzzhler verbunden ist, der festhlt, wie viele Benutzer ein Objekt hat, und signalisiert, ob ein Objekt noch bentigt wird. Zeigt dieser an, dass ein Objekt nicht mehr bentigt wird, wird es aus dem Speicher gelscht. Beide Technologien verlangen deswegen, dass der Code, der mit Objekten arbeitet, signalisiert, dass er ein Objekt bentigt bzw. nicht mehr bentigt. Bei der manuellen Referenzzhlung ist der Programmierer selbst fr die Aufbewahrung und Freigabe der Objekte verantwortlich, mit denen er arbeitet. Objective-C stellt dazu Nachrichten bereit, mit denen der Referenzzhler gesteuert, d. h. heraufgesetzt oder vermindert werden kann. Diese muss der Programmierer so nutzen, dass die Objekte, mit denen er arbeitet, so lange erhalten bleiben, wie er sie bentigt, und gelscht werden, sobald er sie nicht mehr bentigt.
1 Beachten Sie, dass bei ARC vor Mac OS X 10.7 und iOS 5 keine schwachen Referenzen untersttzt werden.

Speicherverwaltungstechnologien unter Objective-C | 103

Bei der automatischen Referenzzhlung ermittelt das System auf Basis der Nutzung der Referenz weitgehend automatisch, ob das von der Referenz referenzierte Objekt weiterhin aufbewahrt werden muss bzw. freigegeben werden kann. Der Programmierer muss dazu bei der Deklaration seiner Eigenschaften, Felder und Variablen nur angeben, wie bindend die jeweiligen Referenzen fr die Objekte sein sollen, die von ihnen referenziert werden. Eins der Probleme, die beide Technologien lsen mssen, ist die bergabe der Verantwortung fr ein Objekt an eine andere Programmeinheit. Eine Methode kann nichts mehr tun, nachdem sie einen Wert zurckgegeben hat. Wenn eine Methode ordentlich hinter sich aufrumen will, muss sie die von ihr genutzten Objekte also freigeben, bevor sie endet. Das gilt auch fr Objektreferenzen, die einen Rckgabewert darstellen. Dann wrde das entsprechende Objekt aber bereits gelscht, nachdem die Methode es freigegeben hat, ohne dass der aufrufende Code berhaupt die Mglichkeit erhlt, selbst die Verantwortung fr seine Aufbewahrung zu bernehmen. Dieses Problem wird mit sogenannten Auto-Release-Pools gelst. Das sind spezielle Speicherstrukturen, die Objekte kurze Zeit am Leben halten garantiert so lange, dass eine andere Programmeinheit die Verantwortung dafr bernehmen kann.

ARC (Automatic Reference Counting)


Mit dem Automatic Reference Counting versucht Apple, Objective-C eine Speicherverwaltungstechnologie zu spendieren, die fr den Programmierer hnlich einfach und sicher zu verwenden ist wie eine Garbage Collection, ohne dabei die Nachteile in Kauf nehmen zu mssen, die eine solche mit sich bringen kann. Anders als eine Garbage Collection, die laufzeitbasiert ist, ist ARC eine Compilereinrichtung. Der Compiler analysiert den Gebrauch eines Objekts und generiert automatisch die erforderlichen Anweisungen fr die Speicherverwaltung. Das macht er so geschickt, dass sich Code meist so verhlt, wie es ein Programmierer erwarten wrde, der mit einer Sprache wie Java aufgewachsen ist.

104 | Kapitel 10: Speicherverwaltung

Schauen wir uns ein Beispiel an (bei dem ein alter Objective-CHase, der mit der manuellen Referenzzhlung vertraut ist, die Hnde ber dem Kopf zusammenschlagen wrde):
id ding1 = [[Ding alloc] initMitName: @"Haus"]; id ding2 = [Ding dingMitName: @"Boot"]; // Zwei Referenzen, die zwei Objekte referenzieren ding1 = ding2; // Zwei Referenzen, die ein Objekt referenzieren ding2= nil; // Eine Referenz, die ein Objekt referenziert

In diesem Beispiel werden zwei Objekte erstellt und Variablen zugewiesen. Beide Objekte werden so lange im Speicher festgehalten, wie die von den Variablen gespeicherten Referenzen auf die Objekte bestehen. Das erste Ding-Objekt wird zur Lschung freigegeben, wenn ding1 ebenfalls die Referenz zugewiesen wird, die von ding2 festgehalten wird, da es nun keine Referenz mehr auf dieses Objekt gibt. Nun gibt es zwei Referenzen auf das zweite DingObjekt, es wird also noch nicht zur Lschung freigegeben, wenn eine der Referenzen, hier die in ding2, gelscht wird. So sieht Ihr Code im Idealfall aus, wenn Sie ARC nutzen. Wie Sie sehen werden, wenn Sie sich den Abschnitt zur manuellen Referenzzhlung vornehmen, mssen Sie sich erheblich weniger Gedanken ber die Speicherverwaltung machen und erheblich weniger Speicherverwaltungscode schreiben als bei der manuellen Referenzzhlung. Das kann allerdings nur der Idealfall sein. Es wrde unausweichlich zu Problemen fhren, wenn sich alle Referenzen auf diese Weise verhielten. Zum Beispiel wre es unmglich, Referenzzyklen zu brechen. ARC unterscheidet deswegen verschiedene Arten von Referenzen, und Sie mssen gegebenenfalls die Art der Referenz signalisieren, die verwendet werden soll.

Starke und schwache Referenzen


Unter ARC werden Variablen, Felder und Eigenschaften in Bezug darauf unterschieden, wie bindend die von ihnen festgehaltenen Referenzen sind. Referenzen knnen stark oder schwach sein; starke Referenzen sind bindend, schwache Referenzen nicht. Nur starke Referenzen sorgen dafr, dass ein Objekt im Speicher aufbewahrt

ARC (Automatic Reference Counting) | 105

wird. Gibt es keine starken Referenzen mehr, wird das Objekt freigegeben und aus dem Speicher entfernt, unabhngig davon, wie viele schwache Referenzen noch darauf bestehen. Alle bestehenden schwachen Referenzen werden auf nil gesetzt. Standardmig stellen alle Eigenschaften und Variablen, die Sie definieren, starke Referenzen dar. Das fhrt zu dem Verhalten, das der am Anfang dieses Abschnitts vorgestellte Codeauszug aufweist. Wie dort mssen Sie in der Regel so gut wie nichts fr die Speicherverwaltung tun. Sollen sich Felder, Eigenschaften oder Variablen anders verhalten, knnen Sie das mit den neu eingefhrten Eigenschaftsattributen und Variablenqualifizierern angeben.

Eigenschaftsattribute
Mit ARC werden zwei neue Eigenschaftsattribute, strong und weak, eingefhrt: strong Der Standardwert, sorgt dafr, dass ein von der Eigenschaft referenziertes Objekt garantiert erhalten bleibt, solange diese Referenz besteht bzw. solange diese Referenz auf das entsprechende Objekt zeigt. weak Gibt an, dass die Referenz eine schwache Referenz ist, die ein Objekt nicht im Leben hlt. Beispielsweise entspricht also NSString * (strong) name; dem krzeren, aber weniger eindeutigen NSString * name;. Beides bewirkt, dass ein Objekt, das der Eigenschaft mit
[derda name: [NSString stringWithFormat: @"%@", @"12345678"]];

zugewiesen wird, erhalten bleibt, solange name auf dieses Objekt zeigt. Das bedeutet, das Objekt wird erst dann freigegeben, wenn name ungltig wird oder wenn name z. B. mit
[derda name: nil];

neu zugewiesen wird.

106 | Kapitel 10: Speicherverwaltung

Schwache Referenzen bentigen Sie, um Referenzzyklen (Objekt a hlt eine Referenz auf Objekt b, das seinerseits eine Referenz auf Objekt a hlt) zu durchbrechen. Das ist etwas, das ARC im Unterschied zu einer Garbage Collection ohne Hilfe des Programmierers nicht lsen kann. Beispielsweise wrden Sie eine schwache Referenz nutzen, um bei der Implementierung einer doppelt verketteten Liste einen Referenzzyklus zu vermeiden:
@interface Listenelement: NSObject Listenelement *(strong) naechstes; Listenelement *(weak) voriges; @end

ltere Mac OS X- und iOS-Versionen untersttzen schwache Referenzen nicht. Auerdem gibt es Bibliothekscode, der keine schwachen Referenzen untersttzt. Nutzen Sie in solchen Fllen das Eigenschaftsattribut assign, wenn Sie eine schwache Referenz bentigen.

r Variablen Qualifizierer fu
Neben den zwei Eigenschaftsattributen gibt es vier Qualifizierer fr Variablen, mit denen Sie Variablen und Felder versehen knnen, um ihr Bindungsverhalten festzulegen. Die Qualifizierer werden jeweils von zwei Unterstrichen eingeleitet: _ _strong Der Standardwert. Markiert eine starke Referenz und sorgt dafr, dass das referenzierte Objekt erhalten bleibt, solange diese Referenz besteht. _ _weak Markiert eine schwache Referenz, die nur so lange gltig ist, wie es andere, starke Referenzen auf ein Objekt gibt. Wird die letzte starke Referenz auf ein Objekt ungltig, werden alle vorhandenen schwachen Referenzen auf nil gesetzt.

ARC (Automatic Reference Counting) | 107

_ _unsafe_unretained Markiert ebenfalls schwache Referenzen, die allerdings nicht auf nil gesetzt werden, wenn das referenzierte Objekt aus dem Speicher entfernt wird. Wird bentigt, wenn Sie Code schreiben, der mit Mac OS X- beziehungsweise iOS-Versionen kompatibel ist, die keine schwachen Referenzen (__weak) untersttzen, oder um mit Code zu interagieren, der nicht ARC-basiert ist. _ _autoreleasing Markiert Argumente, die per Referenz bergeben und bei der Rckkehr der Funktion/Methode automatisch freigegeben werden. Wenn Sie das Hintergrundfeld fr eine strong-Eigenschaft explizit angeben mssen, wrden Sie folglich den Qualifizierer __strong nutzen, bei einer weak-Eigenschaft hingegen den Qualifizierer __weak:
@interface Listenelement: NSObject { Listenelement * _ _strong _naechstes; Listenelement * _ _weak _voriges; } Listenelement *(strong) naechstes; Listenelement *(weak) voriges; @end

Auf dem Stack, d. h. methodenlokal, mssen Sie mit __weak vorsichtig sein, zum Beispiel:
id __weak ding = [[Ding alloc] initMitName: @"Hut"];

Da es keine starke Referenz auf das so erstellte Objekt gibt, wird es unmittelbar zur Lschung freigegeben.

cke Auto-Release-Blo
Neben den Eigenschaftsattributen und Variablenqualifizierern werden mit ARC die @autorelasepool-Direktive und die sogenannten Auto-Release-Pool-Blcke eingefhrt:
@autoreleasepool { // Code, fu r den dieser Auto-Release-Pool-Block aktiv ist }

108 | Kapitel 10: Speicherverwaltung

Cocoa erwartet, dass Ihr Code in einem Auto-Release-Pool-Block ausgefhrt wird. Ein Auto-Release-Pool-Block stellt den Auto-Release-Pool bereit, der die Objekte festhlt, die automatisch freigegeben werden. Gibt es keinen Auto-Release-Pool, fhrt das zu Speicherlchern. Wenn Sie eine Mac OS X- oder iOS-App schreiben, sorgt das jeweilige Framework, AppKit bzw. UIKit, dafr, dass stets ein Auto-Release-Block verfgbar ist. Deswegen mssen Sie in Ihrem Code sehr selten eigene @autoreleasepool-Blcke schreiben. Eigene Auto-Release-Pool-Blcke mssen Sie nur einsetzen, wenn Sie eine Konsolenanwendung schreiben, wenn Sie einen neuen Thread starten, wenn Sie in einer Schleife sehr viele Objekte erstellen.

Neue Regeln
Haben Sie bereits Objective-C-Code geschrieben, der auf einer manuellen Referenzzhlung basiert, mssen Sie beachten, dass sich einigen Regeln gendert haben: 1. So gut wie alles, was mit der manuellen Speicherverwaltung zu tun hat, ist tabu: retain, release, retainCount und auto release drfen weder gesendet noch implementiert werden; dealloc darf nicht mehr gesendet, aber noch implementiert werden; dealloc-Implementierungen drfen nicht [super dealloc] nutzen (das Senden der Nachricht an super erfolgt unter ARC automatisch); NSAutoreleasePool-Objekte sind verboten (ARC fhrt stattdessen die @autoreleasepool-Direktive und die damit verbundenen Autorelease-Blcke ein). 2. ARC kmmert sich nur um echte Objective-C-Objekte. Bei der Interaktion mit C bzw. bei der Arbeit mit Core FoundationObjekten mssen deswegen einige Punkte beachtet werden: C-Structs drfen keine Objective-C-Objektzeiger enthalten; id-Referenzen und void *-Zeiger knnen nicht mehr unproblematisch ineinander umgewandelt werden; und Sie mssen spezielle Casts nutzen, die ARC sagen, wie die resultierenden Objekte verwaltet werden.

ARC (Automatic Reference Counting) | 109

3. Die Verwendung der Foundation-Funktionen NSAllocate Object() und NSDeallocateObject() sowie der Einsatz von NSZone-Objekten sind nicht gestattet. 4. Sie drfen keine Zugriffsmethoden definieren, deren Name mit new beginnt (ARC braucht das, um die Interoperabilitt mit Code zu gewhrleisten, der nicht fr ARC kompiliert wurde).

MRC (Manuel Reference Counting)


Bei der manuellen Referenzzhlung muss Ihr Code die Aufbewahrung und die Freigabe der Objekte, die er nutzt, eigenhndig steuern. Der Referenzzhler der von Ihnen genutzten Objekte wird nicht automatisch angepasst, wenn Ihr Code eine neue Referenz auf ein Objekt anlegt, sondern durch Nachrichten gesteuert, die Sie dem Objekt senden.

Speicherverwaltungsnachrichten
Das NSObject-Protokoll definiert vier Nachrichten, die die manuelle Speicherverwaltung steuern: -retain, -release, -autorelease und -retainCount.2 -retain sagt, dass ein Objekt fr Sie aufgehoben werden soll, whrend -release sagt, dass Sie ein Objekt nicht mehr bentigen. -autorelease stellt ein Objekt in den aktiven AutoRelease-Pool. -retainCount zeigt an, wie oft die Aufbewahrung eines Objekts angefordert wurde.
Sie sollten nie den retainCount-Wert nutzen, um zu prfen, von wie vielen Benutzern ein Objekt genutzt wird, da er auch den Gebrauch durch Framework-Objekte oder Auto-ReleasePools widerspiegelt und deswegen keine zuverlssige Aussage darber ermglicht, wie lange ein Objekt noch bestehen wird.

Die manuelle Speicherverwaltung mit diesen NSObject-Methoden, Auto-Release-Pools und der entsprechenden Untersttzung durch
2 Seit Mac OS X 10.8 und iOS 6 sind diese Methoden in Apples Dokumentation als obsolet markiert.

110 | Kapitel 10: Speicherverwaltung

die Runtime beruht auf einem einzigen, eigentlich ziemlich einfachen Prinzip: Sie mssen alle Objekte freigeben, die Sie besitzen, und drfen keine Objekte freigeben, die Sie nicht besitzen. Wenn Sie wissen wollen, ob Sie sich um die Freigabe eines Objekt kmmern drfen und mssen, mssen Sie also wissen, wie Sie in Erfahrung bringen, ob Sie ein Objekt besitzen oder nicht. Sie sind Besitzer eines Objekts, wenn Sie ein Objekt mit einer Methode erstellen, deren Name mit alloc, new, copy oder mutableCopy beginnt, wenn Sie ein Objekt explizit in Besitz nehmen, indem Sie ihm die retain-Nachricht senden. Dass eine Methode ein Objekt liefert, das der Aufrufer besitzt, heit, dass der Referenzzhler fr das entsprechende Objekt durch den Aufruf der Methode um eins erhht wird und die Methode dem Aufrufer die Verantwortung fr die korrekte Rcknahme dieser Erhhung berlsst. Welche Methoden ein Objekt liefern, fr das bereits retain aufgerufen wurde, wird allein durch eine Konvention geregelt. An diese Konventionen mssen Sie sich auch in Ihren eigenen Klassen halten. Methoden mit Namen, die nicht auf diese Weise beginnen, liefern konventionsgem ein Objekt, das automatisch freigegeben wird. Das heit, das zurckgelieferte Objekt wird vom aktuellen AutoRelease-Pool am Leben gehalten und dann gelscht, wenn dieser geleert wird. Erhalten Sie ein Objekt von einer solchen Methoden, mssen Sie davon ausgehen, dass es automatisch freigegeben wird, und mssen selbst dafr sorgen, dass es ber die Leerung des aktuellen Auto-Release-Pools erhalten bleibt, wenn Sie es lnger bentigen. Schauen wir uns das gleiche Beispiel an wie im ARC-Abschnitt:
id ding1 = [[Ding alloc] initMitName: @"Hut"]; id ding2 = [Ding dingMitName: @"Elefant"]; ding1 = ding2; ding2= nil;

MRC (Manuel Reference Counting) | 111

Dieser Code besitzt das Objekt, auf das ding1 zeigt, aber nicht das Objekt, auf das ding2 zeigt, da das erste Objekt mit alloc erstellt wurde, das zweite hingegen nicht. Der Name der Methode, die zur Erstellung des zweiten Objekts genutzt wird, sagt konventionsgem, dass das zurckgelieferte Objekt automatisch freigegeben wird. Der Code ist also fr die Freigabe des ersten Objekts verantwortlich, whrend er das zweite Objekt nicht freigeben darf. In dieser Form fhrt dieser Code deswegen zu einem Speicherloch. Wenn ding1 auf ding2 gesetzt wird, wird die letzte Referenz auf das Ding-Objekt gelscht. Der Code hat keine Mglichkeit mehr, das Objekt freizugeben. Dem retain, das das [Ding alloc] impliziert, kann nie mehr das erforderliche release folgen. Bei einer manuellen Speicherverwaltung msste der Code dafr sorgen, dass das Ding-Objekt korrekt freigegeben wird. Das lsst sich auf zweierlei Weise erreichen: 1. Das Objekt freigeben, bevor die Referenz berschrieben wird
id ding1 = [[Ding alloc] initMitName: @"Hut"]; id ding2 = [Ding dingMitName: @"Elefant"]; [ding1 release]; ding1 = ding2; ding2 = nil;

2. Das Objekt automatisch freigeben


id ding1 = [[[Ding alloc] initMitName: @"Hut"] autorelease]; id ding2 = [Ding dingMitName: @"Elefant"]; ding1 = ding2; ding2 = nil;

Im ersten Fall wird das Objekt freigegeben, unmittelbar bevor die Referenz darauf berschrieben wird. Im zweiten Fall wird es an den aktuellen Auto-Release-Pool bergeben. Das endgltige release wird ihm erst gesendet, wenn der Auto-Release-Pool gelscht wird.

112 | Kapitel 10: Speicherverwaltung

Es wird ziemlich hei debattiert, welches Verfahren besser ist. release sagt (auch dem Leser Ihres Codes) eindeutig, dass Sie das Objekt nicht mehr bentigen, und erlaubt der Runtime, den Speicher unmittelbar freizugeben. au torelease sagt nur, dass Sie es irgendwann nicht mehr bentigen werden und es der Runtime berlassen, wann es endgltig freigegeben wird. Auf Codeebene sind beide Verfahren in der Regel trotzdem gleichwertig. autorelease wird nur kritisch, wenn Sie sehr viele Objekte (z. B. in einer Schleife) nutzen. Dann sollten Sie entweder release nutzen oder einen lokalen Auto-Release-Pool einsetzen, der so hufig geleert wird, dass der Speicherbedarf nicht berhand nimmt.

Beachten Sie, dass fr diesen Code in Bezug auf das zweite DingObjekt nichts unternommen werden muss. Der Methodenname sagt, dass das erstellte Objekt automatisch freigegeben wird. Ganz gleich, wie viele Referenzen es darauf gibt es wird irgendwann von der Runtime freigegeben. Anders sieht es aus, wenn Sie das DingObjekt lnger festhalten, z. B. einem Feld zuweisen wollen:
self->ding = [Ding dingMitName: @"Elefant"];

Da das auf die dingMitName: -Nachricht erstellte Objekt automatisch freigegeben wird, wird es irgendwann von der Runtime freigegeben. Das Objekt, auf das self->ding zeigt, kann also jederzeit verschwinden. Sein Fortbestehen muss gesichert werden, indem dem Objekt die retain-Nachricht gesendet wird:
self->ding = [[Ding dingMitName: @"Elefant"] retain];

Diesem retain muss natrlich spter ein korrespondierendes re lease folgen. Dazu definieren Sie in Ihren Klassen -dealloc-Nachrichten, in denen Sie die Ivars und Eigenschaften Ihrer Objekte freigeben. Fr das von uns in Besitz genommene Feld she das so aus:
- (void) dealloc { [self->ding release];

MRC (Manuel Reference Counting) | 113

[super dealloc]; }

Nachdem alle eigenen Felder freigegeben wurden, leitet -dealloc die Nachricht an die Oberklasse weiter, damit diese hinter sich aufrumen kann.

Auto-Release-Pools
Auto-Release-Pool-Blcke funktionieren unter MRC genau so wie unter ARC. Sie nutzen die @autoreleasepool-Direktive, um einen Auto-Release-Pool anzufordern. Vor der Einfhrung von ARC und den Auto-Release-Pool-Blcken mit @autoreleasepool basierten Auto-Release-Pools auf NSAuto releasePool-Objekten, die angelegt werden mussten, weil Cocoa erwartet, dass ein Auto-Release-Pool verfgbar ist. In lterem Code werden Sie deswegen auf folgende Konstruktion stoen:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; ... [pool drain];

NSAutoreleasePool-Objekte sind spezielle Objekte, deren Verhalten

sich von anderen Objekten unterscheidet. Sie legen den Pool einmal an, nutzen ihn hinterher aber nie direkt. Die Runtime sorgt dafr, dass die Objekte, denen Sie die autorelease-Nachricht senden, dem Pool hinzugefgt werden. Auerdem knnen sie z. B. nicht mit retain festgehalten werden.

114 | Kapitel 10: Speicherverwaltung

KAPITEL 11

Laufzeitinformationen

Laufzeitinformationen zu Objekten und Klassen knnen Sie ber einige Nachrichten erhalten, die vom NSObject-Protokoll und der Klasse NSObject definiert werden. Fr weitergehende Informationen mssen Sie eventuell direkt auf die in objc/runtime.h definierten Funktionen zurckgreifen. Das Foundation-Framework bietet zustzlich einige Funktionen, die die Interaktion mit der Laufzeit vereinfachen.

Die Objective-C-Laufzeitumgebung
Die Objective-C-Runtime definiert die Umgebung, in dem Ihr Objective-C-Code luft. Sie definiert die C-Funktionen, in deren Aufrufe Ihre Objective-C-Konstrukte bersetzt werden. Teile der Informationen, die die Laufzeitumgebung ber die Elemente Ihres Objective-C-Codes zur Verfgung stellt, werden auch auf ObjectiveC-Ebene, d. h. ber Klassen- und Objektnachrichten, zugnglich gemacht. Aber fr manche Informationen und Operationen gibt es auf Objektebene kein Gegenstck. Wenn Sie diese nutzen wollen, mssen Sie sich mit der Laufzeitumgebung und ihren Funktionen vertraut machen. Das kann diese Einfhrung in Objective-C nicht leisten. Die Funktionen der Laufzeitumgebung ermglichen Ihnen unter anderem folgende Operationen: Zugriff auf Klasseninformationen wie Name, Oberklasse, bernommene Protokolle, Methoden- und Ivar-Listen. Zugriff auf Informationen zu den Instanzvariablen und Methoden von Objekten bzw. Klassen.

| 115

Interaktion mit den Instanzvariablen von Objekten. Zugriff auf Informationen zu Protokollen. Zugriff auf Informationen zu Selektoren und dynamische Registrierung von Selektoren. Dynamische Hinzufgung von Klassen zur Laufzeitumgebung. Dynamische Hinzufgung von Methoden zu Klassen. Dynamische Ersetzung von Methodenimplementierungen. usw. Weitere Informationen finden Sie in Apples Objective-C RuntimeReference und im objc/runtime.h-Header, in dem diese Features deklariert werden.

Objektinformationen
Die Nachrichten -hash und -description liefern eine numerische bzw. eine textuelle Reprsention eines Objekts. -hash entspricht den Hashcode-Eigenschaften bzw. Methoden, die Sie aus anderen Programmiersprachen wie Java kennen, und liefert einen Wert, der ein Objekt zureichend eindeutig identifiziert. -description ist mit den toString()- bzw. ToString-Methoden von Java und C# vergleichbar. Sie wird z. B. vom %@-Formatparameter genutzt, um eine Textdarstellung eines Objekts zu erhalten (falls es keine Implementierung fr die spezifischere -descriptionWithLocale:-Nachricht bietet, die viele Foundation-Klassen implementieren). Das bedeutet, die zwei Logging-Anweisungen unten liefern die gleiche Ausgabe:
id datum = [NSDate date]; NSLog(@"Datum: %@", datum); NSLog(@"Datum: %@", [datum description]);

Die NSObject-Implementierung von -hash liefert die Speicheradresse des Objekts, die Implementierung von -description einen Wert der Form <NSObject: 0x123456789>, wobei der Hexwert die Speicheradresse darstellt. Die meisten Framework-Klassen berschreiben die -description-Nachricht und lassen sie Werte liefern, die eine angemessenere Reprsentation ihrer Instanzen darstellen.

116 | Kapitel 11: Laufzeitinformationen

-isEqual: prft wie erwartet, ob der Empfnger einem anderen Objekt gleich ist. Die NSObject-Implementierung vergleicht, wie

blich, Speicheradressen und betrachtet nur Zeiger auf dasselbe Objekt als gleich. Wenn Sie fr die Objekte Ihrer Klassen ein anderes Vergleichsverhalten wnschen, knnen Sie -isEqual: berschreiben. Beachten Sie, dass Sie dann auch -hash berschreiben mssen.

Klasseninformationen
Die Nachrichten -class und -superclass liefern die Class-Objekte, die die Klasse des Objekts bzw. seine Oberklasse reprsentieren. Mit den Nachrichten -isMemberOfClass: und -isKindOfClass: kann geprft werden, ob ein Objekt eine Instanz einer Klasse oder eine Instanz einer Klasse ist, die von einer anderen Klasse abgeleitet ist. Die Klassennachrichten +class, +superclass und +isSubclassOf Class: bieten quivalente Informationen.
Beachten Sie, dass der direkte Zugriff auf das isa-Feld seit Objective-C 2.0 veraltet ist und in diesem Buch deswegen nicht dokumentiert wird. Seit der Einfhrung von Tagged Pointern (markierten Zeigern, die unmittelbar einen Wert enthalten und nicht auf eine Adresse zeigen) kann er auerdem zu Programmfehlern fhren.

Klassenobjekte
Klassen beschreiben die Struktur von Objekten, d. h. ihre Instanzvariablen und Methoden. Aber Klassen sind selbst auch Objekte. Der Typ Class ist zwar ein Objective-C-Typ, der ber einen Zeiger auf einen opaken C-Struct-Typ deklariert wird, er wird zur Laufzeit aber durch ein Objekt reprsentiert. Anders als Klasseninstanzen, die gewhnlichen Objekte, mit denen Sie arbeiten, werden diese Klassenobjekte von der Laufzeit erstellt, wenn die Klasse geladen wird.

Klasseninformationen | 117

Die Klasse eines Objekts kann mit der Instanznachricht -class abgerufen werden:
Class stringKlasse = [@"123" class];

Das Klassenobjekt kann ebenfalls ber die Klassennachricht +class erhalten werden:
Class stringKlasse = [NSString class];

Der Klassenname selbst kann fr das Klassenobjekt nur als Empfnger einer Nachricht einstehen. Wenn Sie ein Klassenobjekt bentigen, mssen Sie es sich ber eine dieser Nachrichten oder die weiter unten aufgefhrten Alternativen beschaffen. Dass Klassen Objekte sind, macht es berhaupt erst mglich, ihnen Nachrichten zu senden. Wenn Sie eine Klassennachricht senden, wird diese mit dem Klassenobjekt als Empfnger ausgefhrt. Als Objekt sind sie gleichzeitig aber auch Teil der NSObject-Klassenhierarchie und knnen als NSObject-Instanzen behandelt werden. Das heit, Sie knnen Klassenobjekten NSObject-Instanznachrichten wie respondsToSelector: senden:
id klasse = [@"123" class]; if ([klasse respondsToSelector: @selector(stringWithFormat:)) { }

Wenn Sie Klassenobjekte festhalten, ist es deswegen in der Regel empfehlenswert, diese ber eine Objektreferenz statt ber einen Class-Zeiger festzuhalten. Klassen knnen diese NSObject-Instanzmethode durch Klassenmethoden gleichen Namens berschreiben. Beispielsweise definiert NSObject selbst eine +class-Nachricht, die bei einem Aufruf auf dem Klassenobjekt die -class-Instanznachricht verdeckt. Da Klassen Objekte sind, knnen sie in den Cocoa-Collection-Klassen gespeichert werden, z. B. in einem Array:
id klassen = @[[NSString class], [NSNumber class], [NSArray class]];

118 | Kapitel 11: Laufzeitinformationen

Sie knnen sogar als Dictionary-Schlssel verwendet werden, da NSObject speziell zu diesem Zweck die NSCopying-analogen Klassenmethoden +copyWithZone: und +mutableCopyWithZone: bietet.

Metaklassen
Da Klassen Objekte sind, haben sie selbst auch eine Klasse. Diese Klassenklasse wird als Metaklasse bezeichnet. Metaklassen werden genutzt, um die Nachrichten aufzulsen, die dem Klassenobjekt gesendet werden. Auf Objektebene haben Sie keine Mglichkeit, an das Metaklassenobjekt fr eine Klasse zu gelangen. Wenn Sie auf einem Klassenobjekt class aufrufen, wird nicht die Klasse der Klasse, sondern nur die Klasse selbst zurckgeliefert. Das liegt daran, dass die Klassennachricht class einfach self zurckliefert, das in einer Klassenmethode das Klassenobjekt reprsentiert. [[Klasse class] class] liefert also dasselbe Klassenobjekt wie [Klasse class]. Metaklasseninformationen knnen Sie deswegen nur mit Funktionen der Objective-C-Laufzeitumgebung abrufen, die fr Klassenobjekte eine andere Semantik als die +class-Klassenmethode bieten. Die Laufzeitfunktion object_getClass() liefert das Metaklassenobjekt fr eine Klasse, wenn das bergebene Objekt ein Klassenobjekt ist. Metaklassen haben die folgenden drei Eigenschaften: 1. Die Metaklasse hat den gleichen Namen wie die Klasse selbst, zum Beispiel:
id klasse = [NSString class]; id metaklasse = object_getClass(klasse); NSLog(@"Klasse: %@, Metaklasse: %@", klasse, metaklasse); NSLog(@"Gleiche Klasse? %@", klasse == metaklasse ? @"Ja" : @"Nein");

Die Ausgabe ohne Zeitstempel hat die folgende Gestalt. Die Ausgabe fr die letzte Codezeile zeigt Ihnen, dass die beiden Klassen trotz gleichen Namens unterschiedliche Objekte im Speicher darstellen, das Klassen- und das Metaklassenobjekt:
Klasse: NSString, Metaklasse: NSString Gleiche Klasse? Nein

Klasseninformationen | 119

2. Die Oberklasse der Metaklasse ist die Metaklasse der Oberklasse. Das heit, fr alle Klassen K gilt:
object_getClass([K superclass]) == [object_getClass([K class]) superclass]

3. Die Klasse der Metaklasse (sozusagen die Meta-Metaklasse) ist die Metaklasse der Basisklasse der Klassenhierarchie (fr unsere Zwecke also die Metaklasse von NSObject). Das heit, fr alle Klassen K gilt:
object_getClass([NSObject class]) == object_getClass(object_getClass([K class]))

Mit Metaklassenobjekten werden Sie bei alltglichen ObjectiveC-Aufgaben normalerweise nicht konfrontiert. Aber dass Sie wissen, dass es sie gibt, hilft Ihnen, die Struktur des Objective-CKlassensystems zu verstehen. Auf der einen Seite gibt es das hierarchische, vertikale Basisklasse-Oberklasse-Unterklasse-Verhltnis, das bedingt, welche Nachrichten eine Instanz einer Klasse untersttzt. Orthogonal dazu gibt es das Instanz-Klasse-Metaklasse-Verhltnis, das steuert, wie Nachrichten an eine Klasseninstanz oder ein Klassenobjekt tatschlich aufgelst werden, und dafr sorgt, dass Klassen und Metaklassen die vollstndige NSObject-Schnittstelle untersttzen.

Klassennamen
Es gibt verschiedene Mglichkeiten, den Namen einer Klasse zu ermitteln. Die Klasse NSObject definiert eine -className-Nachricht, die unter iOS allerdings nicht untersttzt wird, da sie Teil der Scripting-Komponenten ist. Das Foundation-Framework bietet eine NSStringFromClass()-Funktion, die den Klassennamen als Objective-C-Stringobjekt liefert, die Laufzeitumgebung bietet die class_getName()-Funktion, die einen C-String liefert. Auerdem liefert die NSObject-Instanznachricht -description bei einem Klassenobjekt als Empfnger den Namen der Klasse in Form eines Stringobjekts. Das Foundation-Framework bietet eine NSClassFromString-Funktion, die als Gegenstck zu NSStringFromClass dient. Sie liefert das Klassenobjekt zu einem Stringobjekt, wenn die Laufzeitumgebung

120 | Kapitel 11: Laufzeitinformationen

eine Klasse zu diesem Namen finden kann. Die Laufzeitumgebung bietet die Funktionen objc_getClass() und objc_lookUpClass(), die die Klasse zu einem bergebenen C-String suchen. Alle Funktionen liefern nil, wenn in der Laufzeitumgebung keine Klasse mit einem entsprechenden Namen registriert ist. Beachten Sie, dass Operationen, die auf Klassennamen basieren, inhrent fragil sind. Eines der speziellen Probleme, denen diese Verfahren bei der Objective-C-Programmierung auf den CocoaPlattformen unterliegen, wird im folgenden Abschnitt illustriert.

ngige Verhalten Klassenabha


Wenn das Verhalten Ihres Codes von der Klasse der Objekte abhngt, mit denen er operiert, sollten Sie beachten, dass die tatschliche Klasse eines Objekts nicht notwendigerweise der Klasse entspricht, die zu seiner Erstellung genutzt wurde! Schauen Sie sich z. B. folgenden Code an:
id mArray = [NSMutableArray new]; Class mArrayClass = [NSMutableArray class]; NSLog(@"Instanzklasse: %@", [mArray class]); NSLog(@"Instanzoberklasse: %@", [mArray superclass]); NSLog(@"Klassen: %@", [NSMutableArray class]); NSLog(@"Oberklasse: %@", [NSMutableArray superclass]); NSLog(@"Ist das Objekt eine Instanz dieser Klasse? %@", [mArray isMemberOfClass:mArrayClass] ? @"JA" : @"NEIN"); NSLog(@"Ist das Objekt ein Objekt dieser Art? %@", [mArray isKindOfClass:mArrayClass] ? @"JA" : @"NEIN");

Die Ausgabe (ohne Zeitstempel) drfte eine etwas andere Gestalt haben, als Sie vielleicht erwarten:
Instanzklasse: __NSArrayM Instanzoberklasse: NSMutableArray Klasse: NSMutableArray Oberklasse: NSArray Ist das Objekt eine Instanz dieser Klasse: NEIN Ist das Objekt ein Objekt dieser Art: JA

Die NSMutableArray-Instanz, mit der Sie arbeiten, ist tatschlich also eine Instanz einer Unterklasse von NSMutableArray. In diesem Fall knnten Sie das NSMutableArray-Objekt also nur mit der -isKindOf Class:-Nachricht als eine NSMutableArray-Instanz erkennen.
Klasseninformationen | 121

-isMemberOfClass: prft lediglich, ob der Empfnger eine Instanz der als Argument bergebenen Klasse ist, -isKindOfClass: prft zustzlich, ob der Empfnger eine Instanz einer Unterklasse ist.

Dieses Problem tritt bei den Klassenclustern, die in Cocoa gern genutzt werden, recht hufig auf. Wenn Sie derartige Techniken nutzen, mssen Sie also darauf achten, dass Sie die relevante Klassenhierarchie gut kennen bzw., besser noch, selbst aufgebaut haben. Schauen Sie sich zur zustzlichen Abschreckung den folgenden Ausdruck an:
[@"NSString-Objekt" isKindOfClass:[NSMutableString class]]

Dieser Ausdruck wird zu YES ausgewertet! Wenn Sie so die Vernderlichkeit eines Strings prfen, bevor Sie damit beginnen, seinen Inhalt zu verndern, sind Laufzeitfehler gewiss.
Beachten Sie, dass hier auch eine Funktionsprfung mit -respondsToSelector:, z. B. respondsToSelector: @selector(appendString:), versagen wrde!

Klassenobjekte erkennen
Da Class kein gewhnlicher Objective-C-Objekttyp ist, ist es nicht ganz einfach, zu erkennen, ob ein Objekt eine Klasseninstanz oder ein Klassen- bzw. Metaklassenobjekt reprsentiert. Auch dazu geht man am besten den Umweg ber die Laufzeit. Wenn ein Objekt ein Klassen- oder Metaklassenobjekt ist, muss seine Klasse selbst ein Metaklassenobjekt sein. Ob eine Klasse eine Metaklasse ist, kann mit der Laufzeitfunktion class_isMetaClass() geprft werden. Der folgende Code nutzt diese Funktion, um zu prfen, ob die Objekte in einem Array Klassenobjekte sind. Ist das der Fall, prft er zustzlich, ob es sich um ein Metaklassenobjekt handelt:
id klassen = @[@1, // Instanz [@1 class], // Klasse

122 | Kapitel 11: Laufzeitinformationen

object_getClass([@1 class]), // Metaklasse // Meta-Metaklasse object_getClass(object_getClass([@1 class]))]; for (id objekt in klassen) { BOOL istKlasse = class_isMetaClass(object_getClass(objekt)); NSLog(@"%@ ist Klasse? %@ ", objekt, istKlasse ? @"Ja" : @"Nein"); if(istKlasse) { NSLog(@"Und Metaklasse? %@ ", class_isMetaClass(objekt) ? @"Ja" : @"Nein"); } }

Funktionsinformationen
Mit den Nachrichten -respondsToSelector: und + instancesRes pondToSelector: kann geprft werden, ob der Empfnger einen Selektor untersttzt. Senden Sie -respondsToSelector: einem Objekt, um in Erfahrung zu bringen, ob es die angegebene Instanznachricht untersttzt. Senden Sie -respondsToSelector: der Klasse, um in Erfahrung zu bringen, ob sie die angegebene Klassennachricht untersttzt. Die Klassennachricht +instancesRespondTo Selector: prft, ob die Instanzen einer Klasse eine Instanznachricht untersttzen. Klassen reagieren nicht auf Instanznachrichten, Instanzen reagieren nicht auf Klassennachrichten. Folgende Ausdrcke demonstrieren das am Beispiel der NSString-Nachrichten +stringWithFormat und -substringToIndex. Alle Ausdrcke werden zu YES ausgewertet:
[NSString respondsToSelector: @selector(stringWithFormat:)] == YES; [NSString respondsToSelector: @selector(substringToIndex:)] == NO [NSString instancesRespondToSelector: @selector(substringToIndex:)] == YES [@"123" respondsToSelector: @selector(stringWithFormat:)] == NO; [@"123" respondsToSelector: @selector(substringToIndex:)] == YES;

Beispiele fr den praktischen Einsatz dieser Methoden finden Sie in Kapitel 12, Messaging.

Funktionsinformationen | 123

Diese Methoden prfen auch, ob der Klasse Methodenimplementierungen dynamisch mit +resolveInstanceMethod oder +resolve ClassMethod hinzugefgt werden. Wenn die Klasse den Weiterleitungsmechanismus nutzt, -forwardingTargetForSelector: bzw. -forwardInvocation, kann es hingegen sein, dass diese Methode NO liefert, obwohl ein Objekt die Nachricht dank Weiterleitung untersttzt. Gegebenenfalls sollten Sie -respondsToSelector: entsprechend berschreiben. Jede Anwendung muss selbst entscheiden, ob der Umstand, dass eine Klasse einen Selektor nicht untersttzt, als Fehler betrachtet werden soll oder nicht. Weitere Informationen zu den Nachrichten, die ein Objekt versteht, bzw. einen Zeiger auf die Implementierung selbst knnen Sie sich ber die Nachrichten -methodSignatureForSelector: und -method ForSelector: beschaffen, die im Kapitel 12, Messaging, ausfhrlicher betrachtet werden. Noch tiefer knnen Sie mit den Laufzeitfunktionen der method_-Gruppe bohren, die ein Method-Argument erwarten, das Sie sich mit den Funktionen class_getInstanceMethod() und class_getClassMethod() beschaffen knnen, die jeweils eine Klasse und einen Selektor als Argument erwarten.

Protokollinformationen
Das NSObject-Protokoll deklariert eine -conformsToProtocol:-Nachricht, mit der Sie prfen knnen, ob der Empfnger das angegebene Protokoll untersttzt. Die Klasse NSObject deklariert eine ergnzende +conformsToProtocol:-Nachricht. Diese beiden Nachrichten weisen unterschiedliches Verhalten auf. Die Klassennachricht prft nur, ob die Schnittstelle explizit oder implizit die bereinstimmung mit einem Protokoll deklariert. Implizit wird die bereinstimmung mit einem Protokoll deklariert wenn eine der Oberklassen die bereinstimmung mit dem Protokoll deklariert, oder wenn ein Protokoll, das von der Klasse oder einer ihrer Oberklassen bernommen wird, das Protokoll einschliet. Da NSMutableSet von NSSet abgeleitet ist und NSSet das

124 | Kapitel 11: Laufzeitinformationen

Protokoll NSSecuringCoding bernimmt, das seinerseits NSCoding einschliet, gilt z. B. Folgendes:


[NSMutableSet conformsToProtocol: @protocol(NSCoding)] == YES

Die Instanznachricht hingegen prft tatschlich, ob der Empfnger die im Protokoll deklarierten Nachrichten untersttzt. Sie kann deswegen als effizientere Alternative fr -respondsToSelector: genutzt werden, wenn die Untersttzung mehrerer Nachrichten berprft werden muss. Der folgende Code deklariert eine Klassenmethode, die -conforms ToProtocol: nutzt, um eine Liste mit Kopien der Elemente einer anderen Liste zu erstellen, die das Protokoll NSCopying untersttzen. In diesem Fall wre es mit einer berprfung der Untersttzung der -copy-Nachricht nicht getan, da -copy in NSObject zwar deklariert, aber nicht funktionsfhig implementiert wird:
+(void) trenneListe:(id<NSFastEnumeration>)liste inKopien:(NSMutableArray **)kopie originale:(NSMutableArray **)geborgt { for (id element in liste) { if ([element conformsToProtocol: @protocol(NSCopying)]) { [*kopie addObject: [element copy]]; } else { [*geborgt addObject: element]; } } }

Da zwei Listen zurckgegeben werden, nutzt der Code Referenzparameter. Die Parametervariablen mssen deswegen mit dem Dereferenzierungsoperator dereferenziert werden, wenn den referenzierten Objekten eine Nachricht gesendet werden soll. Beachten Sie, dass der Code allozierte und initialisierte NSMutableArray-Objekte erwartet.

Protokollinformationen | 125

KAPITEL 12

Messaging

Der Objective-C-Nachrichtenmechanismus ist von Grund auf dynamisch. Nachrichten werden erst zur Laufzeit an eine Methodenimplementierung gebunden. Bei der Kompilierung werden die Nachrichtennamen durch Selektoren ersetzt. Empfnger, Selektor und Argumente werden genutzt, um die Nachricht in einen Aufruf der Runtime-Funktion objc_msgSend() umzuwandeln. objc_msgSend() sucht dann zur Laufzeit den Code, der die Methodenimplementierung stellt, ruft diesen auf und bergibt ihm die Argumente. Zustzlich erhlt der Code den Empfnger und den Selektor (in Form von self und _cmd). Dieser Vorgang nimmt notwendigerweise mehr Zeit in Anspruch, als sie ein direkter Funktionsaufruf bentigen wrde. Das kann man an fr die Leistung kritischen Punkten umgehen, indem man eine Methodenimplementierung abruft und direkt aufruft (siehe Abschnitt Implementierungen cachen auf Seite 135). Kann keine passende Methodenimplementierung gefunden werden, kann die Klasse dynamisch eine Methodenimplementierung stellen oder die Nachricht an ein anderes Objekt weiterleiten. Fr die dynamische Methodenimplementierung knnen Sie die NSObject-Methoden -resolveInstanceMethod bzw. -resolveClassMethod implementieren, fr die Nachrichtenweiterleitung die NSObject-Methoden -forwardingTargetForSelector: und/oder -forwardInvocation: (siehe Kapitel 12, Messaging, Abschnitt Dynamische Methodenimplementierung auf Seite 137 bzw. Nachrichtenweiterleitung auf Seite 139).

| 127

Springt keiner dieser Mechanismen ein, wird dem Objekt die -doesNotRecognizeSelector-Nachricht gesendet, deren NSObjectStandardimplementierung eine Ausnahme auslst und damit normalerweise die Programmausfhrung abbricht.

Kompilierung
Die eigentliche Methodenimplementierung wird zwar erst zur Laufzeit ermittelt, aber der Compiler muss trotzdem bereits bei der Kompilierung entscheiden, welchen Rckgabetyp und welche Argumenttypen der objc_msgSend()-Aufruf verwendet. Dazu prft er die Selektoren anhand der Deklarationen, die in der Kompilationseinheit gltig sind. Unter ARC und MRC sind Nachrichten nur unter folgenden Bedingungen uneingeschrnkt zulssig: Wenn die Nachricht vom deklarierten statischen Typ des Objektzeigers, ber den sie gesendet wird, deklariert bzw. geerbt wird. Wenn die Signatur fr eine Nachricht, die ber einen Objektzeiger des Typs id gesendet wird, ermittelt werden kann. Was passiert, wenn ein Selektor unbekannt ist, hngt davon ab, welche Speicherverwaltungstechnologie zum Einsatz kommt. Unter ARC werden unter folgenden Bedingungen folgende Fehler gemeldet: Es fhrt zu einem Keine sichtbaren Schnittstellen fr deklarieren den Selektor -Fehler, wenn einem Objekt eine Nachricht gesendet wird, die fr den entsprechenden Objekttyp nicht deklariert ist, wenn der Objektzeiger statisch mit einem konkreten Typ typisiert ist. Es fhrt zu einem Kein Selektor fr Methode bekannt-Fehler, wenn einem Objekt, das ber einen Objektzeiger mit dem Typ id festgehalten wird, eine Nachricht gesendet wird, die nicht bekannt ist.

128 | Kapitel 12: Messaging

Unter MRC ist der Compiler nachsichtiger. Nachrichten fhren nie zu Fehlern. Die folgenden Konstellationen fhren zu Warnungen: Es fhrt zu einer -Methode nicht gefunden-Warnung, wenn eine Nachricht nicht bekannt ist, unabhngig davon, ob das Objekt ber einen id-Zeiger oder einen Zeiger mit einem konkreten statischen Typ festgehalten wird. Es fhrt zu einer Es kann sein, dass nicht auf -Nachricht reagiert-Warnung, wenn einem Objekt ber eine statisch mit einem konkreten Typ typisierte Referenz eine Nachricht gesendet wird, die bekannt, aber fr den entsprechenden Objekttyp nicht deklariert ist. Ein Beispiel: Angenommen, Sie haben eine Klasse Aufgabe, die eine
-aufMorgenVerschieben-Methode deklariert:
// Aufgabe.h #import <Foundation/Foundation.h> @interface Aufgabe : NSObject -(void) aufMorgenVerschieben; @end;

Es reicht dann, diesen Header in eine Kompilationseinheit zu importieren, damit der Compiler unter ARC keinen Fehler mehr meldet, wenn Sie einem beliebigen von einer id-Referenz festgehaltenen Objekt eine -aufMorgenVerschieben-Nachricht senden:
import "Aufgabe.h" id text = @"Dringend"; [text aufMorgenVerschieben]; // Kein Fehler, keine Warnung

Selektoren
Ein Selektor ist ein eindeutiger Bezeichner, der eine Methode identifiziert. Es gibt nur einen Selektor fr alle Methoden gleichen Namens, auch wenn mehrere Klassen eine Methode eines bestimmten Namens deklarieren. Das heit, wenn Sie zwei Klassen Vogel und Flugzeug haben, die beide eigene -fliegen-Methoden bieten, wird diese Methode durch denselben Selektor identifiziert.

Selektoren | 129

Das kann bei der Kompilierung zu Problemen fhren, wenn in einer Kompilationseinheit mehrere Methoden bekannt sind, die den gleichen Namen, aber unterschiedliche Argument- bzw. Rckgabetypen haben. Das kann zur Folge haben, dass der Compiler falsche Aufrufe kodiert. Unter ARC erhalten Sie in diesem Fall eine Warnung. Dieses Problem knnen Sie lsen, indem Sie den Empfnger auf den gewnschten Typ casten, zum Beispiel:
[(Flugzeug *)ju fliegen];

Objective-C definiert einen eigenen Typ, SEL, fr Selektoren. Selektoren knnen mit der Compilerdirektive @selector() oder der Runtime-Funktion NSSelectorFromString() abgerufen werden. Das Argument fr @selector() ist ein Token, das einen gltigen Methodennamen reprsentieren muss, das Argument fr NSSelector FromString() ein NSString-Objekt, das einen Methodennamen angibt. So knnten Sie sie einsetzen, um die Selektoren fr zwei NSString-Methoden abzurufen:
SEL stringAnfang = @selector(substringToIndex:); SEL stringEnde = NSSelectorFromString(@"substringFromIndex:");

Bei der Direktive muss der Methodenname bei der Kompilierung feststehen, bei der Funktion kann er zur Laufzeit aufgebaut werden.

Implementierungen
Zur Laufzeit dienen Selektoren als dynamische Funktionszeiger, ber die die fr das jeweilige Objekt passende Methodenimplementierung ermittelt wird. Die Methodenimplementierung ist der Code, der tatschlich zur Ausfhrung kommt, wenn einem Empfnger eine Nachricht gesendet wird. Implementierungen werden durch den Objective-C-Typ IMP beschrieben, der folgendermaen definiert ist:
typedef id (*IMP)(id, SEL, ...)

Diese Typdefinition definiert einen Zeiger auf eine C-Funktion, die ein id- und ein SEL-Argument sowie eine beliebige Anzahl weiterer Argumente erwartet und einen Wert des Typs id zurckliefert.

130 | Kapitel 12: Messaging

Die ersten beiden Argumente beschreiben self, die aktuelle Instanz, und _cmd, die aktuell verarbeitete Nachricht. Diese beiden Argumente sind das, was eine C-Funktion zu einer Objective-C-Methode macht. Wenn Sie eine C-Funktion schreiben, die als Methodenimplementierung einstehen soll, muss sie also mindestens diese beiden Argumente deklarieren. Arbeiten Sie direkt mit den Laufzeitfunktionen zur Interaktion mit Methodenimplementierungen, mssen Sie in der Regel die entsprechenden Werte fr diese Argumente angeben. Der in der Typdeklaration angegebene Rckgabetyp id ist nur ein Platzhalter fr den tatschlichen Rckgabetyp der Methode. Wenn Sie direkt mit Implementierungen arbeiten, sollten Sie nie rohe IMPs aufrufen. Stattdessen sollten Sie einen IMP immer auf einen Funktionszeigertyp casten, dessen Rckgabetyp und dessen weitere Argumenttypen dem Rckgabetyp und den Argumenttypen der jeweiligen Nachricht entsprechen. Beispiele fr die direkte Arbeit mit IMPs finden Sie in mehreren der nachfolgenden Abschnitte (unter anderem in Implementierungen cachen auf Seite 135).

hren Selektoren dynamisch ausfu


blicherweise steht die Nachricht, die einem Objekt gesendet wird, bereits bei der Kompilierung fest. Selektoren knnen eingesetzt werden, um die Nachricht, die einem Objekt gesendet wird, erst zur Laufzeit festzulegen. Dazu knnen Sie entweder die Methoden der -performSelector:-Familie, ein NSInvocation-Objekt oder einen direkten Aufruf von objc_msgSend() nutzen.

-performSelector:
Das NSObject-Protokoll definiert drei -performSelector:-Methoden, -performSelector:, -performSelector:withObject, -performSelec tor:withObject:withObject, die von NSObject implementiert werden. Auerdem bietet NSObject einige zustzliche -performSe lector-Methoden, mit denen Selektoren verzgert oder auf spezifischen Threads ausgefhrt werden knnen. Alle -performSe lector-Methoden erwarten einen Selektor und liefern einen Wert

Selektoren dynamisch ausfhren | 131

des Typs id. Die verschiedenen Varianten akzeptieren zwischen null und zwei id-Argumenten. Der nachfolgende Code nutzt -performSelector:withObject:, um aus einem NSDictionary-Objekt dynamisch mit der -objectFor Key:-Nachricht ein Element abzurufen:
id dict = @{@"a" : @1, @"b" : @2}; SEL sel = @selector(objectForKey:); NSLog(@"%@", [dict performSelector: sel withObject: @"b"]);

Beim gleichen Dict entspricht das:


[dict objectForKey: @"b"];

Wenn Sie -performSelector: wie hier mit einem SEL-Argument nutzen, das nicht konstant ist, erhalten Sie unter ARC eine Warnung, die besagt, dass der -performSelector: zu einem Speicherloch fhren kann, weil der Selektor unbekannt ist.

Wenn Sie auf Basis von Selektoren Methoden ausfhren wollen, die Objective-C-Typen erwarten oder liefern, mssen Sie objc_ msgSend() oder ein NSInvocation-Objekt nutzen.

NSInvocation
Die Klasse NSInvocation ermglicht es, Objekte zu erstellen, die Nachrichten beschreiben. Diese kapseln Empfnger und Argumente, schlieen Informationen zum Rckgabetyp und zu den Argumenttypen ein und bieten Mglichkeiten, die Nachricht auszufhren und den Rckgabewert abzurufen.
Beachten Sie, dass der NSInvocation-Mechanismus erheblich langsamer als -performSe lector: ist. Das macht ihn fr zeitkritische Anwendungsteile ungeeignet. Weichen Sie gegebenenfalls auf objc_msgSend oder Implementierungscaching aus.

132 | Kapitel 12: Messaging

Sie sollten ein NSInvocation-Objekt immer ber die Klassennachricht +invocationWithMethodSignature: erstellen, die eine Methodensignatur in Form eines NSMethodSignature-Objekts erwartet. Dieses beschaffen Sie sich fr einen Selektor ber eine der NSObject-Methoden +instanceMethodSignatureForSelector: oder -methodSignature ForSelector:. Ein NSInvocation-Objekt, das den objectForKey:-Aufruf beschreibt, den wir oben mit -performSelector:withObject: ausgefhrt haben, knnen Sie sich also folgendermaen beschaffen:
id dict = @{@"a" : @1, @"b" : @2}; id key = @"b"; SEL sel = @selector(objectForKey:); id sig = [NSDictionary instanceMethodSignatureForSelector: sel]; id aufruf = [NSInvocation invocationWithMethodSignature: sig];

Bevor das NSInvocation-Objekt ausgefhrt werden kann, mssen Ziel, Selektor und Argumente gesetzt werden:
[aufruf setSelector: sel]; [aufruf setTarget: dict]; [aufruf setArgument: &key atIndex: 2];

Das erste Argument fr -setArgument:atIndex: hat den Typ void *, mit dem unter C ein Zeiger beschrieben wird, der auf alles zeigen kann. Sie geben die Argumente unabhngig vom Typ bei einem Objekt wie bei einem int oder einem NSRect also mit dem Adressoperator & ber einen Zeiger an. Das zweite Argument fr -setArgument:atIndex: ist ein NSInteger, der die Position des Arguments im zu generierenden objc_msgSend() Aufruf angibt. Argumente 0 und 1 dieser Funktion sind Ziel und Selektor, die stets ber die dedizierten Nachrichten -setSelector: und -setTarget: (bzw. beim Aufruf ber -invokeWithTarget:) gesetzt werden sollten. Das erste Argument der eigentlichen Nachricht ist also das dritte Argument des objc_msgSend()-Aufrufs und hat deswegen den Index 2. Jetzt knnen Sie das NSInvocation-Objekt folgendermaen ausfhren:
[aufruf invoke]; id wert; [aufruf getReturnValue: &wert];

Selektoren dynamisch ausfhren | 133

Das Argument von -getReturnValue: hat ebenfalls den Typ void * und soll auf den Buffer zeigen, in den der Rckgabewert geschrieben werden soll. Wenn Sie als Rckgabewert ein Objekt erwarten, bergeben Sie einfach die Adresse einer Variablen eines kompatiblen Objekttyps. Hat der Rckgabewert einen Objective-C-Typ, mssen Sie etwas mehr Arbeit leisten. Ein Beispiel sehen Sie im folgenden Code, der ein NSInvocation-Objekt nutzt, um einem NSArray-Objekt die length-Nachricht zu senden:
SEL sel = @selector(length); id sig = [NSString instanceMethodSignatureForSelector: sel]; id aufruf = [NSInvocation invocationWithMethodSignature: sig]; [aufruf setSelector: sel]; [aufruf invokeWithTarget: text]; void *buffer = (void *)malloc([sig methodReturnLength]); [aufruf getReturnValue: buffer]; NSUInteger laenge = *(NSUInteger *)(buffer);

Nachdem aufruf mit -invokeWithTarget ausgefhrt wurde, wird ein Buffer fr das Ergebnis eingerichtet. Der Speicherplatz dafr muss manuell mit der C-Funktion malloc() alloziert werden. Die bentigte Menge Speicherplatz kann mit der NSMessageSignature-Methode -methodReturnLength ermittelt werden. In der letzten Zeile wird der Buffer zunchst auf den richtigen Zeigertyp gecastet (NSUInteger *) und dann mit dem Dereferenzierungsoperator * dereferenziert, um den Wert zu erhalten.

objc_msgSend()
objc_msgSend() ist eine (oder besser die) Funktion der Objective-

C-Laufzeitumgebung. Sie ist dafr verantwortlich, dass bei jeder Nachricht, die Sie senden, die richtige Methode auf dem richtigen Objekt zur Ausfhrung kommt. Dafr sucht sie zunchst die fr diese Kombination von Klasse und Selektor passende Implementierung, fhrt diese dann mit dem Objekt und den eventuell bergebenen Argumenten aus und liefert schlielich den Rckgabewert des Aufrufs als den eigenen Rckgabewert zurck. Der objc_msgSend()-Aufruf wird vom Compiler generiert. Normalerweise sollten Sie diese Funktion nie selbst aufrufen mssen. Aber ...

134 | Kapitel 12: Messaging

objc_msgSend() ist folgendermaen deklariert:


id objc_msgSend(id self, SEL sel, )

Wenn der Compiler einen objc_msgSend() erstellt, kodiert er diesen jedoch mit den erforderlichen Typen fr den jeweiligen Aufruf. Wollen Sie objc_msgSend() einsetzen, sollten Sie den Aufruf ebenfalls auf den entsprechenden Funktionszeigertypen casten. Zum Beispiel knnten Sie einem Stringobjekt folgendermaen dynamisch die length-Nachricht senden:
NSInteger res; SEL sel = @selector(length); res = ((NSInteger)(*)(id, SEL)objc_msgSend)(@"123", sel);

In der letzte Zeile wird der Funktionszeiger objc_msgSend auf den Typ (NSInteger)(*)(id, SEL) gecastet. Das ist eine Funktion, die einen NSInteger-Wert liefert und einen id- sowie einen SEL-Wert als Argumente erwartet. Eine entzerrte Version dieser Zeile she so aus:
typedef NSInteger (*IntVoidFunc)(id, SEL); IntVoidFunc str_len = (IntVoidFunc)objc_msgSend; NSInteger res = str_len(@"123", @selector(length));

Die erste Zeile deklariert den Funktionszeigertyp IntVoidFunc, die zweite definiert eine Funktion namens str_len mit diesem Typ, indem objc_msgSend auf diesen Typ gecastet wird, und die dritte ruft die so definierte Funktion auf.

Implementierungen cachen
Es ist mglich, die dynamische Methodenauflsung zu umgehen, z. B. weil eine Nachricht, in einer Schleife vielleicht, so oft gesendet werden muss, dass die bei jedem Aufruf erfolgende dynamische Methodenauflsung zu Leistungseinbuen fhrt.
NSObject definiert zwei Nachrichten, -methodForSelector: und +instanceMethodForSelector:, ber die ein Zeiger auf die Imple-

mentierung einer Instanz- oder Klassenmethode ermittelt werden kann. Die Instanzmethode -methodForSelector: kann einer Instanz gesendet werden, um Zugriff auf die Implementierung einer Instanzmethode zu erhalten, oder der Klasse selbst, um Zugriff auf eine Klassenmethode zu erhalten. ber die Klassennachricht
Implementierungen cachen | 135

+instanceMethodForSelector: kann die Implementierung einer In-

stanzmethode ber die Klasse abgerufen werden. Folgendermaen knnte man eine Implementierung cachen, um eine Operation auf allen Elementen einer Liste durchzufhren:
id text = @""; id texte = @[@"eins", @"zwei", @"drei"]; NSInteger ergebnis = 0; SEL laengeSel = @selector(length); typedef NSInteger (*IntVoidFunc)(id, SEL); IntVoidFunc laenge_imp = (IntVoidFunc)[text methodForSelector: laengeSel]; for (text in texte) { ergebnis += laenge_imp(text, laengeSel); }

Dieser Code nutzt die gleiche Typdefinition fr den Funktionszeigertyp IntVoidFunc wie das letzte Beispiel des vorangegangenen Abschnitts. Hier wird dieser Typ aber nicht genutzt, um einen objc_msgSend()-Aufruf auf den Typ der Methodenimplementierung zu casten, sondern um eine Funktionszeigervariable zu definieren, die die mit methodForSelector: abgerufene Methodenimplementierung festhlt. Diese wird dann in der Schleife jeweils mit dem aktuellen Iterationswert als Argument fr den Parameter self aufgerufen. Der wesentliche Unterschied ist, dass der objc_msgSend()-Aufruf die Methodenimplementierung jeweils dynamisch auflst. Wird die Methodenimplementierung z. B. wie hier mit methodForSelector: abgerufen und gespeichert, kann sie direkt aufgerufen werden. Die dynamische Methodenauflsung und die mit ihr verbundenen Kosten werden umgegangen.
Beachten Sie, dass Sie zu dieser Manahme nur greifen sollten, wenn leistungskritische Teile einer Anwendung dadurch tatschlich beschleunigt werden knnen. Die dynamische Methodenauflsung von Objective-C ist uerst schnell und effizient und greift intern bereits auf Caching-Mechanismen zurck. Der Beispielcode oben ist selbst bei einer Liste von 10 Millionen Elementen nicht konsistent schneller als eine ganz gewhnliche Objektnachricht!

136 | Kapitel 12: Messaging

Dynamische Methodenimplementierung
Wenn fr einen Empfnger keine Implementierung einer Nachricht gefunden werden kann, erhlt er die Mglichkeit, dynamisch eine Implementierung zu stellen. Dazu ruft die Laufzeitumgebung die Klassenmethoden +resolveInstanceMethod: bzw. +resolveClass Method: auf, je nachdem, ob es sich bei der unbeantworteten Nachricht um eine Instanznachricht oder eine Klassennachricht handelt. Eine +resolveInstanceMethod:- bzw. +resolveClassMethod:-Implementierung kann die Laufzeitfunktion class_addMethod() nutzen, um der Klasse dynamisch eine Implementierung fr einen Selektor hinzuzufgen. class_addMethod() erwartet die Klasse, der die Implementierung hinzugefgt werden soll, den Selektor fr die Implementierung, die Implementierung selbst und einen C-String, der die Signatur mit Typcodes beschreibt. Die Signatur der Funktion hat folgende Gestalt:
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

Basis fr die Implementierung bildet blicherweise eine C-Funktion. Diese muss als erstes und zweites Argument wie blich einen id- und einen SEL-Wert erwarten, die self und _cmd annehmen. Erst darauf folgen die eigentlichen Parameter der Objective-C-Nachricht. Nehmen wir beispielsweise an, Sie haben eine Klasse, die ein
NSArray-Objekt kapselt, und wollen gegebenenfalls dynamisch -objectAtIndex: untersttzen, falls Objekte Ihrer Klasse fr ein NSArray einstehen mssen. Eine C-Funktion, die diese Operation

stellt, knnte so aussehen:


id dynamicObjectAtIndex(id self, SEL _cmd, NSUInteger idx) { if ([[self liste] count] > idx) { return [[self liste] objectAtIndex: idx]; } else { return nil; } }

Der dritte Funktionsparameter entspricht dem eigentlichen Parameter, der in einer objectAtIndex:-Nachricht bergeben wird. Die

Dynamische Methodenimplementierung | 137

+resolveInstanceMethod:-Implementierung knnte dann folgende

Gestalt haben:
+ (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(objectAtIndex:)) { class_addMethod([self class], sel, (IMP)dynamicObjectAtIndex, "@@:Q"); return YES; } else { return [super resolveInstanceMethod:sel]; } }

Als Klasse bergeben wir die Klasse von self, als Implementierung die auf einen IMP gecastete C-Funktion. Das letzte Argument beschreibt die Typsignatur der Funktion. Jedes Zeichen im String gibt einen Typ an, das erste den Rckgabetyp, die weiteren die Argumenttypen. @ gibt einen Objektwert an, : einen Selektor und Q einen unsigned long long-Wert (der Typ, dem NSUInteger auf einem 64-Bit-System entspricht). Beachten Sie, dass wir uns nun auf C-Ebene bewegen und dass fr die Angabe der Typsignatur ein C-String verwendet wird. Statt einer C-Funktion kann die Basis fr die Implementierung auch ein Blockobjekt sein. Dazu wird ein definierter Block mit der Laufzeitfunktion imp_implematationWithBlock() in einen IMP umgewandelt. imp_implementationWithBlock() nimmt das Blockargument, kopiert den Block auf den Heap und liefert ein Sprungbrett (der offizielle Terminus ist Trampolin), ber das der Block wie eine Methodenimplementierung aufgerufen werden kann. Da Blocks (siehe Kapitel 2, Syntax, Abschnitt Blocks auf Seite 18) C-Funktionen mit nur einem vorgeschalteten Argument (self) entsprechen, sieht die Blocksignatur etwas anders aus der SEL-Parameter entfllt. Nachfolgende Blockdefinition ist der Definition der Funktion dynamicObjectAtIndex() weiter oben quivalent:
id (^blockOAI)(id, NSUInteger) = ^id(id self, NSUInteger idx) { if ([[self liste] count] > idx) { return [[self liste] objectAtIndex: idx]; } else { return nil; } };

138 | Kapitel 12: Messaging

Dieser Block kann dann mit imp_implementationWithBlock() in einen IMP umgewandelt werden, der der Klasse mit class_add Method() als Methode hinzugefgt wird:
IMP impOAI = imp_implementationWithBlock(blockOAI); class_addMethod([self class], sel, impOAI, "@@:Q");

Nachrichtenweiterleitung
Der Weiterleitungsmechanismus wird angestoen, wenn keine Methodenimplementierung gefunden wird und keine dynamische Methodenimplementierung generiert wurde. Die Infrastruktur dafr wird von zwei in NSObject deklarierten Methoden, -forwardInvoca tion: und -forwardingTargetForSelector:, gebildet. Diese knnen Sie in Ihren Klassen berschreiben, wenn die Objekte Ihrer Klassen eine Weiterleitung untersttzen sollen.

-forwardingTargetForSelector:
Zunchst ruft die Laufzeitumgebung -forwardingTargetForSelector: mit dem unbekannten Selektor als Argument auf. Wenn diese Methode ein Objekt ungleich nil zurckliefert, sendet die Laufzeit diesem die Nachricht, auf die das aktuelle Objekt nicht reagierte. Dieser einfache und effiziente Mechanismus ist fr den einfachsten Fall der Weiterleitung, die unvernderte Umleitung der Nachricht an ein anderes Objekt, gedacht. Wenn die Nachricht vor der Weiterleitung gendert oder das Resultat der Weiterleitung weiterverarbeitet werden muss, mssen Sie den teuren -forwardInvocation:-Prozess nutzen. Mit dieser Nachricht kann recht leicht eine Klasse aufgebaut werden, die als Proxy fr ein Objekt eines beliebigen anderen Typs dient, zum Beispiel (anderesObjekt knnte beispielsweise ein gekapseltes NSStringObjekt sein, an das alle NSString-Nachrichten umgeleitet werden):
- (id)forwardingTargetForSelector:(SEL)aSel { if ([anderesObjekt respondsToSelector: sSel]) { return anderesObjekt; } else { return [super forwardingTargetForSelector: aSel]; } }

Nachrichtenweiterleitung | 139

Der Code prft mit -respondsToSelector:, ob das Objekt anderes Objekt die erhaltene Nachricht untersttzt. Wenn das der Fall ist, wird anderesObjekt als Rckgabewert des Aufrufs zurckgegeben. Untersttzt das Objekt die Nachricht nicht, wird die Elternklassenversion der Nachricht aufgerufen und ihr Rckgabewert geliefert. Die Standardimplementierung in NSObject liefert nil. Liefert -forwardingTargetForSelector: einen Objektwert ungleich nil, versucht die Laufzeit, diesem Objekt die Nachricht zu senden, andernfalls wird auf dem ursprngliche Empfnger -forwardInvocation aufgerufen. Beachten Sie, dass die Nachrichtenverarbeitung nach der Umleitung nicht mehr im Kontext Ihres eigenen Objekts erfolgt, sondern vollkommen vom neuen Objekt bernommen wird. Eventuelle -forwardInvocation:- oder -doesNotRespondToSelector:-berschreibungen auf Ihrer eigenen Klasse kommen also nicht mehr zur Ausfhrung

-forwardInvocation:
Der Empfnger erhlt die -forwardInvocation:-Nachricht, wenn ihm ein unbekannter Selektor gesendet wurde, der von keiner der anderen Manahmen des Nachrichtenmechanismus verarbeitet wurde. Das Argument der Nachricht ist ein NSInvocation-Objekt, das die unbekannte Nachricht beschreibt. (NSInvocation wird in Abschnitt NSInvocation auf Seite 132 genauer beschrieben.) Dieses wird in einer -forwardInvocation:-berschreibung blicherweise genutzt, um einen geeigneten Empfnger fr die Nachricht zu suchen und das NSInvocation-Objekt dann mit diesem Empfnger als Ziel auszufhren. Die Laufzeitumgebung sorgt dafr, dass das Ergebnis der Ausfhrung des NSInvocation-Objekts (das ber die Eigenschaft return Value zugnglich ist) zum Rckgabewert der unbekannten Nachricht wird. Da returnValue den Typ void * hat, werden Rckgabewerte beliebigen Typs untersttzt. Wenn Sie -forwardInvocation: berschreiben, mssen Sie zustzlich die Nachricht -methodSignatureForSelector: berschreiben. Die Methodensignatur, die ber diese Nachricht abgerufen wird,
140 | Kapitel 12: Messaging

wird zur Konstruktion des NSInvocation-Objekts bentigt. Kann keine Methodensignatur fr einen Selektor ermittelt werden, kommt es zu einem Laufzeitfehler. Folgende berschreibung der beiden Nachrichten bewirkt das Gleiche wie die -forwarding TargetForSelector:-berschreibung aus dem letzten Abschnitt (ist allerdings erheblich langsamer):
- (NSMethodSignature*)methodSignatureForSelector:(SEL)sel { id sig = [super methodSignatureForSelector: selector]; if (!sig) { sig = [@"abc" methodSignatureForSelector: selector]; } return sig; } -(void)forwardInvocation:(NSInvocation *)anInvocation { if ([anderesObjekt respondsToSelector: [anInvocation selector]]) { [anInvocation invokeWithTarget: anderesObjekt]; } else { [super forwardInvocation: anInvocation]; } }

-methodSignatureForSelector: liefert ein Objekt, das eine Methodensignatur beschreibt, oder nil. Die Methodensignatur ist entwe-

der eine Signatur fr eine eigene oder eine geerbte Methode (die hier ermittelt wird, indem die Nachricht an die Oberklassen delegiert wird) oder eine Signatur fr eine Methode der Klasse von anderes Objekt. Besitzt keine der Klassen eine Methode, die dem Selektor entspricht, wird das nil-Ergebnis der Aufrufe durchgereicht.
-invokeWithTarget: ruft das NSInvocation-Objekt mit einem ande-

ren Objekt als Empfnger fr die gekapselte Nachricht auf. Falls das potenzielle Zielobjekt die unbekannte Nachricht nicht untersttzt, wird die Oberklassenversion von -forwardInvocation: aufgerufen. Die NSObject-Implementierung ruft -doesNotRespondToSelector:, dessen NSObject-Implementierung einen Laufzeitfehler auslst. Sie mssen das NSInvocation-Objekt nicht mit -invoke bzw. -invoke WithTarget: ausfhren, um den Rckgabewert zu generieren, sondern knnen ihn auch manuell setzen. Nehmen Sie z. B. an, die Objekte Ihrer Klasse sollen auf eine verirrte integerValue-Nachricht die Lnge des Strings liefern, den sie kapseln. Diesen Rckgabewert knnten Sie

Nachrichtenweiterleitung | 141

mit -setReturnValue: folgendermaen setzen, ohne das NSInvocationObjekt auszufhren (vorausgesetzt, -methodSignatureForSelector: liefert ein passendes NSMethodSignature-Objekt fr diesen Selektor):
- (void)forwardInvocation:(NSInvocation *)anInvocation { if ([anInvocation selector] == @selector(integerValue)) { NSInteger intVal = [[self test] length]; [anInvocation setReturnValue: &intVal]; } else { [super forwardInvocation:anInvocation]; } }

-setReturnValue erwartet wie die Methoden zum Setzen der Argu-

mente einen Zeiger auf die Daten.

142 | Kapitel 12: Messaging

KAPITEL 13

Key/ Value-Coding

Als Key/Value-Coding (kurz KVC) bezeichnet man den indirekten, dynamischen Zugriff auf die Instanzvariablen eines Objekts. Die Objective-C-Key/Value-Coding-Infrastruktur besteht aus einem in NSKeyValueCoding.h als NSObject-Kategorie deklarierten informellen Protokoll, NSKeyValueCoding, und einem Satz von Konventionen fr die Benennung der Zugriffsmethoden von Eigenschaften.
NSKeyValueCoding definiert Methoden, mit denen Sie ber Schlssel

oder Schlsselpfade mit Objektdaten interagieren knnen. Schlssel bzw. Schlsselpfade sind Strings, fr die Schlssel/Wert-Beziehungen bestehen, die 1:1- oder 1:m-Verhltnisse modellieren knnen. 1:1 heit, das auf eine Schlsselanfrage ein Wert geliefert wird. 1:m bedeutet, dass zu einem Schlssel mehrere Werte geliefert werden, blicherweise in Form eines Sammlungsobjekts (z. B. eines NSArray).

t KVC-Konformita
Die NSObject-Standardimplementierung der KVC-Methoden bilden Stringschlssel auf die Namen der Zugriffmethoden und Instanzvariablen eines Objekts ab. Eine Klasse ist KVC-konform fr einen Schlssel schluessel, wenn eine der folgenden Bedingungen zutrifft: Das Objekt hat eine Eigenschaft namens schluessel bzw. einen schluessel-Getter und einen setSchluessel:-Setter (wenn die entsprechenden Daten auch verndert werden knnen sollen). Das Objekt hat eine Instanzvariable names schluessel bzw. _schluessel.

| 143

Wenn der Schlssel in einem 1:m-Verhltnis zu den Daten steht, muss die Eigenschaft/Instanzvariable einen Typ haben, der die Art der Sammlung modelliert (z. B. NSArray oder NSSet). Betrachten wir das am Beispiel der folgenden Klasse:
@interface RPGFigur { @private NSString * _name; } @property NSArray *waffen; @property NSArray *ruestung; @property int leben; - (id) initMitName: (NSString *) name; - (NSString *) name; @end

Auf die Eigenschaft leben kann ber den Schlssel @"leben" zugegriffen werden, auf das gekapselte private Feld _name ber @"name" und auf die Listen waffen und ruestung ber @"waffen bzw. @"ruestung". @"leben" und @"name" stellen 1:1-Beziehungen dar, @"waffen" und @"ruestung" 1:m-Beziehungen. Beachten Sie, dass KVC die Kapselung des Felds _name umgehen kann. Wenn es keine set-Methode gibt, greift KVC standardmig direkt auf das Hintergrundfeld zu und ignoriert dabei Sichtbarkeitsangaben. Dieses Verhalten knnen Sie ndern, indem Sie die NSKeyValueCodingKlassenmethode accessInstanceVariablesDirectly berschreiben und NO liefern lassen, um einen Schreibversuch fehlschlagen zu lassen.

sselbasierte Interaktion mit Schlu Objekten


NSKeyValueCoding deklariert verschiedene Methoden zur schlssel-

basierten Interaktion mit Objekten. Die elementarste dieser Methoden ist -valueForKey:. Sie liefert den Wert zu einem Schlssel, unabhngig davon ob, dieser ein 1:1- oder ein 1:m-Verhltnis begrndet. Die Methode erwartet einen NSString, der den Schlssel angibt, und liefert ein eventuelles Ergebnis ber eine id-Referenz, zum Beispiel:
id name = [spieler valueForKey: @"name"];

144 | Kapitel 13: Key/ Value-Coding

Haben die Daten, die mit dem Schlssel assoziiert sind, einen skalaren Typ, werden sie in einen passenden NSValue-Wrapper verpackt. Vor der Weiterverarbeitung ist also gegebenenfalls ein geeigneter Cast erforderlich:
int verlust = MAX_LEBEN - (int)[spieler valueForKey: @"leben"];

1:m-Verhltnisse knnen z. B. ber eine Beziehung auf ein NSArray umgesetzt sein:
id waffe3 = [ [spieler valusForKey: @"waffen"] objectAtIndex: 2];

Das Gegenstck zu -valueForKey: ist -setValue:forKey: und setzt den Wert fr einen Schlssel:
[spieler setValue: @MAX_LEBEN forKey: @"leben"];

Schlgt das Abfragen oder Setzen ber einen Schlssel fehl, wird das Programm standardmig mit einer NSUndefinedKeyException abgebrochen. Dieses Verhalten knnen Sie ndern, indem Sie die Methoden -valueForUndefinedKey: (fr die Abfrage) und -setValue:forUndefinedKey: (fr das Setzen) berschreiben.

sselpfade Schlu
Schlsselpfade sind zusammengesetzte Schlssel, ber die unmittelbar auf tiefere Schichten des Objektgraphen zugegriffen werden kann. NSKeyValueCoding definiert eigene Methoden fr die schlsselpfadbasierte Objektinteraktion genauer: Zu vielen forKey-Methoden gibt es korrespondierende forKeyPath-Methoden. In einem Schlsselpfad werden die einzelnen Pfadelemente mit dem Punktoperator voneinander abgegrenzt. Beispielsweise knnte man mit dem Schlsselpfad @"name.length unmittelbar auf die Lnge des in _name gespeicherten Werts zugreifen, z. B. um ihn fr die Anzeige auf die passende Lnge zu bringen:
if ([spieler valueForKeyPath: @"name.length"] > 8) { return [NSString stringWithFormat: @"%@%@", [s.name substringWithRange:NSMakeRange(0, 8)], @""]; }

Schlsselpfade | 145

Schlsselpfade untersttzen auerdem von einem @ eingeleitete Schlsselpfadoperatoren. Es gibt Mengenoperationen wie @count oder @avg und einige Operatoren fr Operationen auf Objekten, Listen oder Mengen. Zum Beispiel liefert @"waffen.@count" die Anzahl an Waffen, @"waffen.@unionOfObjects.name" eine Liste mit den Namen der Waffen (wenn Waffen Waffe-Objekte mit einer name-Eigenschaft sind) und @"waffen.@avg.name.length" die durchschnittliche Lnge der Waffennamen. Gibt es kein linkes Pfadelement, bezieht sich die Operation auf das Objekt selbst, z. B. sind die beiden folgenden Ausdrcke quivalent:
[spieler.waffen valueForKeyPath: @"@count"] [spieler valueForKeyPath: @"waffen.@count"]

Wenn die Objekte, die in der Liste oder Menge enthalten sind, die Operatoraktion nicht untersttzen, wird eine Ausnahme ausgelst. Beispielsweise knnen Sie keinen Durchschnitt fr ein Array mit NSString-Elementen berechnen lassen

ssel Virtuelle Schlu


1:m-Verhltnisse mssen nicht notwendigerweise tatschlichen Eigenschaften oder Instanzvariablen entsprechen, sondern knnen emuliert werden. Dazu mssen Untersttzungsmethoden implementiert werden, die die Operationen der entsprechenden Sammlungsklasse ermglichen bei einer Liste (NSArray), die nur lesbar ist, z. B. countOfSchluessel sowie -objectInSchluesselAtIndex: und/oder -schluesselAtIndexes:. Man knnte den Schlssel @"inventar" beispielsweise folgendermaen ein (nur lesbares) 1:m-Verhltnis modellieren lassen, das die Inhalte der Listen waffen und ruestung zusammenfasst:
- (NSUInteger)countOfInventar { return [[self waffen] count] + [[self ruestung] count]; } - (id) objectInInventarAtIndex: (NSUInteger) index { if (index < [[self waffen] count]) { return [[self waffen] objectAtIndex: index]; } else { return [[self ruestung] objectAtIndex: (index - [[self waffen] count])];

146 | Kapitel 13: Key/ Value-Coding

} }

countOf liefert die Anzahl an Elementen in der Liste, hier die

Summe der Elemente der beiden Listen, die wir zum Inventar zusammenfassen. objectInAtIndex ist dafr verantwortlich, das Objekt an der angegebenen Position der zusammengesetzten Liste zu liefern. Rufen Sie diese Liste ber den Schlssel inventar ab, erhalten Sie ein Proxyobjekt, das alle Aktionen untersttzt, die Sie auf einem gewhnlichen NSArray vornehmen knnen:
id inventar = [spieler valueForKey: @"inventar"]; NSUInteger index = // Irgendwo einen Index beschaffen id gegenstand; if (index < [inventar count]) { gegenstand = [inventar objectAt: index]; }

Soll die Liste, die hinter dem 1:m-Verhltnis steht, schreibbar sein, mssen weitere Methoden ergnzt werden (zumindest -insert und/oder -insertSchluessel:at Object:inSchluesselAtIndex: Indexes: sowie -removeObjectFromSchluesselAtIndex: und/oder -removeSchluesselAtIndexes:). Fr die Implementierung anderer Sammlungsarten bentigen Sie andere Methoden. Mehr Informationen finden Sie in Apples Key-Value Coding Programming Guide.

KVC-Validierung
Die KVC-Infrastruktur vereinheitlicht die Validierung neuer Werte, und die KVC-Konformitt fordert eigentlich, dass die Validierung von Setter-Parametern nicht im Setter erfolgen, sondern in eigenen -validateSchluessel:error:-Nachrichten implementiert werden soll. Diese Methoden mssen als Parameter den neuen Wert in Form der Adresse einer id-Referenz sowie die Adresse einer NSErrorReferenz akzeptieren. Sie liefern einen BOOL-Wert, der anzeigt, ob der neue Wert ein gltiger Wert fr den entsprechenden Schlssel ist. Stellt er keinen gltigen Wert dar, wird zustzlich der error-Parameter auf ein NSError-Objekt gesetzt. Eine Validierungsmethode fr den @"leben"-Schlssel auf der Klasse Spieler knnte folgende Gestalt haben:

KVC-Validierung | 147

- (BOOL)validateLeben:(id *)object error:(NSError **)error { if ([*object isKindOfClass: [NSNumber class]] && [*object compare: @0] > NSOrderedAscending && [*object compare: @MAX_LEBEN]] < NSOrderedDescending) { return YES; } else { *error = [NSError errorWithDomain: FEHLER_DOMAIN code: FEHLER_CODE userInfo: @{ NSLocalizedDescriptionKey: @"Falscher Wert" }]; return NO; } }

Die Methode prft, ob der Wert ein NSNumber-Objekt ist und ob er eine Zahl im gltigen Bereich reprsentiert (grer als 0 und kleiner als der irgendwo definierte Maximalwert). Ist das der Fall, liefert sie YES, andernfalls setzt sie ein NSError-Objekt und liefert NO. Die Validierungsmethoden knnen direkt aufgerufen werden oder ber die NSKeyValueCoding-Methoden validateValue:forKey:error: bzw. validateValue:forKeyPath:error:.

sselpfadoperatoren Schlu
Tabelle 13-1 bietet eine Aufstellung der in Schlsselpfaden untersttzten Operatoren:
Tabelle 13-1: Schlsselpfadoperatoren Operator
@avg @count @max @min @sum @unionOfObjects @distinctUnionOfObjects

Beschreibung Durchschnitt der Elementwerte. Anzahl Elemente. tes Element. Gro Kleinstes Element. Summe der Elemente. Vereinigungsmenge der durch den Pfad angegebenen Elemente des Objektgraphen. Wie @unionOfObjects, die Vereinigungs lt aber keine Duplikate. menge entha

148 | Kapitel 13: Key/ Value-Coding

Tabelle 13-1: Schlsselpfadoperatoren (Fortsetzung)


Operator
@unionOfArrays @distinctUnionOfArrays @unionOfSets @distinctUnionOfSets

Beschreibung Vereinigungsmenge der Elemente der Quellarrays. Wie @unionOfArrays, aber die Vereini lt keine Duplikate. gungsmenge entha Vereinigungsmenge der Elemente der Quellmengen. Wie @unionOfSets, aber die Vereinigungslt keine Duplikate. menge entha

Schlsselpfadoperatoren | 149

KAPITEL 14

Objektarchivierung

Als Archivierung wird der Vorgang bezeichnet, mit dem das komplexe Geflecht eines Objektgraphen so gesichert wird, dass er zu einer anderen Zeit und/oder an einem anderen Ort exakt wiederhergestellt werden kann. Exakt wiederhergestellt heit dabei, dass die Identitt der einzelnen Objekte und ihre Beziehungen untereinander bei der Dearchivierung bewahrt bleiben. Objective-C deklariert mit dem in NSObject.h deklarierten Protokoll NSCoding eine einheitliche Archivierungsinfrastruktur, die Sie einsetzen knnen, wenn Sie Objektdaten speichern mssen, sich aber keine Gedanken ber ein Speicherungsformat machen wollen. Objective-C-Archive knnen Objektive-C-Objekte, skalare C-Typen und Structs festhalten. Typen, deren Implementierung plattformabhngig ist, knnen nicht gespeichert werden. Klassen, die die Archivierung untersttzen wollen, mssen dieses Protokoll bernehmen. Viele Klassen der Cocoa-APIs untersttzen die Archivierung, unter anderem auch die wichtigen Collection-Klassen.
Cocoa unterscheidet die Archivierung in ein transparentes Format, aus dem ein Objektgraph exakt rekonstruiert werden kann, von der Serialisierung, bei der ein Objektgraph in ein wohldefiniertes externes Format wie JSON oder Property-Listen berfhrt wird. Eine Serialisierung kann in der Regel nur mit einfacheren Objektsammlungen, Listen oder Dicts beispielsweise, umgehen, und der Graph wird bei der Deseria-

| 151

lisierung nicht exakt wiederhergestellt. Enthielte eine Liste drei Mal dasselbe Objekt, wrden bei der Deserialisierung z. B. drei verschiedene Kopien des Objekts angelegt. Da die Serialisierung keine NSObject-Einrichtung ist, sondern von API-Klassen wie NSJSONSeriali zation oder NSPropertyListSerialization gestellt wird, werden wir uns mit dieser Technik hier nicht befassen.
NSCoding deklariert zwei Methoden: -encodeWithCoder: schreibt die Daten fr ein Objekt in ein Archiv, und -initWithCoder: initialisiert

ein Objekt aus den Daten in einem Archiv, dearchiviert also die Daten. Wenn Ihre Klasse die Archivierungsinfrastruktur untersttzen will, muss sie die Konformitt zu NSCoding deklarieren und diese beiden Methoden implementieren. Sie kommen zum Einsatz, wenn ein Objekt archiviert oder aus einem Archiv wiederhergestellt werden soll.
- (void)encodeWithCoder:(NSCoder *)encoder

Kodiert den Empfnger mit dem angegebenen Coder-Objekt.


- (id)initWithCoder:(NSCoder *)decoder

Liefert ein Objekt, das aus dem als Argument angegebenen Coder-Objekt initialisiert wird. Beide Methoden erwarten ein Coder-Objekt, ber das die Methode mit dem fr sie transparenten Hintergrundspeicher interagiert. Ein Coder-Objekt ist eine Instanz einer Unterklasse der abstrakten Foundation-Klasse NSCoder, die die Schnittstelle fr die Archivierung und Wiederherstellung der verschiedenen Datenelemente definiert, aus denen ein Objekt bestehen kann. Sie knnen eigene Coder definieren oder einen der von Cocoa gestellten Coder nutzen, die beide aus zwei Klassen bestehen einer Archivierer-Klasse und einer Dearchivierer-Klasse, die jeweils eine Seite der NSCoder-Schnittstelle implementieren. Der Coder definiert, welche Gestalt die Archivierung hat. Die beiden zentralen Objective-C-Coder sind die Archivierer-Dearchivierer-Kombinationen NSArchiver/NSUnarachiver und NSKeyed

152 |

Archiver/NSKeyedUnarchiver. Die erste definiert sequenzielle Ar-

chive, die zweite schlsselbasierte Archive.

sselbasierte Sequenzielle und schlu Archive


Bei einem sequenziellen Archiv werden die Daten nacheinander in das Archiv geschrieben und mssen bei der Wiederherstellung genau in dieser Reihenfolge aus dem Archiv gelesen werden. Das heit auch, dass die in einem sequenziellen Archiv enthaltenen Daten immer vollstndig gelesen werden mssen (oder zumindest bis zu dem Punkt, an dem die Daten stehen, die im aktuellen Kontext relevant sind). Bei einem schlsselbasierten Archiv hingegen werden die Daten mit einem Schlssel versehen, ber den sie bei der Wiederherstellung in beliebiger Reihenfolge und in beliebigem Umfang gelesen werden knnen. In der Regel werden Sie schlsselbasierte Archive nutzen. Sequenzielle Archive sind beim Zugriff weniger flexibel und werden unter iOS auerdem nicht untersttzt.

Die NSCoding-Methoden implementieren


Wenn sich ein Objekt archivieren soll, nutzt es das bergebene Coder-Objekt, um die Werte seiner Eigenschaften zu sichern, damit diese aus dem Archiv wiederhergestellt werden knnen. Schauen wir uns das am Beispiel der Tier-Klasse mit den Stringeigenschaften name und art sowie der int-Eigenschaft alter an, die wir in diesem Buch gelegentlich schon zu Beispielzwecken eingesetzt haben:
@interface Tier : NSObject <NSCoding> @property NSString * name; @property NSString * art; @property int alter; @end

Die NSCoding-Methoden implementieren | 153

sselbasierte Archivierung Schlu


Eine NSCoding-Implementierung fr diese Klasse knnte dann folgende Gestalt haben:
- (void)encodeWithCoder:(NSCoder *)encoder { [encoder encodeObject: [self name] forKey: @"nameEigenschaft"]; [encoder encodeObject: [self art] forKey: @"artEigenschaft"]; [encoder encodeInt: [self alter] forKey: @"alterEigenschaft"]; } - (id)initWithCoder:(NSCoder *)decoder { self.name = [decoder decodeObjectForKey: @"nameEigenschaft"]; self.art = [decoder decodeObjectForKey: @"artEigenschaft"]; self.alter = [decoder decodeIntForKey: @"alterEigenschaft"]; }

Die Tabelle 14-1 bietet einen kurzen berblick ber die Methoden, die Sie zur Archivierung bzw. Dearchivierung bestimmter Objektdaten nutzen knnen:
Tabelle 14-1: Archivierungsmethoden Datentyp Objekte BOOL double float int, int32_t, int64_t NSKeyedArchiver -encodeObject:forKey:, -encodeConditionalObject:forKey: -encodeBool:forKey: -encodeDouble:forKey: -encodeFloat:forKey: -encodeInt:forKey:, -encodeInt32:forKey:, -encodeInt64:forKey: -encodeBytes:length:forKey: NSKeyedUnarchiver -decodeObjectForKey: -decodeBoolForKey: -decodeDoubleForKey: -decodeDoubleForKey: -decodeIntForKey:, -decodeInt32ForKey:, -decodeInt64ForKey: -decodeBytesForKey:returned Length:

rohe Bytes

Wenn die Daten fr einen Schlssel einen anderen als den von der Methode erwarteten Datentyp haben, werden sie, wenn mglich, d. h. wenn der Datentyp kompatibel ist, in den von der Methode verwendeten Typen umgewandelt. Dabei knnen Informationen verloren gehen.

154 | Kapitel 14: Objektarchivierung

Wenn Sie komplexere Objective-C-Typen, z. B. Structs, archivieren mssen, ist es in der Regel empfehlenswert, die Elemente separat zu archivieren, da nur so sichergestellt werden kann, dass alle Elemente korrekt archiviert werden.

Sequenzielle Archivierung
Beachten Sie, dass der Code oben davon ausging, dass als Coder NSKeyedArchiver- und NSKeyedUnarchiver-Objekte bergeben werden. Ein Aufruf einer der Methoden mit einem NSArchiver- respektive NSUnarchiver-Objekt wrde zu einer Ausnahme fhren, weil von diesen Klassen die forKey-Varianten der NSCoder-Methoden nicht untersttzt werden. Eine reine NSArchiver/NSUnarchiver-basierte NSCoding-Untersttzung htte folgende Gestalt:
- (void)encodeWithCoder:(NSCoder *)encoder { [encoder encodeObject: [self name]]; [encoder encodeObject: [self art]]; [encoder encodeValueOfObjCType: @encode(int) at: &self->_alter]; } - (id)initWithCoder:(NSCoder *)decoder { self.name = [decoder decodeObject]; self.art = [decoder decodeObject]; [self decodeValueOfObjCType: @encode(int) at: &self->_alter]; }

In neuem Code sollten Sie grundstzlich schlsselbasierte Archive nutzen. Aber es kann passieren, dass Sie gelegentlich noch auf sequenzielle Archive stoen, da sie nicht offiziell veraltet sind.

fen Den Coder pru


Wenn Sie nicht wissen, wie der Code Ihrer Klasse genutzt werden wird, knnen Sie die NSCoder-Methode -allowsKeyedCoding verwenden, um den Archivierungsprozess anzupassen:

Die NSCoding-Methoden implementieren | 155

if ([coder allowsKeyedCoding]) { // Schlu sselbasierter Code } else { // Sequenzieller Code }

Coder-Objekte
Zur Archivierung/Dearchivierung selbst nutzen Sie ein entsprechendes Coder-Objekt, das mit einem Ziel bzw. einer Quelle fr die Objektdaten verbunden ist. Die Coder-Klassen bieten dazu die Klassennachrichten +archivedDataWithRootObject: und +archive RootObject:toFile: bzw. +unarchiveObjectWithData: und +unar chiveObjectWithFile:, mit denen auf die Schnelle ein Objektgraph direkt in einer Datei oder einem NSData-Objekt gespeichert werden kann. Wenn Sie mehrere Objekte oder Objektgraphen in einem Archiv speichern wollen, knnen Sie mit der Instanznachricht -initForWritingWithMutableData: ein Objekt erstellen, ber das Sie in ein NSMutableData-Objekt als Datenspeicher schreiben. Mit -initForReadingWithData: erstellen Sie ein Objekt, ber das Sie Objektgraphen aus einem NSData-Speicher lesen knnen. Unabhngig davon, fr welche der Optionen Sie sich entscheiden, sorgt der Coder dafr, dass auf den Objekten des Graphen die Archivierungs- bzw. Dearchivierungsmethode aufgerufen wird. Zunchst die NSKeyedArchiver-Methoden:
+ (NSData *)archivedDataWithRootObject:(id)rootObject

Erstellt ein Archiv fr den Objektgraphen, dessen Wurzelobjekt als Parameter bergeben wird, und liefert es in Form eines NSData-Objekts.
+ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path

Erstellt ein Archiv fr den Objektgraphen, dessen Wurzelobjekt als Parameter bergeben wird, und speichert es unter dem Pfad, der in Stringform als zweiter Parameter bergeben wird. Liefert einen Booleschen Wert, der anzeigt, ob die Operation erfolgreich war oder nicht.

156 | Kapitel 14: Objektarchivierung

- (id)initForWritingWithMutableData:(NSMutableData *)data Liefert einen initialisierten Coder, der in das bergebene NSData-

Objekt schreibt.
(void) finishEncoding

Muss aufgerufen werden, wenn das letzte Objekt archiviert wurde. Sorgt dafr, dass der genutzte Datenspeicher geschlossen wird. Nachdem die Archivierung damit beendet wurde, knnen in einen Speicher keine weiteren Daten geschrieben werden. Die korrespondierenden NSKeyedUnarchiver-Methoden sind:
+ (id)unarchiveObjectWithData:(NSData *)data Ldt einen Objektgraphen aus einem NSData-Archivspeicher. + (id)unarchiveObjectWithFile:(NSString *)path

Ldt einen Objektgraphen aus einer Datei, die ber einen Pfadstring angegeben wird.
- (id)initForReadingWithData:(NSData *)data

Initialisiert einen Coder zum Lesen mit einem NSData-Objekt als Speicher.
- (void)finishDecoding

Beendet das Lesen auf einem Coder.

Einige Beispiele
Wenn Sie nur ein Objekt bzw. nur einen Objektgraphen archivieren wollen, knnen Sie einfach eine der Klassennachrichten nutzen:
id tier = [Tier tierMitName: @"Bertram" art: @"Elefant" alter: 13]; id data = [NSKeyedArchiver archivedDataWithRootObject: tier];

Zur Objektwiederherstellung nutzen Sie dann das entsprechende NSKeyedUnarchiver-Gegenstck:


kopie = [NSKeyedUnarchiver unarchiveObjectWithData: data];

Wenn Sie mehrere Objekte bzw. mehrere Objektgraphen archivieren wollen, erstellen Sie ein Coder-Objekt, das ein geeignetes Archivierungsziel nutzt (hier ein NSData-Objekt):

Coder-Objekte | 157

id tier = [Tier tierMitName: @"Bertram" art: @"Elefant" alter: 13]; NSMutableData *data = [NSMutableData new]; NSKeyedArchiver *coder = [[NSKeyedArchiver alloc] initForWritingWithMutableData: data]; [coder encodeObject: tier forKey: @"tier"]; [coder finishEncoding];

Wenn Sie keine weiteren Objektgraphen archivieren mssen, schlieen Sie den Archivierungsvorgang ab, indem Sie dem Coder die finishEncoding-Nachricht senden! Aus dem gleichen Datenspeicher knnen Sie die Objekte dann folgendermaen wiederherstellen:
NSCoder *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData: data]; id wiederhergestellt = [decoder decodeObjectForKey: @"tier"];

158 | Kapitel 14: Objektarchivierung

KAPITEL 15

Blocks

Blocks sind Objekte, die ausfhrbare Codeeinheiten reprsentieren. Sie knnen in Ihrem Code wie Objekte herumgereicht und gespeichert und wie Funktionen aufgerufen werden. Wie Funktionen haben sie Argumentlisten und einen Rckgabetyp, der entweder explizit angegeben oder vom Compiler aus dem Definitionskontext geschlossen wird. Im Unterschied zu Funktionen knnen sie mit dem Zustand des Kontexts interagieren, in dem sie definiert wurden. Optional knnen sie ihn auch ndern. Der Compiler sorgt dafr, dass der Zustand des Kontexts, in dem ein Block definiert wurde, erhalten bleibt, solange der Block besteht.

Blocks definieren
Die Deklaration einer Blockreferenz hat die gleiche Struktur wie die Deklaration eines C-Funktionszeigers:
Ru ckgabetyp (^Variablenname) (Argumentliste)

Die Definition eines Blockliterals hat folgende Gestalt:


^Ru ckgabetyp(Argumentliste) {/* Anweisungen */}

Der Rckgabetyp ist optional und muss nur angegeben werden, wenn der Compiler den Rckgabetyp aus der Blockdeklaration nicht korrekt schlieen kann. Eine einfache Blockdefinition, die einen Block definiert, der eine Objektbeschreibung auf der Konsole protokolliert, knnte z. B. so aussehen:
void (^log)(id) = ^(id o){NSLog(@"%@", o);};

| 159

Diese Anweisung deklariert eine Blockreferenz namens log, die Blocks referenziert, die keinen Wert zurckliefern und ein Argument des Typs id erwarten. Ihr wird ein Block zugewiesen, der diese Bedingungen erfllt. Der Blockcode muss in geschweiften Klammern stehen, auch wenn er nur eine einzige Anweisung enthlt. Wenn ein Block keine Parameter erwartet, kann bei der Definition des Blockliterals die Klammer weggelassen werden, in der die Parameterliste ansonsten stnde:
void (^winter)() = ^{NSLog("Der Winter naht");};

Wenn Sie mehrere Blocks gleichen Typs bentigen, knnen Sie einen Blocktyp definieren. Auch diese Definition entspricht der von Funktionszeigertypen und hat formell folgende Gestalt:
typedef Ru ckgabetyp (^BlockTypName)(Argumentliste)

Konkret wrde ein Typ, der einen Block beschreibt, der keinen Rckgabewert hat und ein Argument des Typs id erwartet, folgendermaen definiert:
typedef void (^VoidIdBlock)(id);

Mit dieser Typdefinition knnen Sie die Blockdeklaration oben folgendermaen umschreiben:
VoidIdBlock log = ^(id o){NSLog(@"%@", o);};

Gelegentlich mssen Sie den Rckgabetyp des Blocks explizit angeben, weil der Compiler nicht den gewnschten Typ erschlieen kann. Zum Beispiel meldet der Compiler bei folgender Blockdefinition einen Fehler:
BOOL (^kleiner)(int, int) = ^(int a, int b) {return a < b;};

Der Compiler schliet aus der return-Anweisung, dass dieser Block einen int-Wert liefert, und beschwert sich, dass ein BOOL(^)(int,int) (der Variablentyp) nicht mit einem int(^)(int,int) (dem geschlossenen Blocktyp) kompatibel ist. Dann mssen Sie ihn untersttzen, indem Sie den erwarteten Rckgabetyp folgendermaen angeben:
BOOL (^kleiner)(int, int) = ^BOOL(int a, int b) {return a < b;};

160 | Kapitel 15: Blocks

(In diesem Fall htte man auch einfach den Typ des Werts, zu dem die Vergleichsoperation ausgewertet wird, auf BOOL casten knnen. Dann htte der Compiler den Rckgabewert natrlich korrekt erkennen knnen.) Ein definierter Block kann wie eine Funktion aufgerufen werden:
log(@"Hallo"); if (kleiner(1, 12)) winter();

Blocks als Objekte


Ein Block ist ein Objekt und kann als ein solches behandelt werden. Er kann z. B. einer Objektvariablen zugewiesen, in einem Array oder einem Dictionary gespeichert und sogar als Dictionary-Schlssel verwendet werden:
id ausgabe = ^(NSString *text){NSLog(@"%@", text)}

Wenn Sie einen Block, der als Objekt gespeichert ist, ausfhren wollen, mssen Sie ihn auf den Blocktyp casten. Der Cast hat diese allgemeine Gestalt:
(Ru ckgabetyp (^)(Argumentliste))

Folgendermaen casten Sie die oben definierte id-Referenz auf einen Block, der nichts zurckliefert und einen Wert des Typs NSString * erwartet, und rufen ihn mit einem String als Parameter auf:
((void (^)(NSString *))ausgabe)(@"Rot oder Blau?");

Etwas bersichtlicher ist der Cast, wenn Sie zuvor einen entsprechenden Blocktyp deklariert haben:
((VoidNSStringBlock)ausgabe)(@"Wunderland");

Zugriff auf den Kontext


Blocks knnen auf alle Codeeinheiten zugreifen, die in dem lexikalischen Geltungsbereich sichtbar sind, in dem sie definiert werden. Ein in einer Klasse definierter Block kann auf die Daten und

Zugriff auf den Kontext | 161

Operationen der aktuellen Instanz zugreifen. Blocks knnen darber hinaus auf alle Variablen, Parameter und Konstanten zugreifen, die im Definitionskontext gltig sind, zum Beispiel
int a = 5; int (^aPlusB)() = ^{return [self b] + a;}; a = aPlusB();

Der Code definiert einen Block, der den Wert einer Instanzeigenschaft b und den Wert der lokalen Variablen a addiert und zurckliefert.

nde Zugriff auf Objektzusta


Schauen wir uns zunchst den Zugriff auf Objektzustnde anhand eines vollstndigeren Beispiels an. Folgende Schnittstelle deklariert das Fundament einer Klasse, die einen Cursor darstellt. Anstelle von Nachrichten nutzt sie jedoch Blocks, um die Cursorposition zu manipulieren. Dazu wird ein Blocktyp definiert, der als Rckgabetyp fr die Nachrichten dient, ber die der Nutzer der Klasse die Blocks abrufen kann:
typedef void (^VoidVoidBlock) (); @interface Cursor : NSObject { int pos; } - (VoidVoidBlock)vor; - (VoidVoidBlock)zurueck; - (VoidVoidBlock)position; @end

Die Implementierung knnte folgende Gestalt haben:


@implementation Cursor - (VoidVoidBlock)vor { return ^{pos++;}; }; - (VoidVoidBlock)zurueck { return ^{pos--;}; } - (VoidVoidBlock)position { return ^{NSLog(@"Position %d", pos);}; } @end

162 | Kapitel 15: Blocks

Die drei Instanznachrichten liefern jeweils einen Block, der unmittelbar auf die Ivar pos zugreift.
Das ist weniger unsinnig, als es scheint. Zum Beispiel knnte man die Dekrementierungsund Inkrementierungsblocks unmittelbar als Callbacks fr eine Aktion setzen.

Die so definierte Klasse und ihre Blocks knnte man folgendermaen nutzen. Der Code definiert drei Blockreferenzen mit dem in der Cursor-Schnittstelle definierten Blocktyp, denen die Blocks zugewiesen werden, die die Nachrichten zurckliefern. ber diese werden die Blocks dann ausgefhrt:
Cursor *cursor = [Cursor new]; VoidVoidBlock vor = [cursor vor]; VoidVoidBlock zurueck = [cursor zurueck]; VoidVoidBlock position = [cursor position]; vor(); vor(); zurueck(); position();

Der Code verhlt sich genau so, wie Sie es wahrscheinlich erwarten wrden. Nachdem er ausgefhrt wurde, befindet sich der Cursor an Position 1. So, wie der Implementierungscode formuliert ist, knnte es den Anschein erwecken, als wren die Blocks selbst Member der Klasse. Deswegen scheint es selbstverstndlich, dass der Code funktioniert. Eigentlich sind die Blocks aber unabhngige Objekte, die von den Instanznachrichten zurckgeliefert werden, selbst aber auf den Zustand der Instanz zugreifen. Blocks sind nicht an die Instanz, sondern an den lexikalischen Kontext gebunden, in dem sie definiert werden. Auf die Instanzdaten und -operationen knnen sie zugreifen, weil self in den Nachrichten definiert ist, in denen sie definiert werden. Das pos-Feld des cursorObjekts kann in den Blocks also genutzt werden, weil self-Teil des Kontexts ist, in dem die Blockobjekte erstellt werden.

Zugriff auf den Kontext | 163

Da die Blockoperation nur gewhrleistet werden kann, wenn das Objekt, dessen Zustand manipuliert wird, funktionsfhig ist, muss ein Block, der ein Objekt referenziert, eine starke Referenz auf dieses Objekt darstellen. Die Blocks halten die Objekte, die sie referenzieren, also am Leben. Sie werden erst freigegeben, wenn die Blocks freigegeben wurden, die sie referenzieren. Der Code funktioniert also, weil die Blocks self einfangen und den pos-Zugriff auf self auflsen. Wrden sie einfach den Wert von pos einfangen, wrde der Code nicht funktionieren.

re Referenzen auf self Zirkula


Sie mssen darauf achten, dass Sie Ihre Blocks so gestalten, dass es nicht zu zirkulren Referenzen kommt. Die folgende Schnittstellendeklaration birgt dieses Risiko:
typedef void (^VoidVoidBlock) (); @interface Cursor : NSObject { int pos; @public VoidVoidBlock vor; VoidVoidBlock zurueck; VoidVoidBlock position; } @end

Die Klasseninstanzen halten starke Referenzen auf die drei Blockobjekte. Wrden diese nun ihrerseits starke Referenzen auf die Instanz halten, weil sie self referenzieren, wrde das zu einer zirkulren Referenz fhren. Das wre bei der folgenden naiven Implementierung der Fall:
@implementation Cursor - (id)init { pos = 0; vor = ^{pos++;}; zurueck = ^{pos--;}; position = ^{NSLog(@"Position %d", pos);}; } @end

Die Blocks referenzieren die Ivar pos (was ja nur eine Abkrzung fr self->pos ist). Die Blocks halten also starke Referenzen auf self,

164 | Kapitel 15: Blocks

self hlt starke Referenzen auf die Blocks und schon haben wir

zirkulre Referenzen, die verhindern, dass die beteiligten Elemente freigegeben werden. Glcklicherweise warnt Sie der Compiler, wenn er mgliche Referenzendlosschleifen erkennt. Zirkulre Referenzen brechen Sie blicherweise, indem Sie eine lokale schwache Referenz auf self definieren und diese dann im Block einer starken Referenz zuweisen, auf der Sie dann im Block operieren, zum Beispiel (nur fr vor):
@implementation Cursor -(id) init { pos = 0; __weak Cursor *weakSelf = self; vor = ^{ Cursor *c = weakSelf; c->pos++; }; // } @end

Versuchen Sie nicht, einen Alias fr self zu nutzen, der eine starke Referenz darstellt. Der Compiler beschwert sich dann zwar nicht mehr, weil er die Gefahr nicht mehr erkennt, aber die Referenzendlosschleife haben Sie damit nicht behoben.

Lokale Variablen
Ein Block kann zwar auf die Variablen, Parameter und Konstanten zugreifen, die in seinem Definitionskontext gltig sind, aber er kann sie nicht einfach so manipulieren. Der Compiler meldet einen Fehler, wenn Sie versuchen, den Wert einer solchen Stack-Variablen zu modifizieren. Er sagt Ihnen, dass die Variable nicht zuweisbar ist, wenn Sie z. B. so versuchen, eine lokale Variable in einem Block zu manipulieren:
int a = 0; id plus = ^(int b){a+=b;}; // FEHLER

Das liegt daran, dass Blocks blicherweise nur den Wert der Variablen bei der Definition des Blocks einfangen. Zum Beispiel wirkt

Lokale Variablen | 165

sich eine sptere nderung der lokalen Variablen auch nicht auf den Wert aus, den der Block bei der Erstellung eingefangen hat:
int a = 0; void (^out)() = ^{NSLog(@"Wert von a: %d", a);}; a++; ((void (^)())out)(); // Gibt 0 aus, nicht 1!

Wenn der Block auf eine Objektreferenz zugreift, knnen Sie im Block natrlich problemlos das Objekt ber diese Referenz manipulieren. Block und Kontext arbeiten mit dem gleichen Wert, der Adresse des Objekts. Die Manipulation der lokalen Objektvariablen erfolgt also unter den gleichen Bedingungen wie die Manipulation von self in einer Klasseninstanz. Aber wenn Sie der lokalen Objektvariablen ein neues Objekt zuweisen, dann referenzieren Block und Objekt unterschiedliche Objekte, zum Beispiel:
id liste = [NSMutableArray new]; void (^addObject)(id) = ^(id o) { [liste addObject: o]; }; void (^print)() = ^{NSLog(@"Block %@", liste);}; addObject(@1); addObject(@2); NSLog(@"Lokal %@", liste); print(); liste = @"123"; print();

Dieser Code definiert ein vernderliches Array und zwei Blocks, die dieses Array referenzieren. Der eine fgt ihm Elemente hinzu, der andere gibt es aus. Solange die liste-Referenz nicht verndert wird, sind alle nderungen, die an dem Array-Objekt vorgenommen werden, in den Blocks und im lokalen Kontext sichtbar. Aber wenn der liste ein anderes Objekt zugewiesen wird, operieren die beiden Blocks weiterhin auf der Referenz, die sie bei der Erstellung als Wert erhielten. Die Ausgabe dieses Codes she (umformatiert) so aus:
Lokal (1, 2) Block (1, 2) Block (1, 2)

liste referenziert ein anderes Objekt, aber der print-Block greift

weiter auf das ursprngliche Array-Objekt zu. Es knnte auch


166 | Kapitel 15: Blocks

weiterhin ber add manipuliert werden, da die beiden Blocks dafr sorgen, dass das ursprngliche Objekt nicht freigegeben wird.

Blockvariablen
Wenn der Block nicht den Wert, sondern die Variable selbst einfangen soll, muss die Variable explizit mit dem Variablenqualifizierer _ _block als Blockvariable markiert werden:
_ _block int a = 0; void (^out)() = ^{NSLog(@"Wert von a: %d", a);}; a++; ((void (^)())out)(); // Gibt 1 aus

Mit _ _block als Blockvariablen markierte Variablen knnen in Blocks dann auch verndert werden:
_ _block int a = 0; id plus = ^(int b){a+=b;}; //Kein Fehler mehr

Diese Markierung als Blockvariable ist erforderlich, weil der Compiler dafr sorgen muss, dass die Variable dem Block per Referenz bergeben wird, wenn sich die nderungen an der Variablen im Block auf den Wert der Variablen im Definitionskontext auswirken soll und umgekehrt. Blocks verhalten sich in dieser Hinsicht dann also vollkommen anders als Funktionen oder Nachrichten. Diese erhalten stets eine Kopie des Werts der Variablen. nderungen des Werts in der Funktion oder Nachricht wirken sich nie auf den ursprnglichen Wert aus. Schauen wir uns das anhand einer Abwandlung unseres Cursor-Beispiels an. Folgender Code definiert eine Blockvariable pos, die von zwei Blocks, vor und zurueck, manipuliert wird. Die nderungen am Wert der Variablen, die in den Blocks vorgenommen werden, wirken sich auf den Wert der ursprnglichen Variablen aus. nderungen an der Variablen werden von den Blocks gesehen:
_ _block int pos = 0; VoidVoidBlock vor = ^{pos++;}; VoidVoidBlock zurueck =^{pos--;}; vor(); // pos = 1 vor(); // pos = 2 zurueck(); // pos = 1

Lokale Variablen | 167

pos = 5; zurueck() // pos = 4

Blocks, die auf diese Weise lokale Zustnde einfangen, knnen als Closures verwendet werden, wenn sie aus dem lokalen Geltungsbereich zurckgeliefert oder als Argument an eine andere Methode bergeben werden. Der eingefangene Zustand bleibt so lange erhalten, bis der Block freigegeben wird. Blockvariablen haben noch eine weitere interessante Eigenschaft: Sie knnen den Speicherort wechseln. Die Blockvariable pos ist zunchst eine gewhnliche Stack-Variable. Aber nachdem das erste Blockliteral definiert und einer starken Referenz zugewiesen wurde, wird sie in eine Heap-Variable umgewandelt. Das knnen Sie sehen, wenn Sie sich die jeweils aktuelle Speicheradresse von pos ansehen:
_ _block int pos = 0; NSLog(@"%p", &pos); // Stack, z. B.: 0x7fff5fbff878 VoidVoidBlock vorOp = ^{pos++;}; NSLog(@"%p", &pos); // Heap, z. B.: 0x1003013b8

Das erfolgt, wenn ein Block, der auf eine Blockvariable zugreift, auf den Heap kopiert wird, was unter ARC automatisch passiert, wenn der Block einer Variablen zugewiesen wird, die eine starke Referenz darstellt.

Globale und kontextgebundene Blocks


Blockdefinitionen sind zwar Objektliterale, weisen aber einen entscheidenden Unterschied zu anderen Objektliteralen wie Stringliteralen auf. Stringliterale sind Kompilierungskonstanten, die einmal ausgewertet und zur Laufzeit nur noch referenziert werden. Da Blockliterale mit ihrem Definitionskontext interagieren knnen, der bei jeder Ausfhrung anders sein kann, mssen Blockliterale bei jeder Ausfhrung neu ausgewertet werden. Bei jeder Ausfhrung des Codes muss also ein neues Blockobjekt erstellt werden. Weil das uerst ressourcenaufwendig wre, gibt es zwei Arten von Blocks, globale und kontextgebundene. Globale Blocks sind Blocks ohne Kontext. Sie werden entweder unmittelbar global auf oberster Programmebene definiert oder vom Compiler als Optimierungsmanahme erstellt. Globale Blocks wer168 | Kapitel 15: Blocks

den erzeugt, wenn der Compiler feststellt, dass ein Block nicht mit dem lokalen Kontext interagiert, in dem er definiert wird. Sie verhalten sich wie die Objekte, die auf Basis anderer Literalformen erstellt werden. Es gibt nur eine Kopie eines globalen Blocks, die auf dem Heap gespeichert ist. Ein Beispiel:
NSMutableArray *liste = [NSMutableArray new]; for (int i = 0; i < 3; i++) { [liste addObject:^{NSLog(@"Globaler Block");}]; }

In der Schleife wird den ersten drei Elementen des Arrays ein Blockobjekt hinzugefgt. Da sich der Kontext bei jedem Schleifendurchlauf ndert, msste eigentlich bei jedem Durchlauf das in diesem Kontext definierte Blockliteral neu ausgewertet werden. Aber der Compiler erkennt, dass der Block nicht auf den Kontext zugreift, und erstellt deswegen ein globales Blockobjekt. Die drei Elemente im Array sind also drei Referenzen auf dasselbe Objekt. Das zeigt die folgende Ausgabe, die einfach die Beschreibung der drei Blockobjekte NSLog(@"%@", block); enthlt:
<__NSGlobalBlock__: 0x100005b50> <__NSGlobalBlock__: 0x100005b50> <__NSGlobalBlock__: 0x100005b50>

Wie Sie sehen, werden globale Blocks intern also durch __NSGlobal Block__-Objekte reprsentiert, die im gleichen Speicherbereich wie die anderen Objekte, das heit auf dem Heap, festgehalten werden. Kontextgebundene Blocks sind Blocks, die mit dem lexikalischen Kontext interagieren, in dem sie definiert werden. Wenn ein Block mit seinem Definitionskontext interagiert, muss das Blockliteral tatschlich bei jeder Ausfhrung des Codes neu ausgewertet werden. Wie bei gewhnlichen lokalen Werten gibt es bei jeder Ausfhrung einen neuen Stack-Wert. Wenn Ihr Code unter ARC luft, lsst sich das nur schwer demonstrieren. Blockobjekte werden zwar auf dem Stack erstellt, unter ARC bei der Zuweisung an eine Variable, die eine starke Bindung darstellt, oder beim Einfgen in ein Array usw. aber auf den Heap kopiert. Das sorgt dafr, dass Sie sich bei ARC eigentlich nie darber Gedanken machen mssen, wie die Stack-Objekte, die

Globale und kontextgebundene Blocks | 169

Blocks eigentlich darstellen, ber den Kontext hinaus erhalten bleiben, in dem sie definiert wurden. Betrachten Sie die folgende nur leicht abgewandelte Form des obigen Beispiels:
NSMutableArray *liste = [NSMutableArray new]; for (int i = 0; i < 3; i++) { [liste addObject:^{NSLog(@"%d. Kontextgebundener Block", i);}]; }

Das hier definierte Blockobjekt greift auf die Schleifenvariable i und damit auf den Kontext der Schleife zu. Deswegen wird bei jeder Ausfhrung ein neues kontextgebundenes Blockobjekt auf dem Stack erstellt, das dann bei der Einfgung in das Array auf den Heap kopiert wird. Wenn Sie sich die Beschreibung dieser Blockobjekte ausgeben lassen, sehen Sie an den abweichenden Speicheradressen, dass es sich um drei eigenstndige Objekte des Typs __NSMallocBlock__ handelt:
<__NSMallocBlock__: 0x10010f110> 0. Kontextgebundener Block <__NSMallocBlock__: 0x10010f140> 1. Kontextgebundener Block <__NSMallocBlock__: 0x10010f170> 2. Kontextgebundener Block

Unter der Blockbeschreibung sehen Sie, was bei der Ausfhrung des jeweiligen Blockobjekts ausgegeben wird. Die Blockobjekte fangen den jeweils aktuellen Wert der Schleifenvariablen i ein. Dass Blockobjekte eigentlich Stack-Objekte sind und welche Verpflichtungen das mit sich bringt, sieht man, wenn man den gleichen Code unter MRC ausfhrt. Die Ausgabe htte dann folgende Gestalt:
// Ausgabe bei Ausfu hrung unter MRC <__NSStackBlock__: 0x7fff5fbff300> 2. Kontextgebundener Block <__NSStackBlock__: 0x7fff5fbff300> 2. Kontextgebundener Block <__NSStackBlock__: 0x7fff5fbff300> 2. Kontextgebundener Block

170 | Kapitel 15: Blocks

Es werden drei __NSStackBlock__-Objekte erstellt, bei jedem Schleifendurchlauf ein neues. Aber da diese auf dem Stack erstellt und an der gleichen Speicheradresse abgelegt werden, berschreiben die spter erstellten die zuvor erstellten Blockobjekte. Die Zeiger im Array, die alle auf dieselbe Adresse zeigen, greifen dann nur noch auf die zuletzt erstellte Kopie zu. Deswegen wird immer derselbe Block ausgefhrt und der dort aktuelle Wert der Schleifenvariablen ausgegeben.

Blocks kopieren
Unter MRC mssen Sie sich selbst darum kmmern, dass Blocks auf den Heap kopiert werden, damit sie ber ihren Definitionskontext hinaus erhalten bleiben, zum Beispiel:
for (int i = 0; i < 4; i++) { [liste addObject:[ [^{NSLog(@"%d. Kontextgebundener Block", i);} copy] autorelease]]; }

Das gilt z. B. auch, wenn Blockobjekte aus einer Methode zurckgeliefert werden (was unter ARC ebenfalls kein Problem ist). Beispielsweise wrde Folgendes zu einem Laufzeitfehler fhren, wenn versucht wrde, den zurckgelieferten Block auszufhren:
-(id)zaehlerBlock { int a = 0; return ^(){return i++;}; }

Der Block muss mit copy auf den Heap kopiert werden, damit er ber den Definitionskontext erhalten bleibt:
-(id)zaehlerBlock { int a = 0; return [ [^(){return i++;} copy] autorelease]; }

Bei __NSGlobalBlock__- und __NSMallocBlock__-Objekten liefert -copy einfach self. Beachten Sie, dass -copy den Referenzzhler nicht erhht. Dazu mssen Sie die blichen MRC-Speicherverwaltungsnachrichten nutzen. Der Code oben liefert, dem Nachrichten-

Globale und kontextgebundene Blocks | 171

namen entsprechend, ein Blockobjekt, das automatisch freigegeben wird. Die Blocks-API definiert zwei Makros, die zum Kopieren und Freigeben von Blockobjekten eingesetzt werden knnen: Block_copy() und Block_release(). Diese knnen Sie anstelle der ObjectiveC-Speicherverwaltungsnachrichten nutzen. Wenn Sie diese Makros einsetzen, sollten Sie darauf achten, dass Sie beide Technologien bei der Behandlung eines Blocks nicht mischen. Unter ARC sollten Sie eigentlich nicht in eine Situation kommen, in der Sie einen Block manuell kopieren mssen.

172 | Kapitel 15: Blocks

KAPITEL 16

NSObject-Referenz

Die nachfolgende Referenz ist keine vollstndige Referenz zu NSOb ject. Sie fhrt die Nachrichten auf, die Sie am hufigsten bentigen werden. Die Eintrge enthalten jeweils die vollstndige Nachrichtensignatur und eine Kurzbeschreibung. Nachrichten, die die gleichen Funktionalitten stellen, werden in der Regel in einem einzigen Eintrag behandelt. Zu Anfang jedes Abschnitts ist der Aufgabenbereich vermerkt, fr den diese Nachricht gedacht ist. Zu vielen Nachrichten finden Sie in den vorangegangenen Abschnitten gleichen Namens weitere Erluterungen und Verwendungsbeispiele.
+ (BOOL)accessInstanceVariablesDirectly

Key/Value-Coding. Der Rckgabewert dieser Methode steuert, ob beim KVC-Zugriff auf eine Eigenschaft direkt auf eine Instanzvariable zugegriffen werden soll, wenn es keine Zugriffsmethode gibt. Die Standardimplementierung liefert YES, d. h., es wird direkt auf Instanzvariablen zugegriffen. Das bedeutet, dass die Kapselung von Instanzvariablen durch KVC umgangen werden kann. berschreiben Sie diese Methode und lassen Sie sie NO liefern, wenn Sie das verhindern wollen.
+ (id)alloc + (id)allocWithZone:(NSZone *)zone

Objektlebenszyklus. Liefert eine Referenz auf neu allozierte und vorinitialisierte Instanzen der Empfngerklasse. Die Verwendbarkeit dieser Instanz ist nicht garantiert, da alle Instanzvariablen nur mit den typgemen Vorgabewerten initialisiert werden. Vor der Verwendung mssen allozierte Instanzen zunchst
| 173

mit einer Methode der init-Familie initialisiert werden. Die +allocWithZone:-Nachricht ist ein historisches Relikt. Sie wird intern von alloc aufgerufen. Der zone-Parameter wird ignoriert.
- (id)autorelease

Speicherverwaltung. bergibt das Objekt an den zuletzt erstellten (bzw. den innersten) Auto-Release-Pool. Die Freigabe (die Reduzierung des Referenzzhlers) erfolgt, wenn der entsprechende Pool geleert wird. Unter ARC ist diese Nachricht nicht zulssig.
- (Class)class + (Class)class

Laufzeitinformation. Liefert das Class-Objekt fr die Klasse des Empfngers.


- (BOOL)conformsToProtocol:(Protocol *)aProtocol + (BOOL)conformsToProtocol:(Protocol *)aProtocol

Laufzeitinformation. Prft, ob der Empfnger dem als Argument bergebenen Protokoll entspricht. Liefert YES, wenn das der Fall ist, andernfalls NO. Die Klassenmethode prft nur, ob die Protokollentsprechung deklariert wird. Sie prft nicht, ob der Empfnger tatschlich auf die vom Protokoll deklarierten Nachrichten reagiert. Die Instanzmethode prft, ob die Instanz tatschlich auf die Nachrichten reagiert, die das Protokoll deklariert. Das von beiden Nachrichten als Argument erwartete Protocol-Objekt kann mithilfe der Direktive @protocol() angegeben werden.
- (id)copy

Objektlebenszyklus. Liefert eine Kopie des Objekts zurck, wenn die Klasse das NSCopying-Protokoll untersttzt. -copy ist eine Hilfsmethode, die einfach die NSCopying-Methode -copyWithZone: aufruft. Gibt es eine -copyWithZone:-Methode, wird ihr Rckgabewert zurckgeliefert, gibt es keine, wird die Programmausfhrung standardmig mit einer Ausnahme abgebrochen.
+ (id)copyWithZone:(NSZone *)zone

Objektlebenszyklus. Liefert einfach self zurck, keine Kopie! Das ist keine Implementierung des NSCopying-Protokolls, son174 | Kapitel 16: NSObject-Referenz

dern eine Hilfsmethode, die es ermglicht, Class-Objekte dort zu nutzen, wo NSCopying-kompatible Objekte bentigt werden. Diese Einrichtung wird von Framework-Elementen wie der Klasse NSDictionary genutzt. Benutzerdefinierte Klassen, die eine hnliche Einrichtung bieten wollen, mssen die Untersttzung dieser Methode explizit prfen, da ihre Untersttzung mit conformsToProtocol: nicht festgestellt werden kann. Das zoneArgument wird ignoriert.
- (void)dealloc

Objektlebenszyklus. Gibt den vom Empfnger eingenommenen Speicher frei. Darf unter ARC implementiert, aber nicht aufgerufen werden.
- (NSString *)description

Laufzeitinformation. Liefert einen String, der eine Beschreibung des Empfngers enthlt.
- (NSDictionary *)dictionaryWithValuesForKeys:(NSArray *)keys Key/Value-Coding. Liefert ein NSDictionary mit den Schlssel/ Wert-Paaren zu den Schlsseln, die ber das Array keys angegeben werden. Ruft fr alle Schlssel in keys objectValueFor Key: auf und ordnet dem Schlssel den Wert NSNull zu, wenn der Rckgabewert nil ist. - (id)forwardingTargetForSelector:(SEL)aSelector

Messaging. Wird von der Laufzeitumgebung vor -forwardInvo cation aufgerufen, wenn eine Nachricht unbekannt ist, und sollte ein anderes Objekt liefern, an das die Laufzeit die unbekannte Nachricht unmittelbar weiterleiten soll. Erhlt als Argument den Selektor der Nachricht, fr die keine Implementierung gefunden wurde.
- (void)forwardInvocation:(NSInvocation *)anInvocation

Messaging. Wird von der Laufzeitumgebung aufgerufen, wenn eine Nachricht unbekannt ist. Diese Nachricht bietet dem Empfnger eine Mglichkeit, unbekannte Nachrichten weiterzuleiten, z. B. an einen anderen Empfnger. Das Argument ist ein NSInvocation-Objekt, das die Nachricht beschreibt, fr die keine Implementierung gefunden wurde.

NSObject-Referenz | 175

- (NSUInteger)hash

Liefert einen Hashwert fr das Objekt. Der Hashwert ist ein ganzzahliger Wert, der eine hinreichend eindeutige Darstellung eines Objekts bietet. Der Hashwert von zwei Objekten, die als gleich betrachtet werden, sollte gleich sein. Aus der Gleichheit des Hashwerts fr zwei Objekte kann man aber nicht die Gleichheit der Objekte schlieen.
- (id)init

Objektlebenszyklus. Initialisiert ein Objekt und liefert es zurck. Das zurckgelieferte Objekt muss nicht mit dem identisch sein, dem die Nachricht gesendet wurde. Alle weiteren Operationen sollten deswegen immer den Rckgabewert dieser Nachricht nutzen. Die NSObject-Implementierung macht nichts und liefert einfach self zurck. Andere Klassen berschreiben init, um Instanzen den Anforderungen der Klasse gem zu initialisieren, nachdem sie mit alloc erstellt wurden. blicherweise werden die beiden Nachrichten folgendermaen verkettet:
Klasse *variable = [[Klasse alloc] init]; + (void)initialize

Klasseninitialisierung. Wird zur Initialisierung der Empfngerklasse aufgerufen, bevor sie genutzt wird. Die Laufzeitumgebung garantiert, dass der Klasse diese Nachricht gesendet wird, bevor Anwendungscode ihr oder einer ihrer Unterklassen irgendeine andere Nachricht sendet. Das wird von der Laufzeitumgebung so durchgefhrt, dass Oberklassen die Nachricht vor Unterklassen erhalten. berschreiben Sie diese Nachricht, wenn Sie Initialisierungsschritte durchfhren mssen, die fr die gesamte Klasse nur einmal erfolgen drfen, z. B. um eine statische Variable zu initialisieren.
+ (NSMethodSignature *) instanceMethodSignatureForSelector:(SEL)aSelector Messaging. Liefert eine Referenz auf das NSMethodSignature-Ob-

jekt, das die Signatur der Instanzmethode beschreibt, die durch das Selektorargument angegeben wird, oder nil, wenn keine entsprechende Methode gefunden werden kann.

176 | Kapitel 16: NSObject-Referenz

+ (IMP)instanceMethodForSelector:(SEL)aSelector

Messaging. Liefert einen Zeiger auf die Implementierung der Instanzmethode, die durch den bergebenen Selektor angegeben wird. Der Selektor darf nicht null sein und muss einen gltigen Selektor fr den Empfnger darstellen. Andernfalls meldet die Methode einen Fehler. Nutzen Sie diese Nachricht, wenn Sie ber das Klassenobjekt eine Implementierung einer Instanzmethode ermitteln mssen.
- (BOOL)isEqual:(id)otherObject Prft, ob self dem bergebenen Objekt gleich ist. + (BOOL)instancesRespondToSelector:(SEL)aSelector

Laufzeitinformation. Prft, ob Instanzen des Empfngers den als Argument bergebenen Selektor untersttzen. Liefert YES, falls das der Fall ist, andernfalls NO. Siehe auch -respondsTo Selector:.
- (BOOL)isKindOfClass:(Class)aClass

Laufzeitinformation. Prft, ob der Empfnger eine Instanz der bergebenen Klasse oder einer ihrer Unterklassen ist. Liefert YES, wenn das der Fall ist, andernfalls NO.
- (BOOL)isMemberOfClass:(Class)aClass

Laufzeitinformation. Prft, ob der Empfnger eine Instanz der Klasse ist, die das bergebene Class-Objekt reprsentiert. Liefert YES, falls das der Fall ist, andernfalls NO.
- (BOOL)isProxy

Laufzeitinformation. Mit dieser Methode kann geprft werden, ob der Empfnger eine andere Basisklasse als NSObject hat (vorausgesetzt, diese Basisklasse bernimmt das NSObject-Protokoll, das diese Nachricht definiert). Die NSObject-Implementierung liefert NO. Sie wird beim Umgang mit NSProxy-Objekten bentigt, da Nachrichten wie -isMemberOfClass: oder -isKind OfClass nicht das Proxyobjekt, sondern das Objekt prfen, fr das der Proxy einsteht. NSProxy ist nicht Teil der NSObject-Hierarchie.

NSObject-Referenz | 177

+ (BOOL)isSubclassOfClass:(Class)aClass Laufzeitinformation. Liefert YES, wenn die Klasse des Empfn-

gers der als Argument bergebenen Klasse entspricht bzw. eine Unterklasse der bergebenen Klasse ist.
+ (void)load

Klasseninitialisierung. Wird von der Laufzeitumgebung aufgerufen, wenn ihr eine Klasse oder Kategorie hinzugefgt wird. +load wird dynamisch geladenen und statisch eingebundenen Klassen und Kategorien gesendet, die +load selbst implementieren. Geerbte +load-Implementierungen werden nicht aufgerufen. Fr eine Klasse kann +load mehrfach implementiert sein: einmal fr die Klasse selbst und einmal fr jede Kategorie. Oberklassen erhalten die Nachricht vor ihren Unterklassen, und die +load-Implementierung der Klasse wird vor den +loadImplementierungen von Kategorien aufgerufen. berschreiben Sie +load, wenn Sie beim Laden einer Klasse oder ihrer Kategorien spezielle Schritte unternehmen wollen.
- (IMP)methodForSelector:(SEL)aSelector

Messaging. Liefert einen Zeiger auf die Implementierung der Instanz- oder Klassenmethode, die durch den bergebenen Selektor angegeben wird. Der Selektor darf nicht null sein und muss einen gltigen Selektor fr den Empfnger darstellen. Andernfalls meldet die Methode einen Fehler. Senden Sie diese Nachricht der Klasse, wenn Sie die Implementierung einer Klassenmethode bentigen, bzw. einer Instanz, wenn Sie die Implementierung einer Instanzmethode bentigen.
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector Messaging. Liefert einen Zeiger auf ein NSMethodSignature-Ob-

jekt, das die Methodensignatur der Methode beschreibt, die durch den als Argument bergebenen Selektor identifiziert wird. Muss entsprechend implementiert werden, wenn -forwardInvocation: berschrieben wird, da die Methodensignatur zum Aufbau des NSInvocation-Objekts bentigt wird, das -forwardInvocation: als Argument enthlt.

178 | Kapitel 16: NSObject-Referenz

(NSMutableArray *) mutableArrayValueForKey: (String *) key (NSMutableArray *) mutableArrayValueForKeyPath: (String *) keyPath - (NSMutableSet *)mutableSetValueForKey:(NSString *)key (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath - (NSMutableOrderedSet *)mutableOrderedSetValueForKey: (NSString *)key - (NSMutableOrderedSet *)mutableOrderedSetValueForKeyPath: (NSString *)keyPath

Key/Value-Coding. Diese Methoden liefern jeweils ein vernderliches Objekt des entsprechenden Collection-Typs (NSMutableArray, NSMutableSet, NSMutableOrderedSet), das als Proxy fr das 1:m-Verhltnis dient, das ber den angegebenen Schlssel/Schlsselpfad identifiziert wird. Die Standardimplementierungen nutzen die blichen KVC-Namenskonventionen und sollten auf eine Eigenschaft kompatiblen Typs zugreifen. Alle nderungen an diesem Proxyobjekt wirken sich unmittelbar auf das Objekt aus, das hinter diesem 1:m-Verhltnis steht. Rufen Sie z. B. mit mutableArrayValueForKey den Wert einer NSArray-Eigenschaft ab, erhalten Sie ein NSMutableArray-Objekt, mit dem Sie das von der Eigenschaft referenzierte NSArray-Objekt unmittelbar modifizieren knnen, zum Beispiel:
id hubert = [ [Spieler alloc] initMitName: @"Hubert"]; id waffen = [hubert mutableArrayValueForKey: @"waffen"]; [waffen addObject: @"Schwert"];

hubert.waffen entspricht nun @[@"Schwert"]. - (id)mutableCopy

Objektlebenszyklus. Liefert eine vernderliche Kopie des Objekts zurck, wenn der Empfnger das NSMutableCopying-Protokoll untersttzt. -mutableCopying ist eine Hilfsmethode, die einfach die NSMutableCopying-Methode -mutableCopyWithZone: aufruft. Sie liefert den Rckgabewert von -mutableCopyWithZone: zurck, wenn der Empfnger NSMutableCopying implementiert. Andernfalls lst sie standardmig eine Ausnahme aus.

NSObject-Referenz | 179

+ (id)mutableCopyWithZone:(NSZone *)zone Objektlebenszyklus. Liefert einfach self zurck, keine Kopie! Das ist keine Implementierung des NSMutableCopying-Protokolls, sondern eine Hilfsmethode, die es ermglicht, Class-Objekte dort zu nutzen, wo NSMutableCopying-kompatible Objekte bentigt werden. Das zone-Argument wird ignoriert (siehe +copyWithZone:). + (id)new

Objektlebenszyklus. Alloziert und initialisiert eine neue Instanz der Empfngerklasse und liefert sie zurck. [Klasse new] entspricht [ [Klasse alloc] init]. +new wird in der Objective-C-Welt selten verwendet, ist der alloc/init-Kombination aber vollkommen quivalent, wenn init der designierte Initialisierer ist.
- (id)performSelector:(SEL)aSelector - (id)performSelector:(SEL)aSelector withObject:(id)anObject - (id)performSelector:(SEL)aSelector withObject:(id)anObject withObject:(id)anotherObject

Messaging. Sendet dem Empfnger dynamisch eine Nachricht und liefert das Ergebnis des Aufrufs dieser Nachricht. Die -per formSelector-Nachrichten sind einem direkten Senden der ber die Argumente kodierten Nachricht quivalent. Nutzen Sie sie, wenn die zu sendende Nachricht von Laufzeitbedingungen abhngig ist. Als erstes Argument erwarten alle Versionen einen Selektor, der die Nachricht beschreibt. Nutzen Sie die erste Version fr Nachrichten ohne Argumente, die zweite fr Nachrichten mit einem Argument und die dritte fr Nachrichten mit zwei Argumenten. Die withObject-Parameter geben die Argumente fr die Nachricht an. Mit diesen Nachrichten knnen nur Nachrichten kodiert werden, die null bis zwei Objektargumente erwarten und einen Objektwert als Ergebnis liefern.
- (oneway void)release

Speicherverwaltung. Gibt das Objekt frei und hebt eine Aufbewahrungsanforderung auf (vermindert den Referenzzhler). Unter ARC ist diese Nachricht nicht zulssig.

180 | Kapitel 16: NSObject-Referenz

+ (BOOL)resolveInstanceMethod:(SEL)name + (BOOL)resolveClassMethod:(SEL)name

Messaging. Diese beiden Methoden knnen von der Laufzeit aufgerufen werden, wenn keine Implementierung fr eine Nachricht gefunden werden kann. Sie bieten einer Klasse die Mglichkeit, dynamisch eine Implementierung fr die entsprechende Nachricht zu erstellen. Bei einer Instanznachricht wird +resolveInstanceMethod aufgerufen, bei einer Klassennachricht +resolveClassMethod. Das Argument ist bei beiden Methoden der unbekannte Selektor; der Rckgabewert sollte jeweils anzeigen, ob dem Objekt dynamisch eine Methodenimplementierung hinzugefgt wurde (YES) oder nicht (NO). Die Laufzeit prft, ob dem Objekt tatschlich eine Methode hinzugefgt wurde, und meldet eine Warnung, wenn Methodenimplementierung und Rckgabewert nicht im Einklang sind.
- (BOOL)respondsToSelector:(SEL)aSelector

Laufzeitinformation, Messaging. Prft, ob der Empfnger den als Argument bergebenen Selektor untersttzt. Liefert YES, wenn das der Fall ist, andernfalls NO. Ein Empfnger untersttzt einen Selektor blicherweise, wenn er eine korrespondierende Methode erbt oder selbst implementiert. -respondsToSelector: bercksichtigt aber auch dynamische Methodenimplementierungen, die von +resolveInstanceMethod: bzw. +resolveClass Method: gestellt werden. Wenn ein Empfnger die Weiterleitung von Nachrichten untersttzt, wird das von +respondsToSe lector: nicht widergespiegelt. Gegebenenfalls sollten Sie berlegen, -respondsToSelector: entsprechend zu berschreiben. Wenn Sie wissen wollen, ob die Klasse eine Methode untersttzt, knnen Sie -respondsToSelector auf dem Class-Objekt aufrufen, das diese Klasse reprsentiert.
- (id)retain

Speicherverwaltung. Fordert, dass das Objekt fr den Benutzer aufbewahrt wird (erhht den Referenzzhler um eins). Unter ARC ist diese Nachricht nicht zulssig.

NSObject-Referenz | 181

- (NSUInteger)retainCount

Speicherverwaltung. Liefert die Anzahl der Aufbewahrungsanforderungen (den Wert des Referenzzhlers). Unter ARC ist diese Nachricht nicht zulssig.
- (void)setNilValueForKey:(NSString *)key

Key/Value-Coding. Wird von den Standardimplementierungen der set-Methoden aufgerufen, wenn fr eine skalare Eigenschaft der Wert nil bergeben wird. Die Standardimplementierung lst eine NSInvalidArgumentException aus.
- (void)setValue:(id)value forKey:(NSString *)key - (void)setValue:(id)value forKeyPath:(NSString *)keyPath Key/Value-Coding. Setzt den Wert der ber den String key bzw. keyPath angegebenen Eigenschaft. - (void)setValue:(id)value forUndefinedKey:(NSString *)key Key/Value-Coding. Wird von den anderen set-Methoden auf-

gerufen, wenn ein Schlssel nicht auf einen Setter bzw. eine Instanzvariable abgebildet werden kann. Die Standardimplementierung lst eine NSUndefinedKeyException aus.
- (void)setValuesForKeysWithDictionary: (NSDictionary *)keyedValues

Key/Value-Coding. Setzt die Werte der Objekteigenschaften, die durch die Schlssel des bergebenen NSDictionary angegeben werden, auf die den Dict-Schlsseln zugeordneten Werte. Die Standardimplementierung ruft fr die einzelnen Schlssel/ Wert-Paare im Dict setValue:forKey auf und ersetzt alle NSNullWerte im Dict durch nil.
- (Class)superclass + (Class)superclass

Laufzeitinformation. Liefert das Class-Objekt, das die Oberklasse des Empfngers beschreibt.
- (id)valueForKey: (String *) key - (id)valueForKeyPath: (String *) keyPath

Key/Value-Coding. Ruft den Wert fr das ber den Schlsselbzw. Schlsselpfadstring angegebene 1:1- oder 1:m-Verhltnis ab. Die Standardimplementierung liefert den Wert einer Eigen-

182 | Kapitel 16: NSObject-Referenz

schaft bzw. Instanzvariablen gleichen Namens. Wenn key bzw. keyPath eine skalare Eigenschaft identifiziert, wird der Wert in einen passenden NSValue-Typ verpackt.
- (id)valueForUndefinedKey:(NSString *)key

Key/Value-Coding. Diese Methode wird von den Standardimplementierungen der anderen Methoden aufgerufen, wenn der Schlssel nicht auf einen Getter bzw. eine Instanzvariable abgebildet werden kann. Die Standardimplementierung lst eine NSUndefinedKeyException aus.

NSObject-Referenz | 183

KAPITEL 17

Compilerdirektiven

Die folgende Liste enthlt smtliche Compilerdirektiven, die in Objective-C genutzt werden. Die meisten davon wurden bereits in anderen Teilen dieses Buchs eingesetzt. Manche Direktiven erwarten Argumente; einige erwarten ein Argument in runden Klammern, andere einen Codeblock in geschweiften Klammern, wieder andere beides. Dies wird in der Aufstellung durch Klammern bzw. geschweifte Klammern angezeigt. Zu Anfang jeden Abschnitts ist der Aufgabenbereich vermerkt, fr den diese Compilerdirektive gedacht ist. Zu vielen davon finden Sie in den entsprechenden Kapiteln weitere Erluterungen und Verwendungsbeispiele.
@autoreleasepool{}

Speicherverwaltung. Stellt einen neuen Auto-Release-Pool fr den Block zur Verfgung, der mit der Direktive markiert wird. Beim Eintritt in den Block wird der aktuelle Zustand eines eventuell vorhandenen Auto-Release-Pools gespeichert. Wird der Block normal beendet, wird der fr diesen Block geschaffene Auto-Release-Pool geleert und der gespeicherte Zustand des Auto-Release-Pools wiederhergestellt. Wird die Ausfhrung des Blocks durch eine Ausnahme abgebrochen, wird der Pool standardmig nicht geleert. Auto-Release-Pool-Blcke knnen geschachtelt werden. Die Cocoa-Bibliotheken nutzen die Auto-Release-Einrichtungen und erwarten deswegen, dass ein Auto-Release-Pool zur Verfgung steht. Gibt es keinen, fhrt das unweigerlich zu Speicherlchern, sobald Sie die Cocoa-Bibliotheken nutzen. Da
| 185

die AppKit- und die UIKit-Event-Loops Auto-Release-Pools bereitstellen, mssen Sie sich darum bei der GUI-Programmierung fr Mac OS X bzw. iOS in der Regel nicht selbst kmmern. Normalerweise brauchen Sie sie nur, wenn Sie neue Threads starten bzw. wenn Sie einen lokalen @autoreleasepool-Block zu Optimierungszwecken einsetzen wollen.
@catch() {}

Ausnahme. Markiert einen Ausnahme-Handler. In der Klammer muss ein Ausnahmetyp stehen, in der Regel ist das NSEx ception *, zum Beispiel:
@catch (NSException *) {}

Wird statt eines Ausnahmetyps ein vollstndiges Ausnahmeargument angegeben, wird diesem das Ausnahmeobjekt zugewiesen, das ausgelst wurde. Das ermglicht es, im AusnahmeHandler auf die Informationen zuzugreifen, die der ausgelsten Ausnahme mitgegeben wurden:
catch (NSException *ex) { NSLog(@"%@", ex); }

Auf einen @try-Block drfen beliebig viele @catch()-Blcke folgen, die unterschiedliche Ausnahmetypen angeben. Der Ausnahmetyp, den ein @catch()-Block verarbeitet, muss angegeben werden. Die Klammer darf nicht weggelassen werden oder leer sein. Der allgemeinste Ausnahme-Handler ist einer, der den Ausnahmetyp id deklariert.
@class

Deklaration. Vor-Deklaration einer Klasse. Die Direktive wird einem Namenstoken oder einer Liste kommaseparierter Namenstoken vorangestellt und sagt dem Compiler, dass das entsprechende Namenstoken einen Klassennamen reprsentiert. Es wird keine Klassendefinition importiert, sondern nur ein Name als Klassenname bekannt gemacht. Solange der Compiler keine Implementierungsinformationen fr diese Klasse bentigt, kann diese Direktive den #import der Headerdatei ersetzen, in der die Klasse deklariert wird. Das ermglicht es, #import-Direktiven so nah wie mglich an den Stellen zu halten, in denen die importierten Klassen tatschlich genutzt werden.

186 | Kapitel 17: Compilerdirektiven

In einer Schnittstellendeklaration, die eine Klasse als Ivar-, Eigenschafts-, Argument- oder Rckgabetyp nutzt, werden z. B. hufig keinerlei Informationen ber die Implementierung, d. h. die Felder, Eigenschaften und Methoden einer Klasse, bentigt. Da dort nur bekannt sein muss, dass das Namenstoken eine Klassenangabe reprsentiert, damit der erforderliche Platz fr einen Objektzeiger reserviert werden kann, reicht dann eine Vor-Deklaration mit @class aus. In der Implementierungsdatei, in der die Implementierung der vor-deklarierten Klasse genutzt wird, muss dann der Header importiert werden. Die Implementierung muss z. B. bereits in der Schnittstellendatei bekannt sein, wenn eine Klasse als Oberklasse der neu zu definierenden Klasse dienen soll.
@compatibility_alias

Erstellt einen Alias fr eine Klasse. Diese Direktive wird auf der obersten Ebene einer Schnittstellen- oder Implementierungsdatei genutzt. Sie ist, wie der Name sagt, gedacht, um Klassen zur Kompatibilittssicherung unter einem anderen Namen ansprechen zu knnen. Sie sollten sie nicht wie hier (nur zu Beispielzwecken) vorgefhrt nutzen, um sich Tipparbeit bei langen Klassennamen zu ersparen, da das die Lesbarkeit Ihres Codes beeintrchtigt (und es fr Aliase in Xcode auch keine Vervollstndigungshilfen gibt):
#import <Foundation/Foundation.h> @combatibility_alias String NSMutableString; int main() { String *s = [String stringWithString: @"123"]; [s appendString: @"4"]; // ... }

@dynamic

Eigenschaft. Deklariert, dass die Implementierung der mit der Direktive markierten Eigenschaft dynamisch zur Laufzeit angeboten wird. Der Compiler prft nicht, ob es eine Implementierung fr die angegebene Eigenschaft gibt, und fhrt auch keine automatische Synthetisierung durch.

Compilerdirektiven | 187

@end

Deklaration. Schliet eine Deklarationseinheit, die durch eine Direktive wie @implementation oder @interface geffnet wurde.
@encode()

Typkodierung. Erwartet als Argument in der Klammer ein Objekt oder einen Objective-C-Typ und substituiert den String, der die Typkodierung fr diesen Typ darstellt.
@finally {}

Ausnahme. Letzter Baustein der try-catch-finally-Ausnahmeverarbeitung. Markiert einen Block, der Operationen definiert, die ausgefhrt werden sollen, nachdem ein @try-Block ausgefhrt wurde. Der @finally-Block wird immer ausgefhrt, unabhngig davon, ob der @try-Block erfolgreich ausgefhrt werden kann. Tritt im @try-Block eine Ausnahme auf, wird der @finallyBlock ausgefhrt, nachdem der entsprechende @catch()-Handler abgearbeitet wurde. Der @try-Block wird auch ausgefhrt, wenn es keinen passenden @catch()-Block gibt oder wenn der @catch-Block die Ausnahme mit @throw neu auslst oder im @catch()-Block eine andere Ausnahme ausgelst wird.
@implementation

Deklaration. Markiert den Anfang der Implementierung fr die Klasse, deren Name auf die Direktive folgt. Ein Implementierungsabschnitt muss durch die @end-Direktive geschlossen werden.
@implementation Klasse // Implementierungscode @end

Es kann mehrere Implementierungsabschnitte fr eine Klasse geben, die Kategorien implementieren.


@interface

Deklaration. Markiert den Anfang der Deklaration der Schnittstelle der Klasse, deren Name auf die Direktive folgt. Vererbungsverhltnisse und Protokollimplementierungen werden in der Schnittstellendeklaration angeben, zum Beispiel:
@interface Klasse : Oberklasse <Protokoll1, Protokoll2>

188 | Kapitel 17: Compilerdirektiven

Auf den Klassennamen (und eventuelle Oberklassen und Protokollangaben) kann ein Abschnitt in geschweiften Klammern folgen, in dem die Ivars der Klasse deklariert werden. Die Schnittstellendeklaration muss mit der @end-Direktive geschlossen werden. Es kann beliebig viele @interface-Abschnitte geben, die verschiedene Kategorien deklarieren, die eine vorhandene Schnittstelle erweitern. Eine Kategoriedeklaration wird angegeben, indem hinter dem Klassennamen in einer Klammer der Kategoriename angegeben wird, zum Beispiel:
@interface Klasse

Kategorie. Der Compiler meldet eine Warnung, wenn die gleiche Kategorie mehrfach deklariert wird. Auerdem kann es fr eine Klasse beliebig viele Klassenerweiterungen, anonyme Kategorien, geben. Eine Klassenerweiterung wird durch eine leere Klammer hinter dem Schnittstellennamen angeben:
@interface Klasse ()

@optional

Protokolldeklaration. Leitet in einer Protokolldeklaration einen Abschnitt ein, in dem Methoden aufgefhrt werden, die optional sind. Die Direktive gilt fr alle nachfolgenden Methoden, bis sie durch eine @required-Direktive ersetzt oder die Protokolldeklaration mit @end geschlossen wird.
@package

Sichtbarkeit von Ivars. Leitet im Ivar-Deklarationsabschnitt einer @interface-Deklaration einen Abschnitt ein, in dem Methoden stehen, die Paketsichtbarkeit haben. Sie gilt fr alle nachfolgenden Methoden, bis sie durch eine der anderen Sichtbarkeitsdirektiven (@public, @protected oder @private) ersetzt wird bzw. die Ivar-Deklaration mit } geschlossen wird.

Compilerdirektiven | 189

@private

Sichtbarkeit von Ivars. Leitet im Ivar-Deklarationsabschnitt einer @interface-Deklaration einen Abschnitt ein, in dem Methoden stehen, die privat, also nur in der Klasse sichtbar sind. Sie gilt fr alle nachfolgenden Methoden, bis sie durch eine der anderen Sichtbarkeitsdirektiven (@public, @protected oder @package) ersetzt wird bzw. die Ivar-Deklaration mit } geschlossen wird.
@property

Eigenschaft. Deklariert in einem @interface-Abschnitt eine Eigenschaft. Eine Eigenschaftsdeklaration besteht aus einer Typangabe und einem Eigenschaftsnamen sowie einem Satz von Eigenschaftsattributen und wird durch ein Semikolon abgeschlossen. Die Eigenschaftsattribute werden durch Kommata getrennt in einer auf die Direktive folgende Klammer angegeben:
@property (weak, readonly) id eigenschaft;

Wenn mehrere Eigenschaften den gleichen Typ und die gleichen Attribute haben, knnen sie durch Kommata getrennt mit einer Direktive angegeben werden:
@property (weak, readonly) id eigenschaft1, eigenschaft2;

Wenn keine Eigenschaftsattribute angegeben werden, ist die Eigenschaft unter ARC vorgabemig strong und readwrite, unter MRC hingegen assign und readwrite. Eigenschaften sind auerdem standardmig atomar.
@protected

Sichtbarkeit von Ivars. Leitet im Ivar-Deklarationsabschnitt einer @interface-Deklaration einen Abschnitt ein, in dem Methoden stehen, die geschtzt sind, d. h. in der Klasse und in von ihr abgeleiteten Klassen sichtbar sind. Sie gilt fr alle nachfolgenden Methoden, bis sie durch eine der anderen Sichtbarkeitsdirektiven (@public, @package oder @private) ersetzt wird bzw. die Ivar-Deklaration mit } geschlossen wird.

190 | Kapitel 17: Compilerdirektiven

@protocol

Deklaration. Die @protocol-Direktive wird in drei Kontexten genutzt, in denen sie jeweils eine andere Bedeutung hat. 1. Wenn auf die Direktive ein Protokollname (und optional eine Protokollliste in spitzen Klammern) folgt, an die eine Liste von Methodendeklarationen anschliet, die von der Direktive @end beendet wird, deklariert die Direktive ein Protokoll:
bernommenesProtokoll> @protocol Protokoll <U //Methodendeklarationen @end

2. Wenn die Direktive Teil einer eigenstndigen mit einem Semikolon abgeschlossenen Anweisung ist, in der auf sie ein Namenstoken oder eine kommaseparierte Liste von Namenstoken folgt, wird ein Protokoll bzw. werden mehrere Protokolle vor-deklariert (mehr Informationen zur Vor-Deklaration finden Sie beim @class-Eintrag), zum Beispiel:
@protocol Protokoll;

3. Wenn nach der Direktive in einer Klammer ein Protokollname folgt, substituiert der Compiler das korrespondierende Protocol-Objekt. Auf diese Weise wird die Direktive genutzt, um ber die NSObject-Nachricht -conformsToProtocol: die bereinstimmung mit einem Protokoll zu berprfen, zum Beispiel:
if([Klasse conformsToProtocol: @protocol(Protokoll))

@public

Sichtbarkeit von Ivars. Leitet im Ivar-Deklarationsabschnitt einer @interface-Deklaration einen Abschnitt ein, in dem Methoden stehen, die ffentlich sind. Sie gilt fr alle nachfolgenden Methoden, bis sie durch eine der anderen Sichtbarkeitsdirektiven (@package, @protected oder @private) ersetzt wird bzw. die Ivar-Deklaration mit } geschlossen wird.
@required

Protokolldeklaration. Leitet in einer Protokolldeklaration einen Abschnitt ein, in dem Methoden aufgefhrt werden, die erforderlich sind. Die Direktive gilt fr alle nachfolgenden Deklara-

Compilerdirektiven | 191

tionen, bis sie durch eine @optional-Direktive ersetzt oder die Protokolldeklaration mit @end geschlossen wird.
@selector()

Messaging. Wird bei der Kompilierung durch den Selektor, SEL, mit der Nachricht ersetzt, die als Argument in der Klammer angegeben wird. Das Argument muss unter ARC ein bei der Kompilierung bekannter Nachrichtenname sein.
@synchronized(){}

Threading. Die @synchronized-Direktive erstellt einen Mutex, ber den ein Block synchronisiert wird. Sie erspart Ihnen die manuelle Erstellung eines spezifischen Lock- oder MutexObjekts, indem sie Ihnen gestattet, ein beliebiges Objekt als Sperrobjekt zu nutzen. Wird ein Block in zwei verschiedenen Threads ausgefhrt, kann er nur von einem der Threads betreten werden, wenn sie ber das gleiche Objekt synchronisiert werden.
@synthesize

Eigenschaft. Weist den Compiler an, die Implementierung fr die in der Schnittstelle deklarierte Eigenschaft, Getter und/oder Setter sowie Hintergrundfeld, zu generieren. Es knnen mehrere durch Kommata getrennte zu synthetisierende Eigenschaften angegeben werden, zum Beispiel:
@synthesize a, b;

Eigenschaften mssen nicht explizit mit @synthesize synthetisiert werden. Wird @synthesize nicht angegeben, synthetisiert der Compiler die Eigenschaft automatisch. Sie mssen die Direktive nur noch einsetzen, wenn Sie das verwendete Hintergrundfeld selbst festlegen wollen. Beachten Sie, dass die explizite und die automatische Synthetisierung Hintergrundfelder mit unterschiedlichen Namen verwenden!
@throw

Ausnahme. Lst eine Ausnahme aus. Auf die Direktive folgt ein Ausdruck, der zu einem Objekt ausgewertet wird, das dem @catch()-Handler bergeben wird, der die Ausnahme verarbeitet. In der Regel ist dieses Objekt ein NSException-Objekt, aber

192 | Kapitel 17: Compilerdirektiven

es kann ein beliebiger Objektwert sein. In einem @catch()Handler kann @throw ohne Argument verwendet werden, um die aktuelle Ausnahme mit dem aktuellen Ausnahmeobjekt als Argument neu auszulsen.
@try{}

Ausnahme. Markiert einen Block, in dem die Ausnahmeverarbeitung aktiv ist. Tritt in einem @try-Block eine Ausnahme auf, springt die Ausfhrung unmittelbar in den ersten @catchBlock, dessen Argumenttyp mit dem Typ des Ausnahmeobjekts kompatibel ist. Gibt es keinen passenden @catch-Block, gilt die Ausnahme als unverarbeitet und wird im Aufrufstapel nach oben geleitet.

Compilerdirektiven | 193

KAPITEL 18

Alternative Plattformen

Wenn man mit Objective-C programmieren will, braucht man drei Dinge: einen Compiler, eine Laufzeitumgebung und eine Klassenbibliothek. Fr alles gibt es Alternativen jenseits des Apple-Tellerrands. Die wichtigeren davon werden unten aufgefhrt.
Aufgrund der Vielzahl der Werkzeuge, Plattformen und Systemkonfigurationen sowie des stetigen Wandels, dem diese unterworfen sind, haben wir hier darauf verzichtet, ausfhrliche, aber wahrscheinlich rasch veraltete Installations- und Einrichtungsanweisungen anzubieten. Schlagen Sie diese bitte in den aufgefhrten Informationsquellen nach. blicherweise sollten Sie zunchst den Compiler und dann Laufzeit und Klassenbibliothek installieren bzw. kompilieren. Sie mssen damit rechnen, dass die Einrichtung einer lauffhigen Objective-C-Entwicklungsplattform, die moderne Objective-C-Spracheigenschaften untersttzt, auf anderen System als Mac OS X aufwendig und nicht immer ganz unkompliziert ist.

Welche der in diesem Buch beschriebenen Sprach-Features untersttzt werden, ist gleichermaen von Compiler, Laufzeitumgebung und Klassenbibliothek abhngig. Die jeweiligen Abschnitte versuchen, einen groben berblick darber zu bieten, auf welche Features Sie jeweils verzichten mssen.

| 195

Compiler
Die beiden gngigsten Compiler, die Objective-C-Code kompilieren knnen, sind GCC (die GNU Compiler Collection) und Clang.

GCC
GCC kann Objective-C-Code kompilieren und bringt eine eigene Objective-C-Laufzeitumgebung mit. GCC versteht allerdings nur Teile der Objective-C-Syntax, die in diesem Buch verwendet wurde. GCC beherrscht einige Elemente von Objective-C 2.0 wie beispielsweise Eigenschaften und schnelle Enumerationen sowie die @try/ @catch/@finally-Ausnahmeverarbeitung. Neuere Sprachformen werden jedoch auch von den jngsten GCC-Versionen (4.7) noch nicht untersttzt. Es gibt keine automatische Eigenschaftssynthetisierung. Es gibt keine Objektliterale. Und vor allem gibt es keine automatische Speicherverwaltung (ARC). Der in diesem Buch verwendete Beispielcode wird sich unter GCC nur in den seltensten Fllen (wenn berhaupt) kompilieren lassen. GCC ist von Haus aus Teil der meisten Linux-Installationen und kann, sollte das nicht der Fall sein, in der Regel unproblematisch ber das Softwareverwaltungssystem der Distribution installiert werden. Unter Windows kann GCC mithilfe der Cygwin- oder MinGW-Projekte (http://www.cygwin.com bzw. http://www.mingw.org) installiert und betrieben werden

Clang/LLVM
Clang (http://clang.llvm.org) ist ein ursprnglich von Apple entwickeltes und dann als Open Source-Projekt weitergefhrtes Frontend fr den LLVM-Compiler fr die Sprachen C, C++ und Objective-C. Viele der neuen Features von Objective-C, insbesondere ARC, wurden von Apple auf Basis der Mglichkeiten aufgebaut, die die Clang-Infrastruktur bietet. Clang ist ausgesprochen modular und kann Informationen zu den verschiedenen Stufen des Kompilationsprozesses bieten. Clang wird z. B. intern von Xcode

196 | Kapitel 18: Alternative Plattformen

fr die Syntaxprfung, fr Fehlermeldungen und die Codevervollstndigung genutzt. Welche der in diesem Buch vorgestellten compilerabhngigen Features von Clang untersttzt werden, ist nur von der auf Ihrem System verfgbaren Version abhngig. Clang kann blicherweise in einer meist nicht mehr ganz aktuellen Version aus den Softwarearchiven von Linux-Distributionen installiert werden. Auf der LLVM-Website unter http://llvm.org/releases/download.html stehen Binrpakete fr aktuellere Versionen fr verschiedene Plattformen bereit (unter anderem auch ein, aktuell nur experimentelles, MinGW-basiertes Paket fr Windows). Den Code fr die aktuelle Version finden Sie im LLVM-Subversion-Repository unter http://llvm.org/svn/llvm-project/llvm/trunk. Eine Anleitung fr den Download und die Kompilation von LLVM und Clang finden Sie unter http://clang.llvm.org/get_started.html#build.
Wenn Sie Clang aus den Quellen kompilieren wollen, bentigen Sie natrlich zuvor einen anderen Compiler. Dazu knnen Sie entweder gcc oder ein clang-Binrpaket nutzen.

Laufzeitumgebungen und Plattformen


Neben Apples Laufzeitumgebung gibt es verschiedene andere Laufzeitumgebungen und Plattformen (Klassenbibliotheken), die unabhngig vom Compiler eingesetzt werden knnen. Wir behandeln hier beides gemeinsam, weil mittlerweile die meisten Plattformen ihre eigenen Laufzeitbibliotheken mitbringen.

GNU-Runtime
Die GNU-Runtime ist die Objective-C-Laufzeitumgebung, die Teil von GCC ist. Seit GCC 4.6 untersttzt die GNU-Runtime eine Reihe von Erweiterungen aus Objective-C 2.0. Neue Spracherweiterungen wie ARC haben auch in neueren Versionen (GCC 4.7) noch keinen Eingang gefunden.

Laufzeitumgebungen und Plattformen | 197

Die GNU-Runtime bringt keine Klassenbibliothek mit, sondern bietet nur eine Handvoll Klassen an, die erforderlich sind, um elementare Sprachstrukturen wie Klassen und Protokolle zu modellieren. Wenn Sie die GNU-Runtime nutzen wollen, werden Sie in der Regel also zustzlich eine der nachfolgend aufgefhrten alternativen Plattformen einsetzen mssen. Beachten Sie, dass die GNURuntime eine Basisklasse mit einem anderen Namen als Apples Laufzeitumgebung nutzt, Object statt NSObject.

GNUStep-Runtime
GNUStep (http://www.gnustep.org) ist eine freie Implementierung der OpenStep-Spezifikation, auf die auch Apples Cocoa zurckgeht. Das GNUStep-Projekt bietet unter anderem eine Objective-C-Laufzeitumgebung und eine Objective-C-Klassenbibliothek. Laufzeitumgebung und Klassenbibliothek streben nach weitestgehender Kompatibilitt mit Apples Laufzeitumgebung bzw. Klassenbibliothek. Die GNUStep-Runtime untersttzt unter anderem Blocks, synthetisierte Zugriffsmethoden fr Eigenschaften und ARC. Die GNUStepRuntime kann bei der Kompilation mit Clang mit dem Compilerschalter -fobjc-runtime=gnustep angegeben werden. Die Klassenbibliotheken bieten eine weitgehende Untersttzung der Foundation- und App-Kit-APIs. Das bedeutet, mit GNUStep knnen auch GUI-Anwendungen erstellt werden. Eine Einschrnkung ist aktuell unter anderem, dass die Klassenbibliothek keine Untersttzungsmethoden fr die Objektindizierung bietet. GNUStep ist fr verschiedene Unix-Spielarten, unter anderem Mac OS X, Linux und FreeBSD, sowie Windows verfgbar. Bei vielen Unix-Spielarten kann es ber das jeweilige Paketverwaltungssystem installiert werden. Download-Pakete fr Unix-Systeme finden Sie unter http://wwwmain.gnustep.org/resources/ downloads.php. Download-Pakete und Anleitungen fr die Windows-Installation gibt es unter http://www.gnustep.org/experience/Windows.html. Fr die Entwicklung von KommandozeilenAnweisungen reichen in der Regel die Make- und Base-Pakete (bzw. ihre distributionsspezifischen Entsprechungen, wenn Sie das
198 | Kapitel 18: Alternative Plattformen

Softwarearchiv Ihrer Distribution nutzen). Beachten Sie die Liste der Abhngigkeiten unter http://wwwmain.gnustep.org/resources/ downloads.php#pre. Die GNUStep-Runtime kann ebenfalls aus dem Softwarearchiv (libobjc2) installiert oder aus den Quellen kompiliert werden. Den Quellcode knnen Sie sich folgendermaen aus dem SubversionRepository beschaffen:
svn co http://svn.gna.org/svn/gnustep/libs/libobjc2/trunk \ libobjc2

Zur Kompilation von GNUStep-basierten Anwendungen sollten Sie die GNUStep-Infrastruktur nutzen, d. h. GNUmakefiles oder das gnustep-config-Hilfsprogramm.

Cocotron
Cocotron (http://www.cocotron.org) ist ein Open Source-Projekt, dessen Ziel der Aufbau einer plattformbergreifenden Objective-C-API ist, die weitestgehend mit Apples Cocoa-Plattform kompatibel ist. Cocotron bietet eine eigene Laufzeitumgebung und Klassenbibliotheken, die unter anderem die AppKit- und Foundation-Funktionalitten abdecken. Daneben pflegt das Projekt die Cocotron Developer Tools, die eine Zusammenstellung diverser Open Source-Werkzeuge zum Aufbau einer Objective-C-Entwicklungsumgebung sind. Cocotron unterscheidet sich von GNUStep in der Hinsicht, dass es weniger fr die Entwicklung auf verschiedenen Plattformen gedacht ist als als Cross-Kompilationswerkzeug, mit dem unter Mac OS X Objective-C-basierte Anwendungen fr andere Plattformen erstellt werden knnen, insbesondere moderne Windows-Systeme. Wenn Sie unter Linux oder Windows entwickeln wollen, ist GNUStep deswegen wahrscheinlich die bessere Wahl. Ein Problem bei Cocotron ist, dass der Kompilationsprozess von Haus aus immer noch GCC-basiert ist und deswegen keine ARC-Untersttzung bietet.

ObjFW
ObjFW (https://webkeks.org/objfw/) ist ein leichtgewichtiges, plattformunabhngiges Objective-C-Framework, das ein anderes KonLaufzeitumgebungen und Plattformen | 199

zept als GNUStep oder Cocotron verfolgt. Es definiert eine API, die ein Klassensystem definiert, das hnliche Funktionalitten wie Cocoa bietet, aber einen anderen Klassennamensraum nutzt. Mit ObjFW knnen Sie deswegen plattformbergreifende Anwendungen schreiben, ohne dass es dabei z. B. unter Mac OS X zu Konflikten mit nativen Klassen kommt. Die ObjFW-Klassen spiegeln groe Teile der Foundation-API. Sie tragen das Namensraumprfix OF. Das heit, der FoundationKlasse NSArray entspricht die ObjFW-Klasse OFArray. Viele Nachrichten, die von NSArray untersttzt werden, werden in identischer Form auch von OFArray untersttzt. Aber da ObjFW nicht versucht, die Foundation-API nachzubauen, knnen die Klassen des Frameworks weitere Funktionen anbieten. OFArray bietet z. B. praktische -mappedArrayUsingBlock:- oder -filteredArrayUsingBlock:-Nachrichten, die eine eingebaute blockbasierte Array-Verarbeitung ermglichen, die NSArray nicht anbietet. ObjFW luft auf vielen Unix-Systemen (und sollte laut Website auf allen POSIX-Systemen laufen) sowie unter Windows. Einen Download-Link fr den ObjFW-Quellcode finden Sie auf der ObjFW-Website, oder Sie knnen das Git-Repository unter https://webkeks.org/git/objfw.git klonen. Kompilationsanweisungen finden Sie in der README.md-Datei auf der obersten Ebene der entpackten Verzeichnishierarchie. Die ObjFW-Runtime kann bei der Kompilation mit Clang mit der Kommandozeilenoption -fobjc-runtime=objfw angefordert werden. Das bewirkt gleichzeitig, dass fr Objektliterale die ObjFW-eigenen Klassen genutzt werden. Bei einer Kompilation fr ObjFW wird fr @[...] also ein OFArray-Objekt genutzt.

200 | Kapitel 18: Alternative Plattformen

KAPITEL 19

Clang-Optionen

Die nachfolgende Liste ist keine vollstndige Aufstellung der von Clang/LLVM untersttzten Optionen, sondern nur ein kurzer berblick ber die wichtigsten Objective-C-relevanten Optionen fr Clang.
-arcmt-check

Prfen, ob es bei einer Umwandlung von Code fr ARC Probleme geben knnte, die ein manuelles Eingreifen erfordern.
-arcmt-migrate-emit-errors

Fehler bei der Umwandlung fr ARC auch dann melden, wenn die Migrationskomponente sie reparieren kann.
-arcmt-migrate-report-output Wert

Ausgabepfad fr den Plist-Bericht.


-arcmt-migrate

Modifikationen anwenden und temporre Dateien erzeugen, die ARC- konform sind.
-arcmt-modify

Dateien so anpassen, dass sie ARC-konform sind.


-ObjC

Eingabedateien als Objective-C-Dateien behandeln.


-fgnu-runtime

Ausgaben generieren, die mit der Standard-GNU-Laufzeitumgebung kompatibel sind.

| 201

-fblocks-runtime-optional

Die Blocks-Runtime schwach einbinden.


-fblocks

Aktiviert Blocks.
-fconst-strings

Bewirkt, dass fr Stringliterale ein mit const qualifizierter Typ verwendet wird.
-fconstant-string-class Klassenname

Gibt den Namen der Klasse an, die fr unvernderliche Objective-C-Stringobjekte verwendet werden soll.
-fno-const-strings

In C und Objective-C keinen mit const qualifizierten Typ fr Stringliterale verwenden.


-fno-constant-cfstrings

Keine CoreFoundation-Stringkonstanten erstellen.


-fno-objc-arc

Schaltet ARC bei Projekten, die standardmig ARC nutzen, fr die Kompilation einzelner Dateien aus.
-fno-objc-infer-related-result-type

Den Objective-C-Rckgabetyp nicht aus der Methodenfamilie ableiten.


-fobjc-arc-exceptions

Aktiviert ausnahmeverarbeitungssicheren Code unter ARC.


-fobjc-arc

Aktiviert ARC.
-fobjc-default-synthesize-properties

Aktiviert die automatische Synthetisierung von Eigenschaften.


-fobjc-dispatch-method=Wert

Gibt die zu verwendende Art der Methodenauflsung an.


-fobjc-exceptions

Aktiviert die Objective-C-Ausnahmen.

202 | Kapitel 19: Clang-Optionen

-fobjc-fragile-abi

Aktiviert die Verwendung der veralteten fragilen ABI.


-fobjc-gc-only

Fr die Objective-C-Speicherverwaltung ausschlielich die Garbage Collection verwenden.


-fobjc-gc

Aktiviert die Objective-C-Garbage-Collection.


-fobjc-runtime=Wert

Generiert Ausgaben, die mit der entsprechenden Laufzeitumgebung kompatibel sind. Untersttzte Werte sind macosx, macosx-fragile, ios, gcc gnustep und objfw.
-fobjc-runtime-has-arc

Versichert, dass die anvisierte Objective-C-Laufzeitumgebung ARC-Einstiegspunkte bietet.


-fobjc-runtime-has-terminate

Versichert, dass die anvisierte Objective-C-Laufzeitumgebung einen objc_terminate-Einstiegspunkt bietet.


-fobjc-runtime-has-weak

Versichert, dass die anvisierte Objective-C-Laufzeitumgebung schwache Referenzen untersttzt.

Clang-Optionen | 203

Index

Symbole
#import-Prprozessordirektive 6, 8 %@-Stringformatangabe 6 &-Adressoperator 24 *-Dereferenzierungsoperator 24 *-Zeigerindikator 24 +, Klassennachrichtdeklaration 39 -, Instanznachrichtdeklaration 39 .-Operator, Eigenschaftszugriff 28 ..., Varargs-Parameter 46 .h-Dateinamenserweiterung 8 ==-Operator 29 @"...", Stringliterale 6, 15 @[]-Arrayliterale 15 @{...}-Dictionaryliterale 16

A
+accessInstanceVariablesDirectly:-Nachricht 173 Adressoperator, & 24 +alloc-Nachricht 27, 88, 173 Objekte besitzen 111 -alloc-Nachricht 89 Allozierung von Objekten 8889 alternative Plattformen 195 -fobjc-runtime-Compilerschalter 203 ARC 104105 -fobjc-arc-Compilerschalter 202 Ausnahmen unter 79 Blocks 169

Eigenschaftsattribute unter 106 Regeln 109 strong-Eigenschaftsattribut 106 unbekannte Selektoren 128 Variablenqualifizierer 107 weak-Eigenschaftsattribut 106 +archiveDataWithRootObject:-Nachricht 156 +archiveRootObject:toFile:-Nachricht 156 Archivierung von Objekten 151 Coder-Objekte 156 Archivierungsmethoden 154 Array-hnlicher Zugriff auf Objekte 30 Arrayobjekte Literale 15 assign-Eigenschaftsattribut 56 Aufrufen von Methoden 45 Auslsen von Ausnahmen 80 Ausnahmen 78 -fobjc-arc-exceptions-Compilerschalter 202 -fobjc-exceptions-Compilerschalter 202 auslsen 80, 83 Handler 80 Handler fr nicht abgefangene 82 NSException-Objekt erstellen 83

| 205

Objekte 80 Probleme unter ARC 79 vs. Fehler 73 Auto-Release-Blcke 108 Auto-Release-Pools MRC 114 Objekte bergeben an 112 automatische Synthetisierung von Eigenschaften 50 -autorelease-Nachricht 110 -autorelease-Nachricht 174 @autoreleasepool-Compilerdirektive 14, 108, 185 vs. NSAutoreleasePool-Objekte 114 _ _autoreleasing-Variablenqualifizierer 108 @autoreleaspool-Compilerdirektive 6

Typumwandlung 161 Variablen einfangen 165 zirkulre Referenzen 164 Boxed Expressions 18

C
C-Datentypen Untersttzung in Objective-C 11 C-Funktionen self als erstes Argument 131 C-Zeiger 23, 96 Funktionszeiger casten 135 Objekte referenzieren 11 referenzierten Wert abrufen 24 Typumwandlung 61 und Polymorphie 60 Zeiger auf Zeiger 25 Zeiger auf Zeiger, Beispiel 74 Cachen von Implementierungen 135 Callstack 83 -callStackReturnAddresses-Nachricht (NSException) 83 -callStackSymbols-Nachricht (NSException) 83 Casts Blocks 161 @catch-Compilerdirektive 79, 186 Clang 196 Optionen 201 clang-Compiler 8 +class-Nachricht 118, 174 -class-Nachricht 174 @class-Compilerdirektive 186 -class-Nachricht 118 Class-Objekte 117 Class-Typ 25 Objektreferenzen 118 class_addMethod()-Funktion 137 class_getName()-Funktion 120 -className-Nachricht 120 Closures 168

B
Besitzen von Objekten 111 _ _block-Variablenqualifizierer 108, 167 Block_copy()-Makro 172 Block_release()-Makro 172 Blocks 18, 159 -fblocks-Compilerschalter 202 als Closures 168 als Objekte 161 Blocktyp deklarieren 160 Blockvariablen 167 Definition 159 dynamische Nachrichtenimplementierung stellen 138 globale vs. kontextgebundene 168 kontextgebundene 169 kopieren 171 Objekte manipulieren 166 Rckgabetyp explizit angeben 160 Speicherort von Blockvariablen 168

206 | Index

_cmd-Argument 131 Cocotron 199 -code-Nachricht (NSError) 76 Code auf der Kommandozeile kompilieren 8 mit XCode schreiben und kompilieren 7 Coder-Objekte 152, 156 Beispiel 157 prfen 155 Collection-Typen Objekte enumerieren 30 Unvernderlichkeit 31 @compatibility_alias-Compilerdirektive 187 Compiler ARC 104 Erkennung falscher Typangaben 41 Fehler/Warnung bei unbekannten Selektoren 128 Nachrichtenprfung 128 unbekannte Selektoren 132 Compilerdirektiven 13 @autoreleasepool 6, 14, 185186 @catch() 79 @class 186 @compatibility_alias 187 @dynamic 187 @encode() 188 @end 188 @finally 188 @implementation 188 @interface 33, 188 @optional 68, 189 @package 36, 189 @private 36, 190 @property 49, 190 @protected 36, 190 @protocol 67, 191 @public 36 @required 68, 191

@selector 192 @selector() 130 @synthesize 50, 192 @throw 79, 192 @try 79, 193 +conformsToProtocol:-Nachricht 124, 174 -conformsToProtocol:-Nachricht 124 -conformsToProtocol:-Nachricht 174 -copy-Nachricht 96 +copyWithZone:-Nachricht 174 copy-Eigenschaftsattribut 56 -copy-Nachricht 174 NSString-Klasse 99 Objekte besitzen 111 -copyWithZone:-Nachricht 96 implementieren 98

D
-dealloc-Nachricht 99, 175 Dereferenzierungsoperator, * 24 -description-Nachricht 116, 175 designierte Initialisierer 90 Aufgaben 92 delegieren an, in -init-Methoden 93 Dictionaries Literale 16 -dictionaryWithValuesForKeys:-Nachricht 175 -domain-Nachricht (NSError) 76 Downcast 61 @dynamic-Compilerdirektive 52, 187 dynamische Implementierung von Eigenschaften 52 dynamische Nachrichtenauflsung 45 dynamische Operationen id-Typ 26 dynamische Referenzen Eigenschaftszugriff 29

Index | 207

E
Eigenschaften 47 @property-Compilerdirektive 49 Attribute 54 Deklaration 49 dynamisch implementieren 52 in Kategorien 63 in privaten Klassenerweiterungen 65 manuelle Implementierung 48, 51 nicht-atomare 52 private definieren 65 Speicherverwaltung 55 Synthetisierung 50 Zugriff auf 28, 52, 55 Empfnger 12 Typ in Nachrichtendeklaration angeben 39 -encodeWithCoder:-Nachricht 152 @end-Compilerdirektive 188 -enumerateObjectsUsingBlockNachricht 30 +errorWithDomain:code:userInfo:-Nachricht (NSError) 77 Erstellen von Objekten 87 +exceptionWithName:reason:userInfo:-Nachricht (NSException) 83 Exceptions siehe Ausnahmen; Fehler explizite Synthesierung von Eigenschaften 51

F
F-Postfix, Zahlliterale 17 -fblocks-Compilerschalter 202 -fconstant-string-class-Compilerschalter 15, 202 Fehler 74 Beschreibung 77 Code 7576

Domain 76 in -init-Methoden 94 unbekannte Selektoren 128 vs. Ausnahmen 73 Felder 28, 35 in Kategorien 63 in privaten Klassenerweiterungen 65 Kapselung umgehen mit Key/ Value-Coding 144 Sichtbarkeit 36 statische 37 synthetisierte Hintergrundfelder 51 Werte ber Schlssel/Wert-Beziehungen abrufen 143 Zugriff 37 Zugriff auf, in Blocks 163 Feldzugriff ->-Operator 37 ->-Operator, Feldzugriff 28 Feldzugriff 28 @finally-Compilerdirektive 79, 188 -finishDecoding-Nachricht 157 -finishEncoding-Nachricht 157 -fobjc-arc-Compilerschalter 8, 202 -fobjc-runtime-Compilerschalter 203 -fobjc-exceptions-Compilerschalter 202 -fobjc-arc-exceptions-Compilerschalter 202 -forwardingTargetForSelector:-Nachricht 139, 175 -forwardInvocation:-Nachricht 139140, 175 Foundation-Funktionen NSLog() 6 Foundation.h-Header 9 -framework-Compilerschalter 8 Frameworks 9 Freigeben von Objekten 112

208 | Index

Funktionen C-Funktion fr Implementierung speichern 137 Funktionszeiger casten 135 vs. Blocks 18

I
id-Typ 11, 25 Klassenobjekte 118 IMP-Typ 130 imp_implementationWithBlock()Funktion 138 @implementation-Compilerdirektive 33, 188 Implementierungen 130 auf konkreten Funktionszeigertyp casten 131 cachen 135 dynamisch mit einem BlockObjekt stellen 138 dynamisch stellen 137 Nachrichten 41 Protokolle 69 Schnittstelle importieren 35 Importieren von Headerdateien 6 informelle Protokolle 71 -init-Methoden designierte Initialisierer 90 designierten Initialisierer aufrufen 93 Fehler in 94 Typ von self 43 Unterschied zu Konstruktoren 90 -init-Nachricht 27, 88, 176 -initForReadingWithData:-Nachricht 157 -initForWritingWithMutableData:-Nachricht 157 Initialierungsmethoden mit Parametern 27 Initialisierungsstruktur fr Klassen 94 +initialize-Nachricht 94, 176 -initWithCoder:-Nachricht 152 -initWithDomain:domain:code:userInfo:-Nachricht (NSError) 77 -initWithFormat:-Nachricht (NSString) 27

G
GCC (GNU Compiler Collection) 196 Getter, Namenskonvention 48 getter-Eigenschaftsattribut 55 globale Blocks 168 GNU-Runtime 197 GNUStep 198

H
Handler fr Ausnahmen 80 -hash-Nachricht 116, 176 Headerdateien 8 anwendungsspezifische Header 9 Framework-Header importieren 9 importieren 6, 8 NSKeyValueCoding.h-Header 143 objc/runtime.h-Header 115 Protokoll-Header importieren 70 System-Header 9 Umbrella-Header 9 Heap 101 -helpAnchor-Nachricht (NSError) 77 Hintergrundfelder von Eigenschaften abweichende Namen bei automatischer und expliziter Synthetisierung 51 deklarieren 48 Synthetisierung steuern 50

Index | 209

initWithName:reason:userInfo:-Nachricht (NSException) 83 +instanceMethodSignaturForSelector:-Nachricht 176 +instanceMethodForSelector:-Nachricht 135, 177 +instancesRespondToSelector:-Nachricht 177 -instancesRespondsToSelector:-Nachricht 123 Instanzmethoden 38 Typ von self 43 Instanznachrichten 38 Instanzvariablen siehe Felder @interface-Compilerdirektive 33, 188 +invocationWithMethodSignature-Nachricht 133 iOS Archivierungsuntersttzung 153 Auto-Release-Pools unter 109 Speicherverwaltung unter 102 -isEqual:-Nachricht 30, 117, 177 -isEqualToString:-Nachricht (NSString) 30 -isKindOfClass:-Nachricht 121, 177 -isMemberOfClass:-Nachricht 121, 177 -isProxy:-Nachricht 177 -isSubclassOfClass:-Nachricht 178 Ivars siehe Felder

K
Kategorien 63 Key/Value-Coding 143 Feldkapselung umgehen 144 Konformitt 143 Validierung 147 virtuelle Schlssel 146

Klassen 33, 94 Deklaration 34 Felder 35 Implementierung 35 Kategorien 63 Klassenname als Empfnger 118 Klassenobjekte 117 als NSObject-Instanz 118 erkennen 122 Instanzmethoden durch Klassenmethoden verdecken 118 Name abrufen 120 Metaklassen 119 Methoden 38 Nachrichten an Oberklasse senden 59 nachtrglich erweitern 63 Oberklassenschnittstelle in Deklaration einschlieen 34 Polymorphie 56 Rckgabewert von +classNachricht 119 statische Felder implementieren 37 untersttzte Nachrichten prfen 124 Vererbung 56 Verhalten in Abhngigkeit steuern von 121 Klassencluster 121 Klassenerweiterungen 65 private Nachrichten definieren 38 Klassenmethoden 38 Typ von self 43 Klassennachrichten 38 Klassennamen, Konventionen 12 Kompilieren von Code 7 Frameworks einbinden 10 Konstruktoren Fehlen in Objective-C 27 siehe auch -init-Methoden

210 | Index

Kontext, Blocks 161 kontextgebundene Blocks 169 Kopien von Objekten 95 tiefe und flache Kopien 98 KVC-Konformitt 143 KVC-Validierung 147

L
L-Postfix, Zahlliterale 17 Laufzeit Eigenschaftsimplementierung stellen 52 Laufzeitinformationen 115 Laufzeitumgebung alternative 197 Funktionsinformationen 123 Informationen zu Objekten 116 Klasseninformationen 117 Protokollinformationen 124 Literale 14 Arrays 15 Blocks 159 Dictionaries 16 String 6 Stringobjekte 15 Zahlen 16 LLVM 196 +load-Nachricht 178 -localizedDescription-Nachricht (NSError) 77 -localizedFailureReason-Nachricht (NSError) 77 -localizedRecoveryOptions-Nachricht (NSError) 77 -localizedRecoverySuggestionNachricht (NSError) 78

manuelle Implementierung von Eigenschaften 51 Messaging 127 Metaklassen 119 Klassenobjekte erkennen 122 Objekt abrufen 119 Methoden aufrufen 45 vs. Nachrichten 13 siehe auch Nachrichten 38 Methodenfamilien 41 Methodenberladung vs. Methodenfamilien 41 -methodForSelector:-Nachricht 135, 178 -methodSignatureForSelector:-Nachricht 133, 141, 178 Monkey-Patching 63 MRC 110 Auto-Release-Pools 114 Blocks 170 unbekannte Selektoren 129 -mutable...Value...For...-Nachrichten 179 +mutableCopyWithZone:-Nachricht 96, 180 -mutableCopy-Nachricht 96, 111, 179

N
Nachrichten 12 +alloc 27 +new 27 -enumerateObjectUsindBlock 30 -init 27 -initWithFormat: 27 -isEqual: 30 -isEqualToString: 30 an aktuelle Instanz senden 42 an Oberklasse senden 59 definieren 45 deklarieren 39

M
Mac OS X Auto-Release-Pools unter 109 Speicherverwaltung unter 102 main()-Funktion 5

Index | 211

dynamisch ausfhren 131, 134 dynamisch ausfhren mit NSInvocation-Objekt 132 Implementierung 41 Implementierung dynamisch stellen 137 Implementierungen cachen 135 in Kategorien berschreiben 64 in Protokollen deklarieren 67 Klassenname als Empfnger 118 mit einer variablen Anzahl an Parametern 46 mit NSInvocation-Objekt kapseln 140 mit Parametern 40 Name 39 ohne Parameter 40 optionale, aufrufen 70 optionale, in Protokollen 68 Parameter 39 Parameter angeben 12 private definieren 65 Prfung durch Compiler 128 Rckgabetyp deklarieren 39 schachteln 13 self-Empfnger 42 senden 45, 127 Sichtbarkeit 38 Syntax 28 untersttzte prfen 124 vs. Methoden 13 Weiterleitung 139 nachtrgliche Erweiterung von Klassen 63 -name-Nachricht (NSException) 83 Namensbereiche Kategorien 63 Namensbereiche fr Instanzvariablen und Nachrichten 46 Namenskonventionen Getter 48 Kategoriedateien 64

Setter 48 Speicherverwaltung 111 +new-Nachricht 27, 89, 180 Objekte besitzen 111 nicht abgefangene Ausnahmen, Handler fr 82 nil-terminierte Listen 47 nil-Wert aus Initialisierer zurckliefern, um Fehler anzuzeigen 94 nonatomic-Eigenschaftsattribut 52, 56 NSArray-Klasse 15 Objektindizierung 31 NSAutoreleasePool-Klasse 114 NSClassFromString()-Funktion 120 NSCoding-Protokoll 151 implementieren 153 NSCopying-Protokoll 97 Dictionaryschlssel und 16 implementieren 97 NSCopyObject()-Funktion 99 NSDictionary-Klasse 16 Klassenobjekte als Schlssel 119 Objektindizierung 31 NSError Warndialog 76 NSError-Klasse 74 Instanzen erstellen 75 Nachrichten 76 Objekte erstellen 77 NSException-Klasse Nachrichten 83 und andere Ausnahmeobjekte 80 NSFastEnumeration-Protokoll 30 NSInvocation-Klasse 132 Argument von -forwardInvocation:-Nachricht 140 Objekte ausfhren 141 NSKeyedArchiver-Klasse 154 Nachrichten 156

212 | Index

NSKeyedUnarchiver-Klasse 154 Nachrichten 157 NSKeyValueCoding-Protokoll 143 NSLog()-Funktion 6 NSMethodSignature-Klasse 133 NSMutableCopying-Protokoll 97 NSNumber-Klasse 16 Tagged Pointer und 17 NSObject-Klasse 85 Instanzmethoden durch Klassenmethoden verdecken 118 Klassenobjekte 118 Laufzeitinformationen 115 NSObject-Protokoll 69, 115 NSSelectorFromString()-Funktion 130 NSSet-Klasse Objektindizierung 31 NSSetUncaughtExceptionHandler()-Funktion 82 NSString-Klasse Verhalten von -copy-Nachricht 99 NSStringFromClass()-Funktion 120

O
Oberklasse Initialisierer aufrufen 92 Nachrichten senden an 59 Schnittstelle in Unterklassendeklaration einschlieen 34 objc/runtime.h-Header 115 objc_getClass()-Funktion 120 objc_lookUpClass()-Funktion 120 objc_msgSend()-Funktion 134 object_getClass()-Funktion 119 Objective-C-Laufzeitumgebung 115 Objective-C-Stringliterale 6 Objekte 23, 87 archivieren 151

Ausnahmeobjekte 80 besitzen von 111 Blocks als 161 downcast 61 Eigenschaftszugriff 28 enumerieren 30 erstellen 11, 27 Felder 28 Feldzugriff 28 freigeben 112 indizieren 30 Ivarwerte ber Schlssel/WertBeziehungen abrufen 143 Klassenobjekte 117 kopieren 95 Literaldeklarationen 14 manipulieren in Blocks 166 mit Laufzeittyp erstellen 45 referenzieren 11 schlsselbasierte Interaktion mit 144 umwandeln in Strings 116 untersttzte Nachrichten prfen 124 Upcast 61 Variablen deklarieren 23 vergleichen 29, 117 vernichten 99 Weiterleitungsobjekt stellen 139 ObjFW 199 Operatoren &-Adressoperator 24 *-Dereferenzierungsoperator 24 ==-Vergleichsoperator 29 ->-Operator 37 Feldzugriff 28 @optional-Compilerdirektive 68, 189 optionale Nachrichten aufrufen 70 optionale Protokollnachrichten 68

Index | 213

P
@package-Compilerdirektive 36, 189 Parameter 12 deklarieren 40 fr Ausnahmeverarbeitung 80 Nachrichten 39 Nachrichten mit variabler Anzahl 46 von Schnittstelle abweichender Typ in Implementierung 41 -performSelector-Nachrichten 131, 180 Polymorphie 56 in Objective-C 60 Protokolltyp 71 @private-Compilerdirektive 36, 190 Properties siehe Eigenschaften @property-Compilerdirektive 49, 190 @protected-Compilerdirektive 36, 190 @protocol-Compilerdirektive 67, 191 Protokolle 67 als Einschrnkung auf Variablentyp 71 informelle 71 bernehmen 69 @public-Compilerdirektive 36 Punktnotation 28, 52

Reference Counting siehe Speicherverwaltung Referenzparameter NSError-Referenz 74 Referenzzyklen durchbrechen 107 -release-Nachricht 110, 180 @required-Compilerdirektive 68, 191 +resolveClassMethod:-Nachricht 137, 181 +resolveInstanceMethod:-Nachricht 137, 181 -respondsToSelector:-Nachricht 70, 123, 181 Beispiel 139 retain-Eigenschaftsattribut 56 -retain-Nachricht 110, 181 -retainCount-Nachricht 110 -retainCount-Nachricht 182 Rckgabetyp Blocks 159 von Blocks explizit angeben 160 von Schnittstelle abweichender in Implementierung 41

S
Schachteln von Nachrichten 13 Schlssel/Wert-Beziehungen 143 virtuelle Schlssel 146 schlsselbasierte Archive 154 Archivierungsmethoden 154 vs. sequentielle Archive 153 Schlsselpfade 145 Schlsselpfadoperatoren 148 schnelle Enumeration 30 Schnittstellen in Implementierungsdatei importieren 35 siehe auch Klassen 34 schwache Referenzen 105 Referenzen durchbrechen 107 von Nachrichten deklarieren 39 SEL-Typ 130

R
+raise:...-Nachrichten (NSException) 83 -raise-Nachricht (NSException) 84 readonly-Eigenschaftsattribut 55 readwrite-Eigenschaftsattribut 55 -reason-Nachricht (NSException) 84 -recoveryAttempter-Nachricht (NSError) 78

214 | Index

@selector()-Compilerdirektive 130, 192 Selektoren 129 dynamisch ausfhren 131 Implementierung abrufen 135 nicht konstante 132 unbekannte 128 self-Empfnger 42 als Funktionsparameter 131 als Klasse von Klassenobjekten 119 Feldzugriff 37 in Initialsierungsmethode setzen 92 Typ in Instanzmethoden 43 Typ in Klassenmethoden 43 Verhalten in -init-Methoden 43 zirkulre Referenzen auf 164 Zugriff in Blocks 161 Senden von Nachrichten 45 sequentielle Archive 155 vs. schlsselbasierte Archive 153 Serialisierung vs. Archivierung 151 -setNilValueForKey:-Nachricht 182 Setter, Namenskonventionen 48 setter-Eigenschaftsattribut 55 -setValue:forUndefinedKey:-Nachricht 182 -setValue:forKey...-Nachrichten 182 -setValuesForKeysWithDictionary:-Nachricht 182 Sichtbarkeit Felder 36 Nachrichten 38 private Klassenelemente definieren 65 Speicherort Blocks 168 Speicherverwaltung 101 ARC 104 Blocks 171

Eigenschaftsattribute 55 MRC 110 Stack 101 starke Referenzen 105 statische Referenzen, Feld- und Eigenschaftszugriff 28 Stringformatangaben 6 Stringobjekte -fconstant-string-class-Compilerschalter 202 Literale 15 Objekte umwandeln in 116 vergleichen 30 _ _strong-Variablenqualifizierer 107 strong-Eigenschaftsattribut 56, 106 super-Empfnger 59 vs. -superclass-Nachricht 59 -superclass-Nachricht 117, 182 +superclass-Nachricht 182 @synthesize-Compilerdirektive 50, 192 Synthetisierung automatische 50 explizite 50

T
Tagged Pointer 17 @throw-Compilerdirektive 79, 192 tiefe und flache Kopien 98 @try-Compilerdirektive 79, 193 try/catch/finally-Ausnahmeverarbeitung 78 Typangaben Variablentyp und Protokolle 71 Typcodes 137 typedef-Operator 160 Typen Class 25 statische vs. dynamische 25

Index | 215

Typumwandlungen Blocks 161 Funktionszeiger casten 131, 135

U
U-Postfix, Zahlliterale 17 bernahme von Protokollen 69 berschreiben von Nachrichten in Kategorien 64 Umbrella-Header 9 +unarchiveObjectWithFile:-Nachricht 157 +unarchiveObjectWithData:-Nachricht 157 _ _unsafe_unretained-Variablenqualifizierer 108 Upcast 61 -userInfo-Nachricht (NSError) 78 -userInfo-Nachricht (NSException) 84

Variablenqualifizierer _ _block 167 ARC 107 Vererbung 56 Felder 37 Protokolle 68 Vergleichen von Objekten 29, 117 Verhalten vom Klassentyp abhngig machen 121 Vernichten von Objekten 99 virtuelle Schlsselbeziehungen 146 Vorwrtsdeklaration von Protokollen 69

W
Warndialog auf Basis eines NSError-Objekts erstellen 76 _ _weak-Variablenqualifizierer 107 weak-Eigenschaftsattribut 56, 106 Weiterleitung von Nachrichten 139 Wrapper-Ausdrcke 18

V
-valueForKey:-Nachricht 144 -valueForKey...-Nachrichten 182 -valueForUndefinedKey:-Nachricht 183 Varargs-Methoden 46 Variablen Blocks 19 C-Zeiger und 24 einfangen in Blocks 165 mit Protokolltyp einschrnken 71 statisch typisierte 26 Zugriff in Blocks 161 Variablennamen, Konventionen 12

X
Xcode 6 Frameworks einbinden 10

Z
Zahlen Literale 16 Wrapper-Ausdrcke 18 Zeiger siehe C-Zeiger zirkulre Referenzen bei Blocks 164

216 | Index

Das könnte Ihnen auch gefallen