Fertigstellungsgrad:
40%
Wir wollen anfangen, selbst ein Programm zu schreiben. Dazu setzen wir uns einfach mal an den Computer und fangen an.
Zunächst müssen Sie Xcode mit Ihrem Projekt gestartet haben. Wenn Sie Xcode bereits beendet haben, so ist es am einfachsten es zu starten, indem Sie im Verzeichnis /Developer/Applications/ im Finder das Programm Xcode.app starten. Sollten das Programm oder das Verzeichnis nicht existieren, müssen sie wohl zunächst die Developer Tools von der Apple Homepage herunterladen (siehe unter Links) und installieren.
Objective-C ist DIE Sprache für alle Mac OS X Computer. Der Grund ist einfach, dass ein Großteil des Betriebsystems und des Cocoa-Frameworks, also auch die Fenster und alles, was sie auf dem Monitor ihres Apples sehen, in dieser Sprache formuliert ist. Objective-C fußt auf C. C ist entwickelt worden um in den 1970ern UNIX zu entwickeln und bietet viele Vorteile gegenüber dem Programmieren in Maschinensprache. C ist sehr schnell und bietet einen hardwarenahen Zugriff auf die Ressourcen des Rechners. für den Kernel und die Treiberentwicklung ist es auch heute noch unentbehrlich. Durch seinen prozeduralen Ansatz kommt C jedoch bei komplexen Projekten an seine natürlichen Grenzen. In der Windows- und der alten Unixwelt entstanden C++ und Java als objektorientierten Sprachen, um diese Grenze zu erweitern. Unter Mac OS X - man ahnt es - war dies Objective-C.
Ein Computerprogramm besteht aus einer Vielzahl von Anweisungen. Bei MacOS X Tiger sollen es um die 86 Millionen Zeilen Code sein, wie Steve Jobs zur WWDC 2006 verkündete. Das untenstehende Programm hat 5 SLOC (Source Lines Of Code). Man darf den SLOC allerdings nur als einen Nährwert für die Komplexität eines Programms sehen, da die Aussagekraft einer Zeile variieren kann.
Diese Anweisungen kann man in Funktionen zusammenfassen. Der Ausdruck ist nicht zufällig gewählt. Er stammt aus der Mathematik und hat dort eine ähnliche Bedeutung. Zum einen dient er der Strukturierung eines Programms, indem man einzelne Anweisungen sinnvoll bündelt, zum anderen vermeidet man so Redundanzen, man erspart sich also das Wiederholen eines Anweisungsblockes, indem man ihn durch den Aufruf einer Funktion die diese Anweisungen enthält, ersetzt.
Die letzte Form der Strukturierung eines Programms ist die Klasse. In ihr werden artverwandte Funktionen und Variablen zusammengefasst und bilden so eine nach aussen geschlossene Einheit. In der objektorientierten Programmierung wird jedes Problem in Klassen (auch Objekte genannt) zerlegt, die miteinander über Schnittstellen Informationen austauschen.
Nach dem Starten von Xcode klicken sie doppelt auf Create new Xcode project, dann auf Commandline Tool schließlich speichern sie das Projekt unter dem Namen simonsays und erstellen es durch das Anwählen von Save. Gehen Sie im Projektfenster in die Gruppe Source und dort mit einem Doppelklick auf main.c. Es öffnet sich ein Fenster, indem sie einige Programmzeilen sehen. Diese sehen in etwa wie folgt aus:
#include <stdio.h> int main (int argc, const char * argv[]) { // insert code here... printf("Hello, World!\n"); return 0; }Was bedeuten das? Zeilen, die mit einem
// beginnen sind so genannte Kommentare. Sie werden vom Compiler überlesen, haben keine Bedeutung für das Programm und können enthalten, was Sie wollen, insbesondere Erläuterungen Ihres Programms. Sie sollten sich früh angewöhnen, Ihr Programm zu kommentieren, wenn Sie es schreiben. Sie vergessen nämlich später, was Sie genau gewollt haben und machen sich die Entwicklung so unnötig schwer.
Die main Zeile und die letzte Zeile mit } bilden eine Funktion, in ihr befinden sich zwei Anweisungen. Einmal printf die einen Text als Parameter übernimmt und selbst wieder eine Funktion ist. return 0 ist der Rückgabewert der Funktion main und bedeutet soviel wie „beim Verlassen des Programms war alles okay liebes Betriebssytem…“.
Viel neues, wenig Erklärung. Nehmen sie es zunächst einmal so hin, lassen sie es auf sich wirken und am Ende des Textes kehren sie zurück und werden sehen, was sie alles gelernt haben, wenn sie das Programm noch einmal betrachten und verstehen werden was hier eigentlich vor sich geht.
Ähnlich wie die Funktion stammt auch die Variable aus der Mathematik. Ganz allgemein wird mit einer Variablen dem Programm ein bestimmter Teil des Speichers zugeteilt. Dies hat eine vielleicht nicht auf den ersten Blick ersichtliche Konsequenz: Variablen sind in ihrer Größe begrenzt. Rationale und irrationale Zahlen lassen sich so nur mit einer begrenzten Genauigkeit darstellen, ausserdem sind die Zahlen auch nach oben und unten begrenzt. Hier haben sich im Laufe der Zeit feste Wertebereiche gebildet, die für den Alltag zumeist ausreichend sind.
Jede Vaiablendefinition hat im Prinzip die Form (Syntax):
<datentyp> <variablenname> [<=> <Wert>] <;>
| Art | max. Wert | min. Wert | Bitbreite | |
|---|---|---|---|---|
int | ganze Zahlen | 32767 | -32767 | 16 |
long | ganze Zahlen | 2147483647 | -2147483647 | 32 |
short | ganze Zahlen | 32767 | -32767 | 16 |
char | ASCII-Zeichen | 256 | 0 | 8 |
float | Kommazahlen | 3,403·1038 | 1,401·10-45 | 32 |
void* | Speicheradressen |
WICHTIG Fließkommezahlen (float), werden in C und Objective-C wie im Englischen üblich, nicht mit einem Komma, sondern einem Punkt geschrieben. Ausserdem können sich die Werte in Abhängigkeit von ihrem Computermodell unterscheiden und sind auch von Betriebssystem zu Betriebssystem unterschiedlich.
#include <stdio.h> #include <limits.h> #include <float.h> int main (int argc, const char * argv[]) { // Den Speicher anfordern int integer; char character; float floating_point; // Mit Werten belegen integer = 65; character = 'A'; floating_point = 1.0; // Den Inhalt der Variabeln auf dem Bildschirm ausgeben printf("\n\n.:DIE WERTE DER VARIABELN:.\n\n"); printf("char: %c\n", character); printf("integer: %i\n", integer); printf("float: %f\n", floating_point); printf("\n\n.:DIE MAXIMAL UND MINIMALWERTEWERTE EINZELNER DATENTYPEN:.\n\n"); printf("char: %d %d\n",CHAR_MAX,CHAR_MIN); printf("integer: %d %d\n",INT_MAX,INT_MIN); printf("float: %f %f\n", FLT_MAX, FLT_MIN); return 0; }Bitte beachten Sie, dass
char immer nur ein Zeichen speichert. Den Wert schreiben wir mit Hochkommata eingegrenzt. Experimentieren sie ein wenig! Versuchen sie einmal den Wert 'A' von character durch den Integer 42 zu ersetzen und schauen sie was beim Ausführen des Programms passiert.
Eine Konstante ist ein Platzhalter für eine Wert.
Die allgemeine Syntax schaut so aus:
<const> [<datentyp>|<int>] <konstantenname> <=> <Wert> <;>
Also Beispiel dient folgender Code. Hier weisen wir Konstanten Werte zu die nicht mehr geändert werden können und geben sie auf dem Bildschirm aus.
#include <stdio.h> int main (int argc, const char * argv[]) { // Konstanten const int PI = 3.141592...; const int DAUMEN = 2.0; // Ausgabe printf("PI mal DAUMEN = %i", PI*DAUMEN); ...
Anders als Konstanten dienen Compilerdirektiven der Steuerung des Präprozessors. Dies ist ein Programm, welches vor der eigentlichen Übersetzung des Codes den Text durchläuft. Genaueres entnimmt man bitte der einschlägigen Literatur. Wichtig ist für uns nur, dass eine solche Direktive mit einem # beginnt. Im Falle von #define bedeutet dies, dass der Präprozessor bestimmte Zeichen durch andere ersetzt.
<#define> <ZEICHENKETTE> [<Ausdruck>]
Unser obiges Programm sähe jetzt so aus:
#include <stdio.h> // Hinzugefuegt: #define PIMALDAUMEN 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679 * 2 int main (int argc, const char * argv[]) { const int PI = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679; const int DAUMEN = 2.0; // Hinzugefuegt: printf("PI mal DAUMEN = %i", PI*DAUMEN); printf("\nPIMALDAUMEN = %f",PIMALDAUMEN); return 0; }
TODO
Eine Schleife nutzt man in der Programmierung, um einen Vorgang bis zum Erreichen einer bestimmten Endbedingung auszuführen.
#include <stdio.h> int main (int argc, const char * argv[]) { int i = 0; while (i < 10) { printf("%i. Wiederholung.\n",i++); } return 0; }
In diesem Programm haben wir einige Neuerungen, die unbedingt einer Erläuterung bedürfen. Wir gehen Zeilenweise vor.
#include <stdio.h>
Dies ist eine sogenannte Zuweisung. Die Variable zaehler werhält den Wert 0. Diese 0 ist in ihr gespeichert. Geben Sie dies ein. Danach bauen wir unsere Schleife auf. Wir überlegen uns folgendes: Wir erhöhen solange den Wert von zaehler, bis er 5 erreicht hat. Dann haben wir diesen Arbeitsschritt fünfmal wiederholt. Das schreiben wir hin:
while( zaehler < 5 ) { zaehler = zahler + 1; }Was bedeutet dies? while sagt uns, dass wir die nachfolgende Sequenz wiederholen sollen, wie die Bedingung in den Klammern vorliegt. Die Bedingung lautet „Zaehler ist kleiner als 5“. Also wird die nachfolgende Seuqenz wiederholt, bis 5 erreicht ist. Gut so.
Die Anweisung, die wir wiederholen lautet:
zaehler = zahler + 1;Es wird also der Wert von zahler genommen, 1 hinzuaddiert und das Ergebnis wieder in zaehler gespeichert. Wenn wir also am Anfang zaehler auf 0 gesetzt hatten, so wird er nach dieser Anweisung 1 sein. Weil dann die Bedingung noch erfüllt ist (1 ist kleiner als 5), wird die Anweisung erneut ausgeführt. Aus 1 wird jetzt 2, dann 3, dann 4, dann 5. Erst bei 5 ist die Bedingung nicht mehr erfüllt und die Anweisung wird nicht mehr wiederholt.
Eine kürzere Schreibweise für das Erhöhen ist
zaehler++;
Wenn Sie einer Variablen ein ++ hinzufügen, wird sie immer um 1 erhöht.
Gut, das ist schön. Nur haben wir davon rein gar nichts. Deshalb erweitern wir jetzt die Schleife:
while( zaehler < 5 ) { NSLog( @"Wiederhole mich!" ); zaehler++; }Was geschieht nun? Da sich zwei Anweisungen in den geschweiften Klammern befinden, werden beide Anweisungen wiederholt solange die Bedingung erfüllt ist. Es wird also nicht nur zaehler jedesmal um 1 erhöht sondern es wird auch der Text jedesmal ausgegeben. Das Ergebnis ist, dass dieser Text fünfmal auf dem Bildschirm erscheint, wenn Sie das Programm übersetzen und ausführen.
Nun haben wir es also mit einer einfachen Zählschleife zu tun. Die kann man in Objective-C aber auch einfacher formulieren. Wenn sie aufgepasst haben, so bemerkten Sie, dass eine Zählschleife aus drei Dingen bestand:
i = 0;zaehler < 5zahler++;
Alle drei Dinge kann man in einer Zeile formulieren, wenn man for benutzt:
for( zaehler = 0; zaehler < 5; zaehler++ ) { NSLog( @"Wiederhole mich" ); }Der erste Teil in der Klammer ist die Initialisierung, der zweite die Bedingung, der dritte die Erhöhung. Es spielt übrigens hier keine Rolle, ob Sie von 0 bis 5 zählen oder von 1 bis 6 oder von 7 herunter auf 2. Im letzten Falle würden Sie formulieren:
for( zaehler = 7; zaehler > 2; zaehler-- ) { NSLog( @"Wiederhole mich" ); }Beachten Sie hierbei bitte, dass natürlich die Bedingung umgekehrt lauten muss und zaehler mit – um 1 vermindert wird.
Nach all diesen Überlegungen können wir also eine Funktion hallo() schreiben, die einen Text fünfmal ausgibt:
void hallo() { // Unsere Zählvariable definieren int zaehler; // Die Schleife für die Wiederholungen for( zaehler = 0; zaehler < 5; zaehler++ ) { NSLog( @"Wiederhole mich" ); } }An unserer Funktion
main ändert sich nichts. Dort wird einfach weiterhin hallo() aufgerufen. Nur macht hallo() jetzt etwas anderes. Erinnern Sie sich an das Prinzip der Kapselung?
Geben Sie dies so ein und starten Sie das Programm. Der Text „Wiederhole mich“ müsste fünfmal im Run Log stehen.
Nun beenden Sie Ihr Program wieder und starten es erneut im Debugger. Wenn Sie jetzt mit Step Into in die Funktion hallo() gehen, werden die Anweisungen in der Schleife fünfmal wiederholt. Achten Sie jetzt bitte dabei auf das untere linke Fenster. Dort erscheint in der Gruppe Locals der Name zaehler und Sie können beobachten, wie bei jeder Wiederholung der Schleife sich der Wert um 1 erhöht, bis er 5 erreicht hat.
TODO
TODO
Als nächstes widmen wir uns mal der bedingten Anweisung. Unser hallo() kann theoretisch auch negative Werte wie -5 bekommen, da int auch eine negative Ganzzahl sein kann. Der Aufruf
hallo( -5 );macht aber gar keinen Sinn, da man nichts -5 Male ausführen kann. Unser Parameter muss also mindestens 1 sein. Es macht auch keinen Sinn, an hallo() sehr große Zahlen zu übergeben. Mehr als 5mal sollte nicht gegrüßt werden. Das ist ja affig.
Wir können jetzt in hallo() dafür sorgen, dass dies berücksichtigt wird. Wir ändern hallo() wie folgt:
void hallo( int anzahl ) { // Bei einer negativen Zahl eine Fehlermeldung ausgeben. if( anzahl < 1 ) { NSLog( @"Dummes Zeugs." ); } else { // Bei einer großen Zahl, auf 5 begrenzen if( anzahl > 5 ) { anzahl = 5; } // if anzahl // Jetzt endlich drucken for( ; anzahl > 0; anzahl-- ) { NSLog( @"Hallo" ); } // for } // if anzahl }Das sieht jetzt schon ziemlich wüst aus. Sie sehen bereits, dass ich bei der geschlossenen Klammer mit
// einen Kommentar eingefügt habe. Wenn Programme komplizierter werden, ist es eine gute Idee dies zu tun, da man so gleich sieht, wozu die geschlossene Klammer gehört. Da es sich um einen Kommentar handelt, können Sie dort einen Hinweis nach Ihren persönlichen Gusto unterbringen. Noch ein Tipp: Wenn sie mit dem Cursor über eine geschlossene Klammer fahren, lässt Xcode kurz die entsprechende geöffnete blau aufblinken. Versuchen Sie es einmal!
Schauen wir es und genauer an:
Ganz oben steht eine Abfrage nach der Anzahl. if() bedeutet, dass die Anweisungen in den folgenden Zeilen nur ausgeführt werden, wenn die Bedingung in den Klammern von if() wahr ist. Wenn also jemand hallo( -3 ); probiert, so ist anzahl = -3. Da dann die Bedingung anzahl < 1 wahr ist, wird die Zeile zwischen den folgenden geschweiften Klammern ausgeführt. Es wird also der Text „Dummes Zeugs“ gedruckt.
Gut. Ist der Ausdruck nicht wahr, hat also jemand mindestens 1 als Parameter eingegeben, so werden die Anweisungen ausgeführt, die in der geschweiften Klammer nach dem else stehen. Dies sind also die Anweisungen:
// Bei einer großen Zahl, auf 5 begrenzen if( anzahl > 5 ) { anzahl = 5; } // if anzahl // Jetzt endlich drucken for( ; anzahl > 0; anzahl-- ) { NSLog( @"Hallo" ); } // forHier wird zunächst wieder eine Bedingung abgefragt, nämlich, ob
anzahl größer als 5 ist. Ist dies der Fall, so wird anzahl auf 5 gesetzt. Ist dies nicht der Fall, ist also anzahl kleiner als 5, so wird nichts gemacht, da es kein else gibt.
Hiernach haben wir unsere bekannte Schleife, die dann mit entsprechender Anzahl den Text „Hallo!“ ausdruckt. Probieren Sie in Main Aufrufe von hallo() mit verschiedenen Zahlen. Beobachten Sie die Ausgabe in Run Log und starten Sie das Programm im Debugger.
Funktionen dienen der Strukturierung von Quellecodes und erlauben die Nutzung von Rekursion. In C/ObjC wird nicht, wie in Pascal, zwischen procedure und function unterschieden. Das heißt es gibt keinen Unterschied zwischen Aufrufen mit und ohne Funktionsparameter.
TODO
Nach diesem Funktionskopf folgt der Funktionsrumpf. Dies ist der Text, der in den geschweiften Klammern eingeschlossen ist. In C stehen alle Anweisungen in irgendeiner Funktion. Hierin befinden sich die einzelnen Anweisungen, also Arbeitsschritte. Wir finden hier nur eine Anweisung:
return NSApplicationMain(argc, (const char **) argv);Ich bitte Sie, dies nicht verstehen zu wollen. Bitte beachten Sie aber, dass die Anweisung durch ein Semikolon abgeschlossen ist. Dies ist immer so! Wir geben jetzt einfach davor selbst einen Arbeitsschritt ein. Tippen Sie vor der Zeile
return NSAppl… eine Zeile
NSLog( @"Halli - Hallo - Hallöchen!" );ein. Es ist übrigens für C eigentlich gleichgültig, ob in ener Zeile eine oder mehrere Anweisungen stehen. Die Anweisung wird a bereits mit
; abgeschlossen. Es ist aber so Sitte, an die Sie sich halten sollten.
Nun, auch unsere Anweisung endet mit einem ;. Das ist schon einmal gut. Außerdem enthält sie die Benutzung einer Funktion, wir sagen: Einen Funktionsaufruf. Erkennen kann man das an der runden Klammer nach dem Namen. Die benutzte Funktion heißt NSLog. Sie ist in dem Cocoa Framework bereits enthalten. In den Klammern kommt, wie in der Mathematik, der Parameter. In diesem Falle ist es ganz unmathematisch ein Text. Ohne weiter darauf einzugehen, sei hier erwähnt, dass man in Objective-C Klartexte eingeben kann, indem man sie mit @“ und “ einschließt. Die Funktion NSLog() macht nichts anderes, als den Text in den Klammern auszugeben. Das probieren wir jetzt aus! Ihr Prorammtext müsste jetzt wie folgt aussehen:
// // main.m // Start // // Created by IHRNAME on 24.12.04. // Copyright __MyCompanyName__ 2005. All rights reserved. // #import <Cocoa/Cocoa.h> int main(int argc, char *argv[]) { NSLog( @"Halli - Hallo - Hallöchen!" ); return NSApplicationMain(argc, (const char **) argv); }Schließen Sie wieder das Fenster und klicken Sie auf
Build and Go. Etwaige Nachfragen, ob die Änderungen gespeichert werden sollen, bejahen Sie natürlich.
Das Programm startet jetzt und es öffnet sich wieder neben Ihrem Programmfenster ein weiteres Fenster Run Log. Schauen Sie es sich mal an. Die letzten Zeilen lauten:
[Session started at 2004-12-28 15:25:21 +0100.] 2004-12-28 14:25:22.249 Start[5667] Halli - Hallo - Hallöchen!Aha! Da ist es also. Der Computer hat den eingegebenen Arbeitsschritt ausgeführt und den Text gedruckt. Ach ja, da steht ja noch viel mehr. Gut, schauen wir uns das eben mal schnell an:
Die erste Zeile bedeutet, dass Ihr Program zu dem angebenen Zeitpunkt gestartet wurde. Dies ist natürlich ein anderer als hier in dem Artikel. Ich kann ja nicht wissen, wann Sie gerade vor dem Computer sitzen. Zusätzlich zu dem eingebenen Text druckt NSLog() auch immer noch den Zeitpunkt des Ausdruckes hinzu. Dies ist der Anfang der zweiten Zeile. Start[5667] ist Ihr Programm und die Programmzeile. (Ja, es ist wirklich 5667, weil Sie ja mit #import <Cocoa/Cocoa.h> ganz viel von Cocoa importiert haben, was für den Computer vor Ihrem Programm steht.)
Wenn Sie Ihr Programm mit Quit beenden, dann wird noch die Zeile
Exectuable "Start" has exited with status 0
hinzugefügt. Auch hier sei ganz kurz darauf eingegangen. Wie Sie bereits wissen ist main eine Funktion, die eine ganze Zahl als Ergebnis liefert. Auf welchem Wege auch immer, hat ihr main als Ergebnis 0 geliefert. Dieses wird im Run Log angeziegt. Übrigens, Status 0 bedeutet: Alles ist in Ordnung. Es ist also eine gute Nachricht.
Jetzt machen wir uns mal selbst eine Funktion.
Oberhalb von int main fügen Sie bitte folgenden Programmtext ein:
void hallo() { NSLog( @"Hallo - ich bin eine Funktion" ); }Wir machen hier also ein Unterprogramm in der Form einer Funktion. Die Funktion heißt
hallo. Da ihre runden Klammern leer sind, erwartet Sie keine Parameter. Vor hallo steht void. Dies ist also das Ergebnis unserer Funktion. Da unsere Funktion aber gar nichts errechnet, sondern nur einen Text ausdruckt, hat sie kein Ergebnis. void bedeutet genau dies: Es gibt kein Ergebnis!
In main löschen Sie jetzt den alten Aufruf von NSLog() und schreiben stattdessen:
NSLog( @"Hier ist main" ); hallo(); NSLog( @"Und wieder main" );Hiermit schreiben Sie einen Text, dann rufen Sie die soeben programmierte Funktion
hallo() auf und schreiben schlussendlich wieder einen Text.
Übersetzen und starten Sie das Programm wieder. Der Text müsste gedruckt werden.
Was Sie beachten müssen. In main wird zunächst ein Text gedruckt, dann wird hallo aufgerufen und wenn dieses fertig ist, geht es wieder in main weiter mit dem nächsten NSLog(). Nachdem also eine Funktion fertig ist, geht es wieder in dem Aufrufer weiter! Das ist wie bei dem Rezept: Wenn Sie mit dem Teig fertig sind, geht es wieder im Hauptrezept weiter. Deshalb spricht man auch von einem Unterprogramm.
Jetzt werden wir auch zum erstenmal den Debugger bemühen, von dem schon häufiger die Rede war. Öffnen Sie noch einmal den Programmtext und klicken Sie auf die hellgraue Leiste am Rand. Und zwar in der Höhe der Zeile „int main […]“. Dort erscheint jetzt eine Art grauer Pfeil. Sie haben einen sogenannten Breakpoint gesetzt. An dieser Stelle wird unser Programm demnächst angehalten. Anstaat auf Build and Go klicken Sie jetzt im Projektfenster auf Build and Debug. Es öffnet sich ein Fenster, welches Start - Debugger heißt. Dann dauert es eine ganze Weile, bis etwas passiert. Das ist normal. Aber plötzlich erscheint in dem Fenster rechts ihr Programmcode mit einer rot unterlegten Zeile. Das Programm hatt an dieser Stelle angehalten. Wenn Sie jetzt in der Toolbar auf Step Into klicken, können Sie verfolgen, wie Ihr Programm Zeile für Zeile ausgeführt wird. Dies ist sehr nützlich, wenn Sie kompliziertere Programme schreiben und Sie nicht wissen, wieso Ihr Programm etwas anderes macht als Sie wollten - das wird passieren!
Nun wundern Sie sich vielleicht, dass Sie gar kein Run Log haben, in dem der von Ihnen ausgegebene Text steht. Diesen gibt es auch nicht. Sie können aber im Menü Debug den Menüpunkt Concole Log auswählen und sehen dort in rot den Text. Dazu erscheint noch jede Menge anderer Text, der sie hier nicht interessiert.
Ich hatte oben erläutert, dass Funktionen Parameter nehmen und Ergebnisse zurückliefern können. Wir hatten uns dann aber eine Funktion hallo() programmiert, die gar keine Parameter verwendete und auch kein Ergebnis zurücklieferte. Das wollen wir jetzt ändern.
Wir ändern unsere Funktion hallo() jetzt so, dass man bestimmen kann, wie häufig ein Text ausgegeben wird. Dazu ändern Sie sie wie folgt:
void hallo( int anzahl ) { for( ; anzahl > 0; anzahl-- ) { NSLog( @"Hallo" ); } }Was bedeutet dies? Nun, zunächst sehen wir im Funktionskopf, dass wir in den Klammern so etwas wie eine Variable definieren. Wir machen das nicht wirklich, da diese Vaiable nicht angelegt wird, sondern wir gleich in main einfach in den Aufruf von
hallo() eine Zahl hereinschreiben. In hallo können wir diese Zahl mit der Variablen anzahl abfragen, so, als ob wir ihr selbst einen Wert zugewiesen hätten. Das bedeutet, dass bei dem Aufruf
hallo( 5 );
in main in hallo() die Variable anzahl den Wert 5 hat, als ob wir selbst anzahl = 5; am Anfang von hallo() geschrieben hätten. Steht in main
hallo( 3 );so ist es so, als ob wir am Anfang von
hallo() anzahl = 3; geschrieben hätten. Mit anderen Worten: Nicht mehr hallo() bestimmt die Anzahl der Wiederholungen, sondern derjenige, der hallo() benutzt. Das werden wir sehr häufig verwenden!
In der for-Schleife wird daher am Anfang auch nicht mehr die Anzahl gesetzt. Sie ist ja bereits gesetzt. Daher steht vor dem Semikolon nichts.
Der Rest funktioniert so wie gehabt.
Ändern Sie jetzt main wie folgt:
int main( int argc, char* argv[] ) { NSLog( @"Dreimal:" ); hallo( 3 ); NSLog( @"Zweimal:" ); hallo( 2 ); }Übersetzen Sie das Programm und starten es. Verstehen Sie die Ausgabe im Run Log?
Starten Sie das Programm erneut im Debugger und schauen Sie, wie sich die Variable anzahl ändert.
Was sie jetzt gelernt haben, war der Mikrokosmos der Programmierung, sozusagen die Atome eines Programes. Diese merken Sie sich bitte. Aber nun werden wir eine ganz andere Perspektive lernen. Das Große!
Wenn Sie meinen, dass Sie mit dem bereits Gelernten noch nicht sicher umgehen können, haben Sie mutmaßlich Recht. Das ist nicht schlimm. Übung macht den Meister!
Objective C gehört zu den sogenannten objekt-orientierten Programmiersprachen. Diese haben eine besondere Sicht der Welt: Alles sind Objekte. Nehmen Sie eben den Kopf hoch und schauen Sie durch ihr Zimmer. Sie werden zahlreiche Objekte sehen: Fenster, den Boden, einen Tisch, den Stuhl auf dem Sie sitzen, einen Bleistift, mit dem Sie sich Notizen machen usw. usf. Die Welt ist voll von Objekten!
Jedem diesem Objekt können zwei Dinge zugeordnet werden. Nämlich die Eigenschaften des Objektes und seine Fähigkeiten. Nehmen Sie den Bleistift. Man könnte vielleicht einen seiner Eigenschaften einordnen als stumpf bis spitz. Diese Eigenschaft verändert sich natürlich ständig. Dann gibt es einen Härtegrad. Bevorzugen Sie auch weiche Bleistifte? Auch eine Eigenschaft, allerdings ein unveränderlicher. Das macht nichts. Eigenschaft ist Eigenschaft.
Sie können mit Ihrem Bleistift auch Dinge machen. Zum Beispiel schreiben. Haben Sie aber etwa einen Bleistift mit Radiergummi auf der Rückseite, so können sie auch mit ihm radieren. Sie können mit ihm klappern, wenn sie nervös sind. Also: Mit dem Objekt Bleistift kann man Dinge anstellen.
Schön. So weit so gut. Wenn wir jetzt Programmieren, dann erzeugen wir solche Objekte und verbinden sie. Wir programmieren Objekte aber nicht! Dazu jetzt mehr:
Wir sahen bei den Variablen, dass sie einen gewissen Typen haben, ganze Zahl etwa. So geht es auch Objekten. Ihr Bleistift ist ein konkretes Objekt. Ohne jetzt phänomenologische Ontologie zu betreiben. Sie wissen das, weil seine Fähigkeiten so sind wie die eines Bleistiftes. Auch dieser konkrete Bleistift hat daher so etwas wie einen Typen. Wen wir also etwa eine Variable haben wollen, die ein Objekt enthält (Das ist eigentlich nicht genau richtig, wie wir noch sehen werden.), so muss diese Variable von dem Typen sein, der dem Objekt entspricht. Man nennt dies Klasse. Wir schreiben also statt
int variable; // Variable vom Typen int= Ganze Zahleinfach
EineKlasse* dasObjekt; // Ein Objekt von dem Typen Eine KlasseIhr Bleistift gehört also zur Klasse aller Bleistifte. Wenn Sie zwei Bleistifte hätten, dann wären es zwei verschiedene Objekte, seien sie sich noch so ähnlich. Es sind die gleichen aber nicht dieselben. Ein Bleistift ein Objekt, der andere Bleistift ein anderes Objekt. Sie gehören aber zur selben Klasse Bleistift. Die Klasse ist also eine abstrakte Vorstellung, wie etwas auszusehen hat. Eine Vorstellung, eine Idee, wie der griechische Philosoph uns lehrt. Sie können den Unterschied auch gut daran erkennen, dass zwei verschiedene Bleistifte unterschiedlich stumpf sein können!
Eines noch kurz an dieser Stelle. Sie wissen jetzt, das sie jedem konkreten Objekt eine Klasse zu ordnen können. Ihren konkreten Bleistift können sie der Klasse Bleistifte zuordnen. Sie können aber auch eine Klasse einer Klasse zuordnen, nämlich einer Superklasse: Die Klasse der Bleistifte ist ein Spezialfall der Klasse der Schreibgeräte, weil Ihre Vorstellung von einem Bleistift ein Spezialfall von Ihrer Vorstellung von Schreibgeräten ist. Wenn wir also von der Klasse Bleistift sprechen meinen wir die Klasse Schreibgeräte immer mit, bloß dass wir iBleistiften zusätzliche Fähigkeiten und Eigenschaften hinzufügen. Nehmen wir etwa die Fähigkeit, zu schreiben. Das kann jedes Schreibgerät. Diese Fähigkeit kennzeichnet also alle Schreibgeräte. Jetzt nehmen wir die Fähigkeit anspitzen. Diese Fähigkeit hat nur ein Bleistift, hingegen kein Kugelschreiber. Es ist also eine Fähigkeit von Bleistift, keine allgemeine Fähigkeit von Schreibgeräte.
Wenn wir also eine Klasse programmieren, dann nehmen wir uns in aller Regel eine Superklasse (oder auch Basisklasse), der wir Eigenschaften und Fähigkeiten zufügen. So sagen wir etwa:
Bleistifte sind Schreibgeräte und haben zusätzlich die Eigenschaften
Stumpfheit
und haben zusätzlich die Fähigkeit
anspitzen
Die Fähigkeit schreiben ist hingegen schon in der Klasse Schreibgeräte vorhanden. Wenn wir sagen, dass Bleistifte Schreibgeräte sind, wird die Fähigkeit schreiben automatisch auch einem Bleistift verliehen. (Wenn alle Schreibgeräte schreiben können und Bleistifte Schreibgeräte sind. dann müssen auch Bleistifte schreiben können.) Man nennt dies vererben. (Der Bleistift erbt die Fähigkeit zu schreiben von dem Schreibgerät.)
Dies hat zwei Vorteile:
Sie werden jetzt vielleicht einwenden, dass ja jedes Schreibgerät anders schreibt. Ein Bleistift etwa in der Regel grau ein Kugelschreiber in der Regel blau. Also kann man doch gar nicht allgemein sagen, was schreiben bei einem Schreibgerät bedeutet!
Richtig, das kann man nicht. Das ist aber nicht schlimm. Denn sie können, wenn sie wollen, die Fähigkeit schreiben bei der Unterklasse Bleistift einfach neu programmieren. Vererben bedeutet nur, dass prinzipiell diese Fähigkeit übernommen wird, sie also vorhanden sein muss und dass auch ohne Weiteres sie so funktioniert wie in der Superklasse. Wollen sie ein anderes Verhalten beschreiben, so schreiben Sie die Fähigkeit einfach neu. Dann wird automatisch die Fähigkeit der Unterklasse verwendet. Das gilt sogar dann, wenn Sie die Unterklasse gar nicht kennen! Hiermit beschäftigen wir uns später.
Jetzt fragen sie sich vielleicht, was denn dann über Schreibgerät kommt. Und was dann darüber kommt usw. Es muss ja irgendwann eine Obercheffeklasse geben. Das Ding an un vör sisch! Das gibt es auch: Es ist die Klasse NSObject. Und diese Klasse hat keine höhere mehr. Da Sie stets von dieser Klasse mehr oder weniger direkt ableiten können, haben alle ihre Klassen bereits die Fähigkeiten, die auch NSObject hat. Und das sind eine Menge! Insbesondere hat NSObject die sagenhafte Fähigkeit, Objekte der Klasse zu erstellen. Mit anderen Worten: Wenn sie einen konkreten Bleistift haben wollen, dann bitten Sie einfach die Klasse Bleistift darum, eines herzustellen. Bleistift hat aber bereits diese Fähigkeit von NSObject geerbt, so dass sie diese für Ihre Klasse Bleistift nicht neu programmieren müssen.
Und was ist mit den Eigenschaften? Diese werden ebenfalls vererbt. Da ändert sich nichts! Allerdings können Sie eine einmal festgelegte Eigenschaft nicht mehr verändern. Das ist aber nicht schlimm, weil wir das gar nicht benötigen.
Sie programmieren nur diese Klassen, Vorstellungen. Sie sagen, welche Eigenschaften und Fähigkeiten die Objekte später haben sollen, die zu dieser Klasse gehören. Machen wir das einfach mal als Beispiel:
Grundsätzlich gibt es zwei Arten, neue Klassen zu programmieren. Wir bedienen uns der ersten in Xcode. Zunächst wählen Sie rechts in Group & Files Start und dann Classes aus, damit unsere neue Klasse dort landet. Gehen Sie in das Menü File und wählen dort New File…. Dann erscheint ein Dialog, der Sie auffordert, die Art der Datei auszuwählen. Wir wählen Objective-C class aus. Sie klicken dann auf Next, was einen neuen Dialog bringt. Dort ist in der obersten Zeile schon untitled vorselektiert. Tippen Sie auf der Tastatur einfach nur den Namen unserer neuen Klasse ein: Pencil. Klassen haben immer Namen, die mit Großbuchstaben beginnen. Achten Sie darauf.
Xcode erzeugt automatisch ein Fenster mit einer Datei darin. Die Datei heißt Pencil.h. Außer einer Einleitung mit Kommentaren steht da:
#import <Cocoa/Cocoa.h> @interface Pencil : NSObject { } @endJede Definition einer Klasse hat zwei Teile. Diese werden durch
@interface bzw. @implementation eingeleitet und mit @end beendet. Hier sehen wir nur den ersten Teil. Er sagt, welche Eigenschaft Objekte dieser Klasse haben und welche Fähigkeiten. Dieses Interface steht immer in einer Datei, die die Endung .h hat. Das #import <Cocoa/Cocoa.h> hat wieder nur die Bedeutung, dass Sie Cocoa verwenden dürfen.
Das @interface leitet, wie bereits erwähnt, die Definition ein. Danach kommt der Name unserer neuen Klasse, hier Pencil. Durch einen : getrennt folgt die sog. Superklasse, welche hier NSObject ist. Was genau das ist, lernen Sie sogleich.. Nur soviel: Die Superklasse ist eine allgemeinere Vorstellung. In der Wirklichkeit ist etwa „Schreibgerät“ die Superklasse von „Bleistift“. In unserem Beispiel ist dies NSObject, was in etwa soviel bedeutet wie: Irgendein Objekt. Der Vorteil: Dadurch kann Pencil schon alle Fähigkeiten, welche auch NSObject hat. Und Pencil hat alle Eigenschaften, welche NSObject hat - und das ist ziemlich viel. NSObject ist so etwas wie der Ausgangspunkt für (fast) alle Klassen in Cocoa. Später dazu mehr.
Dann folgen geschweifte Klammern. Innerhalb dieser befinden sich die Eigenschaften von PPencil, genauer: Jene, die wir als Bleistift NSObject hinzufügen.. Wir werden hier zwei Eigenschaften, nämlich Stumpfheit und Härtegrad eintragen. Der Einfachheit halber nehmen wir hier einfach zwei ganze Zahlen, die das repräsentieren.
Nachdem die geschweifte Klammern geschlossen sind, folgt eine Aufzählung der Fähigkeiten. Wir haben hier zwei Fähigkeiten: Schreibe und Anspitzen. Deutsch ist übrigens reichlich unbrauchbar zum programmieren, weshalb wir Englisch verwenden. Die Fähigkeiten haben in Klammern den Datentypen, den sie als Ergebnis liefern, so ähnlich wie Funktionen. Hier liefern die Fähigkeiten gar nichts zurück.
Dies tragen wir jetzt einmal ein:
@interface Pencil : NSObject { // Hierher gehören die Eigenschaften // Die Schärfe ist eine Eigenschaft. Sie wird hier durch eine Variable // und dem Datentypen ganze Zahl festgelegt. Wir speichern hier einfach Werte von 1 (spitz) bis 20 (stumpf). int _sharpness; // Außerdem speichern wir noch die Eigenschaft Härtegrad - von 1 (HH - H - HB - B - BB) bis 5. int _hardness; } // Hierher gehören die Fähigkeiten // Zunächst können wir mit dem Bleistift schreiben - (void)write; // Außerdem können wir ihn anspitzen. - (void)sharpen @endNachdem wir das alles so eingetippt haben, müssen wir jetzt noch die Funktionalität programmieren. Das macht man in einem zweiten Teil. Dort sagen wir, was -write und -sharpen überhaupt machen. Sie können das Fenster schließen und sehen in
Groups & Files eine zweite Datei, die erzeugt wurde: Pencil.m. Doppelklicken Sie diese. Wieder erscheint ein Fenster. Hier dürfte ein Text stehen, der -abgesehen von Kommentaren- etwa folgendes Aussehen hat:
#import "Pencil.h" @implementation Pencil @endMit der ersten Zeile wird gesagt, dass wir alles kennen, was wir in
Pencil.h angelegt haben, also vor allem die Eigenschaften und Fähigkeiten. Da jedoch in Pencil.h ja Cocoa importiert wurde, kennen wir hier auch alles von Cocoa. Dann folgt @implementation, also die wirkliche „innere“ Beschreibung unserer Klasse. Hier müssen wir unsere Fähigkeiten programmieren. Machen wir das:
@implementation Pencil - (int)sharpness { return _sharpness; } - (void)write { int sharpness; sharpness = [self sharpness]; sharpness = sharpness + [self hardness]; [self setSharpness:sharpness]; } @end
In Objective-C bezeichnet man ein einzelnes Objekt, also jeden einzelnen Bleistift als Instanz oder Instanzobjekt. Seine Klasse ist eben seine Klasse. Aber auch diese Klasse ist wieder ein Objekt. Denn auch mit ihr können Sie Dinge anstellen, sie halt also Fähigkeiten. Wir nennen dies das Klassenobjekt. Sie glauben das nicht? Sie glauben nicht, dass man mit Vorstellungen etwas anfangen kann? Nun, sie haben sicher eine Vorstellung davon, wie ein Bleistift aussieht. Mit dieser Vorstellung können Sie etwas anfangen: Sie können Bleistifte herstellen, also konkrete Objekte. Oder anders gesagt: Ohne diese Vorstellung von einem Bleistift könnten Sie niemals einen Bleistift bauen. Ihre Vorstellung (Die Klasse Bleistifte) dient also dazu, Objekte der Klasse herzustellen. Und da wären wir bei der wichtigsten Funktion von Klassenobjekten: Sie erstellen uns Instanzobjekte. Wir bitten Sie einfach darum. Und jetzt muss ich Ihnen gleich nochmals sagen, dass ich Sie vorhin belogen habe. Als wir die Variable für ein Objekt anlegten, enthielt die gar kein Objekt. In Wirklichkeit wird stattdessen nur gespeichert, wo der Computer das Objekt findet. Es ist also ein reiner Verweis, eine Fundstelle, so ähnlich wie in einem Inhalts- oder einem Sachverzeichnis. Das erkennt man an dem * was soviel wie Verweis bedeutet. Wenn wir wirklich dann auch ein Objekt haben wollen, müssen wir es noch erzeugen. Das sähe etwa so aus:
Pencil* aPencil; // Der Verweis auf ein Objekt der Klasse Bleistift aPencil = [Pencil pencil]; // Jetzt wird das Objekt erzeugtDas große
Pencil ist ein Klassenobjekt (denen gibt man immer Namen die mit einem Großbuchstaben beginnen). Das kleine Pencil ist die Aufforderung, einen Bleistift herzustellen. Wir befehlen also unserer Klasse, unserer Vorstellung, ein konkretes Instanzobjekt zu erstellen. Wie dies mit der Aufforderung geschieht, sehen wir später.
Nachdem wir nun eine vage Vorstellung davon haben, was eine Klasse ist müssen wir gleich noch etwas klären, was nicht ganz einfach ist. Klassen sind Vorstellungen von Dingen. Diese Vorstellungen kann man aber, wenn man ein ordentlicher Mensch ist, sortieren. In Ihrer Küche befinden sich eine Spül- und eine Waschmaschine. (Nehmen wir das an!) Ihre konkrete Spülmaschine ist ein Instanzobjekt und gehört zur Klasse Spülmaschinen. Ihre konkrete Waschmaschine ist ein Instanzobjekt und gehört zur Klasse Waschmaschinen. Gut. Beide Gerätearten sind aber Haushaltsgeräte. Das heißt, die Klasse Waschmaschine ist nur eine spezielle Vorstellung von der Klasse Haushaltsgeräte. Sie haben eine Vorstellung von allen Haushaltsgeräten. Dies ist die Klasse Haushaltsgeräte. Innerhalb dieser Klasse haben Sie eine Vorstellung von Waschmaschinen. Dazu gehört es auch, dass diese ein Haushaltsgerät ist.
Klassen sind also immer Spezialfälle von einer allgemeineren Klasse. Man nennt die allgemeinere Superklasse, Oberklasse oder superclass. Die speziellere heißt Unterklasse oder subclass. Wenn wir selbst eine Klasse programmieren, dann sagen wir gleich, zu welcher Oberklasse sie gehört. Das sieht in etwa so aus:
// Waschmaschine gehört zur Oberklasse Haushaltsgeräte @interface Waschmaschine : HaushaltsgeräteDiesen Vorgang nennt man vererben, ein schrecklich falsches Wort, aber so ist es. Oder man nennt es ableiten. Ein viel besseres Wort. Wir leiten die Klasse Waschmaschine also von der Klasse Haushaltsgeräte ab.
Wieso macht man das? Es ist nichts als Bequemlichkeit. Alle Haushaltsgeräte haben Gemeinsamkeiten. Zum Beispiel haben sie alle die Fähigkeit, dass man sie einschalten kann. Nichts Berühmtes, aber immerhin. Also verleihen wir bereits der Klasse Haushaltsgeräte bereits diese Fähigkeit, indem wir sie programmieren. Machen wir dann eine Waschmaschine, so ererbt (daher kommt das Wort) die Klasse Waschmaschine bereits diese Fähigkeit. Wir brauchen sie jetzt nicht mehr zu programmieren. Machen wir uns dann eine Klasse Spülmaschine, so ererbt diese ebenfalls die Fähigkeit „einschalten“. Wir brauchen sie nicht zu programmieren. Machen wir weiter mit dem Trockner, der Küchenmaschine usw.
Wir müssen also abgeleiteten Klassen noch nur diejenigen Fähigkeiten hinzufügen, die die Superklasse noch nicht hatte. Einer Waschmaschine also etwa „waschen“, einer Spülmaschine „spülen“ usw. Das erspart uns Arbeit, weil in Cocoa schon ganz viele vorgefertigten Klassen vorhanden sind, denen wir nur noch Fähigkeiten hinzufügen müssen. Das Gleiche gilt übrigens für die Zustände.
Wir können aber nicht nur hinzufügen, sondern auch verändern, verbessern. Das sehen wir uns jetzt einmal an einem computerisiertem Beispiel an:
Wenn Sie ein beliebiges Programm starten, so zeigt es etwas an. Texte, Graphiken, Listen, was weiß ich. Jedes dieser Anzeigeelemente nennt man View. Es hat die Klasse NSView. Objekte dieser Klasse können schon ganz schön viel! Wir schauen uns nur eine Eigenschaft an: Ihre Größe. Diese Eigenschaft hat jedes Objekt der Klasse NSView. Dann hat es natürlich die Fähigkeit, diese Größe zu verändern. Diese Fähigkeit hat jedes Objekt der Klasse NSView. Und das kann es sogar sehr schön, nämlich animiert. Auch das kann schon jedes NSView. Wenn Sie sich jetzt also selbst eine Klasse schreiben, die von NSView abgeleitet ist, also irgendetwas anzeigt, dann haben Objekte dieser Klasse bereits diese Eigenschaften und Fähigkeiten. Sie müssen das nicht mehr programmieren.
Andererseits sollen ihre Objekte jetzt ganz bestimmte Dinge anzeigen auf eine ganz bestimmte Art. Dazu hat NSView bereits die Fähigkeit sich selbst zu zeichnen. Nur ist diese ganz allgemein gehalten und zeichnet einfach nur einen weißen Hintergrund. Bei Ihnen soll da etwas anderes gezeichnet werden, insbesondere etwas, was sinnvoller ist. Also sagen Sie jetzt, dass statt der allgemeinen Fähigkeit ihre verwendet werden soll. Schon sind sie fertig! Alles andere existiert ja bereits.
Ein Programm besteht in einer objekt-orientierten Programmiersprache aus zahlreichen Objekten, die miteinander vernetzt sind. Dies geschieht, indem sie aufeinander verweisen. Man nennt dies eine Referenz. In Ihrem Programm werden schnell unzählige Referenzen entstehen und ein wahres Wirrwarr hervorrufen. Das ist in gewissem Maße gewollt. Wir müssen irgendwie sehen, wie wir das in den Griff bekommen.
Wenn Sie sich etwa wieder in die Küche begeben, so haben Sie dort die Spülmaschine. Ein Objekt, weil alles ein Objekt ist. Auch der Wasserabfluss in der Wand ist ein Objekt, weil alles ein Objekt ist. Wenn die Spülmaschine an den Wasserabfluss angeschlossen ist, dann referenziert die Spülmaschine den Wasserabfluss.
Warum ich das hier überhaupt erwähne liegt daran, dass viele Programmierer Angst davor haben, solche Netze mit vielen kleinen Objekten anzulegen. Das liegt daran, dass sie von einer anderen Programmiersprache kommen, meist C/C++ und befürchten, dass dies viel Speicher kostet und das Programm langsam machen. Das ist selten bis nie der Fall. Lassen Sie sich das also nicht einreden. Ich kann ihnen von mehr als einem Programmierer berichten, der dieses Vorurteil hatte, bis er es selbst probierte. Ich gehöre übrigens dazu! Sie haben daher einen riesigen Vorteil: Sie fangen gleich mit einer objekt-orientierten Programmiersprache an und sind daher unbelastet von altertümlichen Vorurteilen. Nutzen Sie das!
Wichtig sei hier noch Folgendes: Wenn Objekte vernetzt sind, dann haben sie Referenzen aufeinander. Das schrieb ich schon. Aber es bedeutet auch: Sie haben 'nur' Referenzen aufeinander. Sie sind nicht das andere Objekt, es gehört ihnen auch nicht. Wenn also zwei Objekte ein drittes referenzieren, dann hat jedes ein Verweis auf das andere. Wenn eines diesen Verweis dazu benutzt, das andere zu ändern, so darf es das. Aber man muss sich klar machen, dass für beide referenzierenden Objekt das Dritte verändert ist. Denn es gehört niemanden! Oder umgekehrt muss man sich klar machen, dass das von mir referenzierte Objekt durch jeden anderen geändert worden sein kann.
Stellen Sie sich also vor, dass an dem Wasserabfluss auch eine Waschmaschine angeschlossen ist. Jetzt referenzieren beide den Wasserabfluss. Wenn eines von den beiden diesen verstopft, so ist er auch für die andere Maschine verstopft!
Hier finden Einsteiger weitführende Informationen zur Programmiersprache Objective-C und C:
Einstieg für C++ - Programmierer
Nach dem Login können hier die DeveloperTools von Apple heruntergeladen werden
V. Reichenberger: Einführung in Objective-C
The Objective-C Programming Language
"C von A bis Z von Jürgen Wolf - Das umfassende Handbuch" auf Galileo OpenBooks