Anhang A Geschichte und Bedeutung von Objective-C

In diesem Anhang möchte ich noch etwas näher auf die Geschichte und den Einfluss von Objective-C eingehen. Dieser Anhang ist von Interesse für alle, die über den Tellerrand hinausschauen und verstehen möchten, welche Rolle und Bedeutung Objective-C in der heutigen Zeit besitzt.

A.1 Die Sprache der Computer

In Kapitel 2 haben Sie gelernt, dass Computer einen Compiler benötigen, um Programme, die von menschlichen Entwicklern geschrieben werden, in die Sprache der Computer zu übersetzen.

Hier möchte ich Ihnen zwei konkrete Beispiele dafür geben, wie die Sprache eines Computers grundsätzlich aussieht. In Listing 2.2 haben Sie gesehen, wie Sie in der Programmiersprache Objective-C zwei Zahlen addieren können.

A.1.1 Übersetzungen in Assembler

Wenn Sie eine einfache Aufgabe wie die Addition zweier Zahlen 34 und 67 betrachten, so würde diese für einen Intel-Prozessor, wie er z. B. in einem MacBook eingebaut ist, wie in Listing A.1 aussehen.

  movl $34, -36(%ebp) 
  movl $67, -40(%ebp) 
  movl -36(%ebp), %eax 
  addl -40(%ebp), %eax 
  movl -68(%ebp), %ecx 
  movl %eax, (%ecx)

Listing A.1 Beispiel für die Sprache eines Intel-Prozessors

 

Und für ein iPad sieht es so aus, wie in Listing A.2 gezeigt ist. Dies sind streng genommen noch vereinfachte, für Menschen gedachte Darstellungen namens Assembler. Die tatsächliche Sprache des Computers besteht lediglich aus Zahlen und ist ohne die Hilfe von Assembler für Menschen praktisch unlesbar.

  movsr0, #34 
  addsr0, #67 
  movwr1, :lower16:(L_a$non_lazy_ptr-(LPC0_7+4)) 
  movtr1, :upper16:(L_a$non_lazy_ptr-(LPC0_7+4)) 
LPC0_7: 
  add  r1, pc 
  ldr  r1, [r1] 
  str  r0, [r1]

Listing A.2 Beispiel für die Sprache eines ARM-Prozessors, wie er in einem iPad verwandt wird

 

Sie sehen, dass Sie eigentlich für jeden Typ von Rechner eine völlig eigene Programmiersprache lernen müssen – die zudem so wahnsinnig kompliziert ist, wie sie auf den ersten Blick aussieht. Selbst um die einfachsten Aufgaben zu lösen!

A.1.2 Programmiersprachen

Bis hierhin ist noch nichts gewonnen, denn dummerweise spricht Ihr Prozessor kein Objective-C. Daher müssen Sie diese Sprache noch in die prozessorspezifische Sprache übersetzen. Und genau dafür verwenden Sie den Compiler. Ein Compiler übersetzt die Anweisung aus Listing 2.2 in die prozessorspezifischen Formen aus Listing A.1 oder Listing A.2. Damit braucht man zwar zwei verschiedene Compiler, aber sobald man diese einmal hat, kann man Programme, die in C geschrieben sind, auf allen diesen Prozessoren laufen lassen.

Wenn Sie also ein Programm für Ihr MacBook schreiben wollen, dann benutzen Sie den Compiler, der Objective-C in die Maschinensprache des Intel-Prozessors übersetzt. Wenn Sie ein Programm für Ihr iPhone schreiben wollen, dann benutzen Sie den Compiler, der Objective-C in die Maschinensprache des ARM-Prozessors übersetzt. Und wenn Sie ein Kollege bittet, ein Programm für seinen alten Mac mit PowerPC zu übersetzen? Genau – Sie benutzen einen Compiler, der ein Programm in die Sprache des PowerPC-Prozessors übersetzt!

Wenn Sie sich fragen, was denn die ersten Programmierer gemacht haben, die noch keine Compiler hatten: Sie mussten wirklich in der Sprache der Prozessoren arbeiten. Für einfache Programme ging das noch ganz gut und Computer waren so groß und so teuer, dass die Kosten für ausgebildete Fachleuten und Wissenschaftler, die mit ihnen gearbeitet haben, nicht ins Gewicht gefallen sind.

Irgendwann wurden die Programme aber so kompliziert, dass es einfacher war, einen Compiler für die Übersetzung einer einfachen, prozessorunabhängigen Sprache in die Sprache des Prozessors zu schreiben. Heute ist das das erste, was der Entwickler eines neuen Prozessors macht!

A.1.3 Cocoa und Cocoa Touch

Ein wesentliches Hilfsmittel ist eine vollständige Entwicklungsumgebung, mit der Sie programmieren und arbeiten können. Diese enthält auch einen grafischen Editor, in dem Sie Benutzeroberflächen ähnlich wie in einem Grafikprogramm visuell arrangieren können. Er ist ein wichtiger Bestandteil von Xcode, der Entwicklungsumgebung von Apple für iPhone, iPad und auch Mac OS X.

Aber zur Entwicklung gehört noch mehr als nur die Programmiersprache: Es ist notwendig, dem Anwender eines Programmes Ergebnisse anzuzeigen und auch Eingaben in Empfang zu nehmen. Stellt ein iPhone beispielsweise eine Tabelle dar, so muss ein Programm die Tabelle mit Inhalt füllen und ebenfalls darauf reagieren, wenn der Benutzer einzelne Tabellenzeilen auswählt. Um eine Sache muss sich der Programmierer des Programmes allerdings nicht kümmern: die Tabelle selbst darzustellen, sie zu scrollen, wenn ein Benutzer mit dem Finger nach oben oder nach unten fährt, und die Berührungen zu analysieren. Diese Arbeit wird vom Betriebssystem übernommen.

Dadurch kann das Betriebssystem einem Programmierer viel Arbeit abnehmen. Diese Funktionen gehören allerdings nicht zur Programmiersprache selbst, die Programmiersprache bestimmt nur die Art und Weise, wie ein Programmierer seine Anweisungen formuliert. Daher hat diese Funktionalität unter Mac OS X einen eigenen Namen, man spricht dort von Cocoa. Cocoa ist der Name aller Funktionalitäten, die das Betriebssystem einem Programmierer in der Programmiersprache Objective-C bietet. Auf dem iPhone gibt es weitere Funktionen, z. B. die Gestensteuerung, die es unter Mac OS X nicht gibt. Daher haben diese Funktionen auf dem iPhone einen etwas anderen Namen, er lautet Cocoa Touch. Wenn Sie Objective-C beherrschen, können Sie Cocoa und Cocoa Touch benutzen, um Programme sowohl für Mac OS X als auch das iPhone und das iPad zu schreiben.

Dieses Buch vermittelt nicht nur die Grundlagen von Objective-C, Cocoa und Cocoa Touch, sondern auch die Konzepte und Prinzipien, nach denen diese funktionieren. Alle heute gängigen Systeme beruhen auf ähnlichen Prinzipien, wobei Cocoa und Cocoa Touch die ältesten und ausgereiftesten Systeme auf dem Markt sind. Dadurch können Sie mit einem geringen zusätzlichen Lernaufwand auch auf die Entwicklung anderer Systeme umsteigen, wenn Sie dies später einmal wünschen sollten. Andererseits bin ich mir sicher, dass Sie in naher Zukunft von den Apple-Lösungen überzeugt sein werden und dann gar nicht mehr umsteigen wollen.

A.2 Bedeutung von Objective-C und Cocoa

Ein wichtiges Argument für eine Sprache ist, dass sie bereits besonders lange existiert und in vielen Projekten erfolgreich eingesetzt wird. Dadurch erhöhen sich ihre Chancen auch in Folgeprojekten und sie wird sozusagen zu einem Selbstläufer.

In der Kombination mit Objective-C kann das Cocoa-Framework auf eine über 25-jährige Geschichte zurückblicken. Damit ist es nicht nur das heute erfolgreichste System, sondern es ist auch eines der ausgereiftesten und erprobtesten Systeme am Markt. Kein anderes System mit ähnlicher Verbreitung ist auch nur annähernd 10 Jahre alt. Sowohl für Objective-C Entwickler als auch für Investoren bedeuten der Reifegrad und die Verbreitung daher die beste Investitionssicherheit.

In Bezug auf die Fähigkeit, ein iOS-Gerät im Vergleich zu anderen Geräten zu benutzen, hat sich in der Praxis gezeigt, dass die kognitive Last von iOS 7 deutlich niedriger ist als von allen anderen Systemen. Das bedeutet, dass es für einen Benutzer einfacher ist, sich unter iOS 7 zurechtzufinden als unter allen anderen Systemen. Für weitere Informationen und Referenzen können Sie diesen englischsprachigen Artikel zu Rate ziehen: http://appleinsider.com/articles/13/09/24/ios-_7-_shines-_as-_apple-_bests-_android-_windows-_phone-_in-_user-_experience-_shootout

Diese Stärke schlägt sich auch in der praktischen Verbreitung der Geräte wieder. Inzwischen konnte Apple in den Verkaufszahlen den Marktgiganten Microsoft schlagen, siehe z. B. diese Nachrichtenmeldung: http://www.heise.de/mac-_and-_i/meldung/Analyst-_Apple-_ueberholt-_Microsoft-_mit-_allen-_Geraeten-_2113117.html

Anwendungen, die unter Objective-C entwickelt werden, sind ebenfalls deutlich profitabler als alle Konkurrenten. So waren im Jahre 2013 die Einnahmen von iOS-Entwicklern höher als die der Android-Entwickler mit Java – obwohl Android-Telefone viel höhere Stückzahlen am Markt haben als Apple, siehe http://www.heise.de/newsticker/meldung/Marktforscher-_Zwei-_Drittel-_aller-_App-_Umsaetze-_entfallen-_auf-_iOS-_1897951.html

Woran liegt diese Diskrepanz? Die theoretisch höhere Verbreitung von Android-Telefonen wird natürlich dadurch zunichte gemacht, dass die meisten eine veraltete Version von Android verwenden. Die Verbreitung der Versionszahlen findet sich z. B. auf Wikipedia http://de.wikipedia.org/wiki/Android_(Betriebssystem)

Die Konsequenzen veralteter Systeme schlägt sich auch in mangelnder Sicherheit nieder, siehe z. B. diese Meldung: http://www.heise.de/newsticker/meldung/Android-_Sicherheitsluecken-_wegen-_fehlender-_Updates-_bleiben-_Problem-_2139700.html

Im Februar 2014 war die tatsächliche Verbreitung der aktuellsten Android-Version 4.4 gerade einmal 2,5 %, obwohl diese Version schon mehrere Monate verfügbar war. Zum Vergleich: iOS 7 von Apple hatte bereits nach 3 Monaten eine Verbreitung von 74 %, siehe diesen englisch-sprachigen Bericht: http://www.maclife.com/article/news/apple_ios_7_adoption_rate_stands_74

Man kann also guten Gewissens behaupten, dass iOS heute die beste Plattform für Entwickler darstellt, wobei dies sowohl für Entwickler gilt, denen es ums Geldverdienen geht, als auch für die, denen es auf ausgereifte Systeme und Zukunftsfähigkeit ankommt.

Anhang B Lösung der Aufgaben

Die Lösungen einiger Aufgaben sind zu umfangreich, um sie hier abzudrucken. Sie können diese zusammen mit den Codebeispielen von den Webseiten http://www.downloads.wrox-_press.de und http://objective-_c-_sprachkurs.de herunterladen.

Lösungen zu Kapitel 2

4. Nein, das ist nicht möglich, weil Sie das Label gar nicht im grafischen Editor zu sehen bekommen.

Lösungen zu Kapitel 3

1. So brauchen Sie nur eine einzige printf-Anweisung:
 printf("Wir haben x = %i und y = %i.\nx + y = %i sowie x - y = %i.\n", 
       x, y, summe, differenz);
2. Die Funktion scanf ist leider nicht besonders schlau und hat keine Möglichkeit, ungültige Eingaben zu erkennen. Im ersten Fall findet sie keine gültigen Zeichen und liefert daher 0 zurück. Im zweiten Fall liest sie die gültigen Ziffern, solange es geht, und hört danach auf. Daher liefert sie 126 zurück.
3. Die Divison 23/2 liefert nicht 11.5, sondern 11. Es wurde offenbar abgerundet, bevor das Ergebnis angezeigt wurde. Die Multiplikation 5.5*2 liefert ebenfalls 11, diesmal das korrekte Ergebnis. Die Multiplikation 5.5*3 liefert 16, hier wird wieder vom korrekten Ergebnis abgerundet. Warum das so ist und was Sie dagegen machen können, wird in Abschnitt 4.2 erklärt.

Lösungen zu Kapitel 4

1. Dieser Ausdruck ist äquivalent zu YES. (Und zwar für alle möglichen Datentypen und Werte von a!)
2. Dieser Ausdruck ist NO.
3. Er ist NO.
4. Er ist YES.
5. Das hängt vom Datentyp von a ab. Wenn a ein int ist, so ist der Ausdruck immer NO. Falls a ein float ist, so ist der Ausdruck gleich YES, falls a zwischen 3 und 4 (jeweils ausschließlich) liegt. Ansonsten ist der Ausdruck NO.
6. Wenn a ein int ist, dann ist er NO, falls a gleich 2 ist, ansonsten YES. Wenn a ein float ist, sollte man dies nicht so schreiben, sondern z.B. mit (fabs(a-2.0) < 0.0001).
7. Er ist dann YES, wenn a und b in ihrer Summe kleiner als 100 sind. Dabei können a oder b durchaus negativ sein, weswegen jede Zahl für sich auch größer als 100 sein kann. Es kann aber auch passieren, dass ein Overflow auftritt, wenn einer oder beide Werte zu groß werden.
8. Dies lässt sich wie folgt bewerkstelligen:
((a + b < 100) && (a < 100) && (b < 100))
9. Er ist YES, falls a gerade ist, und NO, falls a ungerade ist.

Lösungen zu Kapitel 5

1. Eine mögliche Lösung lautet:
 void fizzbuzz(int x) 

  // x ist durch 3 teilbar. 
  // (Beachten Sie, dass "Fizz" auch dann ausgegeben wird, 
  // wenn x durch 3 teilbar ist!) 
  if (x % 3 == 0) { 
    printf("Fizz"); 
  } 
 
  // x ist durch 5 teilbar. 
  // (Beachten Sie, dass dies an "Fizz" angehängt wird, 
  // falls x durch 15 teilbar ist!) 
  if (x % 5 == 0) { 
    printf("Buzz"); 
  } 
 
  // x ist weder durch 3 noch durch 5 teilbar. 
  if ((x % 3 != 0) && (x % 5 != 0)) { 
    printf("%i", x); 
  } 
  printf("\n"); 
}

Lösungen zu Kapitel 7

3. Das resultierende Objekt hat keinerlei Eigenschaften und die Methode arbeitet im Wesentlichen so wie eine Funktion. Aus der Sicht eines anderen Programmierers muss erst ein entsprechendes Objekt erzeugt werden, bevor die Methode aufgerufen werden kann. Dies ist umständlich im Vergleich zu einem einfachen Funktionsaufruf.In dieser Form ist es keine sinnvolle Anwendung von Objekten, eine Implementierung als »normale« wäre hier vorzuziehen. Es könnte allerdings u.U. sinnvoll sein, eine Objekt mit einer Ansammlung von Klassenmethoden zu definieren, die in Abschnitt 8.5.2 erwähnt werden.

Lösungen zu Kapitel 8

2. Es ist zwar nicht möglich, die Methode nur einmal zu implementieren, aber es wäre möglich, diese Methode – wenn sie sehr aufwändige Arbeiten durchführt – in einer Funktion oder einer externen Hilfsklasse unterzubringen. Diese würde dann einfach von beiden Implementierungen von studiere aufgerufen. Dies könnte beispielsweise die Funktion studienHelfer erledigen, die folgendermaßen aussieht:
 void studienHelfer(WSArbeiter* opfer) 

  // Studieren erhöht die Erfahrung sehr deutlich um zwei. 
  opfer.erfahrung = opfer.erfahrung + 2; 
 
  // Das Alter wird aber auch wie gewohnt um 1 erhöht. 
  opfer.alter = opfer.alter + 1; 
}
Die Methode studiere sähe dann in beiden Fällen so aus:
- (void)studiere 

  studienHelfer(self); 
}

Lösungen zu Kapitel 9

1. In einem hat Steve Recht: »Seine« Funktion hat tatsächlich kein Problem mit einer Endlosschleife und liefert Ergebnisse mit exakt dem gleichen Aufwand bei jedem Aufruf.
Allerdings macht sie geringfügig etwas Anderes: Die ursprüngliche Funktion hat entweder eine Ziffer, einen Kleinbuchstaben oder einen Großbuchstaben erzeugt. Dies sind 62 mögliche Zeichen. Die Wahrscheinlichkeit, dass dieses Zeichen eine Ziffer ist, beträgt 10/62, weil es 10 mögliche Zifferzeichen gibt. Dies sind 16,1 Prozent.
Die Wahrscheinlichkeit, dass Steve’s Funktion eine Ziffer erzeugt, beträgt hingegen 1/3, also 33,3 Prozent.
Deswegen funktioniert Steve’s neue Funktion nicht völlig so wie die alte.
2. Die richtige Schreibweise der Suchfunktion lautet lautet:
 NSRange suchErgebnis = [eingabe rangeOfString:@"griech" 
                        options:NSCaseInsensitiveSearch];

Lösungen zu Kapitel 10

1. Die folgende Funktion ermittelt die Summe:
 double summe(NSArray* array) 

  // Diese Variable akkumuliert die einzelnen Einträge. 
  double ergebnis = 0; 
 
  for (int i=0; i<[array count]; i++) { 
    // Die Schleife addiert alle Zahlen einzeln. 
    ergebnis = ergebnis + [array[i] doubleValue]; 
  } 
 
  // Gib das Ergebnis zurück. 
  return ergebnis; 
}
2. Die folgende Funktion ermittelt die Schnittmenge:
 NSArray* schnittArray(NSArray* array1, NSArray*array2) 

  // Das Ergebnis wird in einem Array gespeichert, das 
  // veränderlich und anfangs noch leer ist. 
  NSMutableArray* ergebnis = [NSMutableArray new]; 
 
  // Diese Schleife läuft über alle Einträge des 
  // ersten Arrays. 
  for (int i=0; i<[array1 count]; i++) { 
 
    // Diese Schleife läuft über alle Einträge des 
    // zweiten Arrays und bricht ab, wenn ein gemeinsamer 
    // Eintrag gefunden wurde. 
    int j = 0; 
 
 
    while (j < [array2 count]) { 
      if ([array1[i] integerValue] == [array2[j] integerValue]) { 
        // Gemeinsamer Eintrag gefunden. Füge ihn zur 
        // Schnittmenge hinzu und breche dann die innere 
        // Schleife ab. 
        [ergebnis addObject:array1[i]]; 
        break; 
      } 
      j++; 
    } 
  } 
 
  // Gib das Ergebnis zurück. 
  return ergebnis; 
}
Die Vereinfachung, dass alle Zahlen int sind, dient dazu, dass Sie sich nicht mit Rundungsproblemen bei Fließkommanzahlen rumschlagen mussten.
3. Hier besteht der Trick darin, ein Array mit 15 Einträgen zu gestalten und anschließend den Tabelleneintrag mit der Modulo-Funktion auszulesen. Das Projekt, das diese Strategie umsetzt, finden Sie in den Downloads.
4. Diese Methode ist sehr schwierig zu schreiben. Die wesentliche Idee ist, dass diese Methode sich selbst aufrufen muss. Dies bezeichnet man auch als Rekursion. Außerdem mussten Sie eine Methode recherchieren, um Objekte an ein Array anzuhängen, und zwar addObjectsFromArray:. Eine mögliche Lösung ist wie folgt:
 NSArray* flachMacher(id element) 

  // Fallunterscheidung: Ist das Element überhaupt instanziiert? 
  if (element == nil) { 
    return @[]; 
  } 
 
  // Ist das Element ein Array? 
  if ([element isKindOfClass:[NSArray class]]) { 
    NSArray* elementArray = (NSArray*)element; 
    NSMutableArray* sammler = [NSMutableArray new]; 
 
    // Durchlaufe das Array. 
    for (int i=0; i< [elementArray count]; i++) { 
      id unterElement = elementArray[i]; 
 
      if (![unterElement isKindOfClass:[NSArray class]]) { 
        // Bei einem einfachen Element: Füge es zum Array hinzu. 
        [sammler addObject:unterElement]; 
      } else { 
        // Hier ist der Trick: Rufe Dich selbst auf! 
        // Hänge dann die Elemente hinten dran! 
        [sammler addObjectsFromArray:flachMacher(elementArray[i])]; 
      } 
    } 
 
    // Gib das neue Array zurück. 
    return sammler; 
  } 
 
  // Ansonsten: Das Element ist kein Array, mache es zu einem. 
  return @[element]; 
}
Wenn Sie eine korrekte Lösung ohne fremde Hilfe gefunden haben, dann ist das wirklich eine Meisterleistung!

Lösungen zu Kapitel 12

2. Sie können das Programm vereinfachen, indem Sie die folgenden Änderungen in der Datei WSMasterViewController.m vornehmen:
(a) Vereinfachen Sie zunächst die Methode viewDidLoad aus Listing 12.2 :
  - (void)viewDidLoad 
  { 
    // Rufe die Methode der Basisklasse auf. 
    [super viewDidLoad]; 
  }
(b) Dann fügen Sie an den Anfang (vor der Zeile mit @implementation) die folgende Funktion hinzu:
  NSString* fizzbuzzEintrag(int x) 
  { 
    if (x % 15 == 0) { 
      // Jeder 15. Eintrag ist @"FizzBuzz". 
      return @"FizzBuzz"; 
 
 
    } else { 
      // Ansonsten ist jeder 5. Eintrag @"Buzz". 
      if (x % 5 == 0) { 
        return @"Buzz"; 
      } else { 
        // Und jeder 3. Eintrag @"Fizz". 
        if (x % 3 == 0) { 
          return @"Fizz"; 
        } else { 
          // Alle anderen Einträge sind die Zahl selbst. 
          return [NSString stringWithFormat:@"%i", x]; 
        } 
      } 
    } 
  }
(c) Dann ändern Sie die Methoden wie folgt:
  - (NSInteger)tableView:(UITableView *)tableView 
   numberOfRowsInSection:(NSInteger)section 
  { 
    // Die Zahl der Zeilen ist jetzt fest auf 10000 gelegt. 
    return 10000; 
  } 
 
  - (UITableViewCell *)tableView:(UITableView *)tableView 
           cellForRowAtIndexPath:(NSIndexPath *)indexPath 
  { 
    // Erzeuge eine Tabellenzelle. 
    UITableViewCell *cell = [tableView 
      dequeueReusableCellWithIdentifier:@"Cell" 
      forIndexPath:indexPath]; 
 
    // Der Text wird durch die Funktion fizzbuzzEintrag 
    // bestimmt. 
    cell.textLabel.text = fizzbuzzEintrag(indexPath.row + 1); 
 
    // Gib die Zelle zurück. 
    return cell; 
  } 
 
 
  - (void)prepareForSegue:(UIStoryboardSegue *)segue 
                   sender:(id)sender 
  { 
    // Ein Übergang vom Master zum Detail-Bildschirm. 
    if ([[segue identifier] isEqualToString:@"showDetail"]) { 
      // Hole die aktuelle Tabellenzeile. 
      NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; 
 
      // Hole den NSString in der Zeile, die der Benutzer 
      // ausgewählt hat. 
      NSString *object = fizzbuzzEintrag(indexPath.row + 1); 
 
      // Setze diesen als Eigenschaft detailItem im Detail- 
      // Controller. 
      [[segue destinationViewController] setDetailItem:object]; 
    } 
  }

Lösungen zu Kapitel 14

1. Eine mögliche Lösung sind die folgenden Erweiterungen:
(a) Verknüpfen Sie zunächst die drei Buttons mit den Eigenschaften nimmEinsButton, nimmZweiButton sowie nimmDreiButton.
(b) Fügen Sie eine Methode resetButtons hinzu, die alle drei Buttons aktiviert:
  - (void)resetButtons 
  { 
    // Alle Buttons sollen aktiv sein. 
    self.nimmEinsButton.enabled = YES; 
    self.nimmZweiButton.enabled = YES; 
    self.nimmDreiButton.enabled = YES; 
  }
(c) Fügen Sie an das Ende der Methode spielerZug: die folgenden Zeilen an:
  // (De-)aktiviere Buttons, wo notwendig. 
  if (self.holzHaufen.hoelzer == 2) { 
    // Nur die ersten beiden Buttons sollen aktiv sein. 
    self.nimmDreiButton.enabled = NO; 
  } else { 
    if (self.holzHaufen.hoelzer == 1) { 
      // Nur der erste Button soll aktiv sein. 
      self.nimmZweiButton.enabled = NO; 
      self.nimmDreiButton.enabled = NO; 
    } else { 
      // Alle Buttons sollen aktiv sein. 
      [self resetButtons]; 
    } 
  }
(d) Fügen Sie an das Ende der Methode neuesSpiel den folgenden Aufruf hinzu:
  [self resetButtons];

 

2. Wenn jeder Button die Methode spielerDruecktButton: mit dem Parameter sender vom Typ UIButton* aufruft, so kann die Funktion folgendermaßen aussehen:
- (IBAction)spielerDruecktButton:(UIButton*)sender 

  // Hole die Button-Beschriftung. 
  NSString* buttonText = sender.titleLabel.text; 
 
  // Extrahiere die Zahl der zu nehmender Hölzer. 
  NSString* holzZahlString = [buttonText 
                         stringByReplacingOccurrencesOfString:@"Nimm " 
                         withString:@""]; 
  unsigned holzZahl = [holzZahlString integerValue]; 
 
  // Rufe spielerZug mit dieser Zahl auf. 
  [self spielerZug:holzZahl]; 
}
Hierbei mussten Sie allerdings wieder einige Methoden recherchieren:
  • Wie kommen Sie an den Text des Buttons heran? (Mit der Eigenschaft titleLabel, die ihrerseits die Eigenschaft text besitzt.)
  • Wie können Sie von einem String bestimmte Zeichen entfernen? (Mit der Methode stringByReplacingOccurrencesOfString:withString:.)
  • Wie können Sie einen NSString in einen unsigned umwandeln? (Mit der Methode integerValue.)
Grundsätzlich werden auch erfahrene Programmierer nicht alle Eigenschaften, Methoden und Funktionalitäten auswendig können. In der Praxis müssen Sie daher oft Recherchen in der Dokumentation vornehmen. Machen Sie sich also keine Gedanken, wenn die Dokumentation auf den ersten Blick zu gewaltig erscheint: Mit der Zeit werden Sie sicherer werden und Dinge schneller finden können!

 

3. Es gibt mehrere Stellen an denen der Text »x Hölzer« ausgegeben werden soll. Daher bietet es sich an, eine eigene Methode zu schreiben, die einen grammatisch korrekten String zurückgibt. Diese Methode holzFormatierer: könnte so aussehen:
- (NSString*)holzFormatierer:(unsigned)n 

  // Vorbelegung: Fall eines Holzes. 
  NSString* ergebnis = @"1 Holz"; 
 
  // Anderer Fall: Es gibt 0 oder 2 oder mehr Hölzer. 
  if (n != 1) { 
    ergebnis = [NSString stringWithFormat:@"%i Hölzer", n]; 
  } 
 
  // Gib das Ergebnis zurück. 
  return ergebnis; 
}
Diese Methode müssen Sie dann in den Methoden neuesSpiel und in spielerZug: benutzen. Dazu ersetzen Sie die letzte Anweisung in neuesSpiel durch:
// Zeige die anfängliche Zahl der Hölzer im Label an. 
self.streichHolzAnzeige.text = [self 
                            holzFormatierer:self.holzHaufen.hoelzer];
Die Methode spielerZug: aus Listing 14.9 enthält 2 Texte, die die Streichholzzahl benutzen, mit der neuen Hilfsmethode holzFormatierer: sieht sie folgendermaßen aus:
- (void)spielerZug:(unsigned)zahl 

  // Spielerzug ausführen und Hölzer entfernen. 
  [self.holzHaufen entferneHoelzer:zahl]; 
 
  // Ist das Spiel verloren? 
  if ([self.holzHaufen istVerloren]) { 
    // Spielende: Der Spieler hat verloren. 
    [self spielEnde:@"Sie haben das Spiel verloren!"]; 
  } else { 
    // Sonst: Computerzug ausführen. 
    unsigned computerZug = [self.strategie macheZug]; 
 
    // Computerzug als Text anzeigen. 
    self.zugAnzeige.text = [NSString 
                            stringWithFormat:@"Ich nehme %@", 
                            [self holzFormatierer:computerZug]]; 
 
    // Ist das Spiel verloren? 
    if ([self.holzHaufen istVerloren]) { 
      // Spielende: Der Spieler hat gewonnen. 
      [self spielEnde:@"Sie haben das Spiel gewonnen!"]; 
    } 
  } 
 
  // Gib die aktuelle Zahl der Hölzer im Label aus. 
  self.streichHolzAnzeige.text = [self 
                             holzFormatierer:self.holzHaufen.hoelzer]; 
}

Lösungen zu Kapitel 16

1. Hier sind zwei mögliche Varianten:
  • Sie erzeugen einen ganzen Bildschirm voller Passwörter, der Benutzer kann davon eines auswählen.
  • Sie lassen den Benutzer ein Wort oder einen kurzen Satz eingeben, aus der er dann ein oder mehrere Passwortvorschläge generiert. Einige Programme machen dies tatsächlich so, denn auf diesem Wege kann der Benutzer sich Passwörter leichter merken (wenn diese tatsächlich nicht aufgeschrieben werden sollen).

Lösungen zu Kapitel 18

1. Ein Fehlerbericht könnte wie folgt aussehen:
  • Version: Mac OS X 10.9.3, Xcode 5.1.1, FizzBuzz aus dem Listing 18.1 in Ihrem Buch Objective-C: Der Sprachkurs für Einsteiger und Individualisten, 1. Auflage.
  • Schritte, um das Verhalten zu reproduzieren: Eingabe des Programms genauso wie im abgedruckten Listing gezeigt unter Xcode 5.1.1. Übersetzen und Starten, Ausgabe im Debugger-Fenster von Xcode.
  • Erwartetes Verhalten: Alle Vielfachen von 15 erhalten den Eintrag »FizzBuzz«.
  • Tatsächliches Verhalten: Alle Vielfachen von 15 erhalten den Eintrag »Fizz«.
  • Weitere Beobachtungen: Das Programm aus dem Download-Bereich Ihrer Webseite weist das gleiche Fehlverhalten auf!
2. Die folgenden drei Testcases führen die Prüfung durch:
- (void)testDummerZug 

  // Erzeuge die zu testende Klasse. 
  WSDummerComputerZug* dummerZug = [WSDummerComputerZug new];
  // Erzeuge das Hilfsobjekt. 
  WSHolzHaufen* haufen = [WSHolzHaufen new]; 
  dummerZug.derHaufen = haufen;
  // Vorbedingung. 
  haufen.hoelzer = 10;
  // Prüfung, ob der Zug korrekt ist. 
  XCTAssert([dummerZug macheZug] == 1, @"Der Zug ist nicht gleich 1."); 
  XCTAssert(haufen.hoelzer == 9, @"Der Haufen hat keine 9 Hölzer."); 
}
- (void)testKlugerZug 

  // Erzeuge die zu testende Klasse. 
  WSKlugerComputerZug* klugerZug = [WSKlugerComputerZug new];
  // Erzeuge das Hilfsobjekt. 
  WSHolzHaufen* haufen = [WSHolzHaufen new]; 
  klugerZug.derHaufen = haufen;
  // Vorbedingung. 
  haufen.hoelzer = 10;
  // Prüfung, ob der Zug korrekt ist. 
  XCTAssert([klugerZug macheZug] == 1, @"Der Zug ist nicht gleich 1."); 
  XCTAssert(haufen.hoelzer == 9, @"Der Haufen hat keine 9 Hölzer."); 
}
- (void)testKlugerZug2 

  // Erzeuge die zu testende Klasse. 
  WSKlugerComputerZug* klugerZug = [WSKlugerComputerZug new];
  // Erzeuge das Hilfsobjekt. 
  WSHolzHaufen* haufen = [WSHolzHaufen new]; 
  klugerZug.derHaufen = haufen;
  // Vorbedingung. 
  haufen.hoelzer = 11;
  // Prüfung, ob der Zug korrekt ist. 
  XCTAssert([klugerZug macheZug] == 2, @"Der Zug ist nicht gleich 2."); 
  XCTAssert(haufen.hoelzer == 9, @"Der Haufen hat keine 9 Hölzer."); 
}

Über den Autor

IMG

Dr. Wolfram Schroers ist nicht nur Physiker, sondern auch begeisterter Macianer und hat dadurch schon früh die iPhone- und iPad-Entwicklung für sich entdeckt. Als selbstständiger Programmierer und Berater entwickelt er inzwischen seit vielen Jahren Enterprise-Apps für international tätige Großkunden und DAX-Unternehmen.

Daneben steht er auch gerne der nächsten Generation von Entwicklern mit Rat und Tat zur Seite und unterstützt Studenten bei eigenen Projekten mit seiner geballten technischen und unternehmerischen Erfahrung. Sein Händchen für Didaktik hat dabei schon über einige Verständnishürden hinweg geholfen.

Dass er schwierige Dinge einfach erscheinen lassen kann, liegt wohl nicht zuletzt auch an seiner Zweitkarriere als Zauberer. Ansonsten macht er in seiner Freizeit gerne Karate und kümmert sich um seine gerade geborene Tochter Marta Cheng-Wei.

1 Alternativ können Sie auch Safari starten und die URL https://itunes.apple.com/de/app/xcode/id497799835?mt=12 eingeben. Klicken Sie auf IM MAC APP STORE ANSEHEN und der App Store startet mit Xcode.

Kapitel 1 Warum zahlt es sich aus, Objective-C zu lernen?

Programmieren von Computern gilt im Allgemeinen als kompliziert, schwierig und aufwendig zu lernen. In einem gewissen Sinne ist das alles richtig, aber es gibt noch einen anderen wichtigen Punkt: Computer zu programmieren kann vor allem richtig Spaß machen!

1.1 Warum ausgerechnet Objective-C?

In diesem Buch werden Sie die wichtigsten Grundlagen des Programmierens lernen, und zwar anhand der Programmiersprache Objective-C. Bevor ich Sie ins kalte Wasser werfe und Ihnen Programmcodes, Variablen und Strukturen um die Ohren haue, möchte ich allerdings die allerwichtigste Frage klären: Warum lernen Sie Objective-C, um einen Computer zu programmieren?

Im Laufe der Jahre gab es sehr viele verschiedene Programmiersprachen. Einige davon basierten auf sehr cleveren Ideen, andere waren auf bestimmte Anwendungen spezialisiert. Aber die allermeisten starben nach ein paar Jahren aus oder verschwanden in einzelnen Nischen. Richtig »sterben« tun Programmiersprachen zwar eigentlich nicht, aber irgendwann gibt es keine neuen Projekte mehr, die mit ihnen begonnen werden, und erfahrene Programmierer finden keine Anstellung mehr. Eine Alternative ist hier ständiges Umlernen.

Oder eine Sprache zu wählen, die heute nicht nur kurzfristig »in Mode« ist, sondern die seit vielen Jahren in aller Munde bzw. auf aller Rechner ist. Sodass jeder, der sie einmal gelernt hat, mit Sicherheit auch in vielen Jahren noch mit Programmen arbeiten kann, die heute geschrieben werden.

Die gute Nachricht: So eine Sprache gibt es tatsächlich und es ist die Programmiersprache C! C wurde von 1969 bis 1973 in den AT&T Bell Labs von Dennis Ritchie entworfen. Im Jahre 1978 schrieb Dennis Ritchie zusammen mit Brian Kernighan die erste Ausgabe des heute als Standardwerk geltenden Klassikers The C Programming Language.

In der Sprache C wurden ebenfalls im Jahre 1973 große Teile des Betriebssystems Unix entwickelt, dessen Konzepte auch heute noch die Grundlage der meisten modernen Rechner bilden. Die Sprache C hat ebenfalls viele weitere Sprachen beeinflusst, u. a. C#, Java, JavaScript und eben auch Objective-C. Wenn Sie C beherrschen, können Sie viele Konstrukte dieser Sprachen auf Anhieb lesen und verstehen. Objective-C ist im Vergleich zu anderen Sprachen sehr eng an C angelehnt und erweitert C um lediglich eine kleine Menge an Spracherweiterungen.

Trotz ihres Einflusses auf andere Sprachen ist C heute weiterhin eine der wichtigsten und verbreitetsten Sprachen. In vielen aktuellen Entwicklungsprojekten wird C verwandt und die Bedeutung von C ist ungebrochen hoch. Siehe dazu z. B. den Index von Tiobe Software unter http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html, wo C nicht nur jetzt die absolute Nummer 1 ist, sondern als einzige Sprache auch in den vergangenen 25 Jahren gewesen ist. Ein guter Objective-C-Entwickler kann daher auch in C entwickeln und mit reinen C-Projekten Geld verdienen.


Hintergrund
Vielleicht wundern Sie sich, dass eine so »alte« Sprache auch heute noch eine so große Rolle spielt, obwohl die Computer selbst so viel leistungsfähiger geworden sind und sich weiterentwickelt haben. Dafür gibt es zwei Gründe: Zum einen sind die Computer zwar schneller und leistungsfähiger geworden, aber an ihrer grundsätzlichen Funktionsweise hat sich in den letzten Jahrzehnten nicht viel geändert. Zum anderen hat sich viel an der Art weiterentwickelt, wie wir einen Computer bedienen. Aber dies bezieht sich eben weniger auf die Technik als vielmehr auf die Benutzerführung. Aus der Sicht Ihres Macs macht es keinen großen Unterschied, ob Sie einen Punkt durch Koordinatenwerte angeben, mit einer Maus ansteuern oder ihn mit dem Finger berühren. Aus Ihrer Sicht als Benutzer macht es aber einen sehr, sehr großen Unterschied!

Dass Objective-C heute eine der wichtigsten Programmiersprachen ist, hat sie dem Umstand zu verdanken, dass sie die Sprache des Vorgängers von Mac OS X, NeXTSTEP, war. NeXTSTEP war das System von NeXT, die Firmengründung von Steve Jobs, nachdem er Apple verlassen hatte. Nach dem ersten Release 1988 war dem System zwar kein kommerzieller Erfolg beschieden, aber viele Konzepte, die dort zum ersten Mal öffentlich vorgestellt wurden, wurden von anderen Firmen aufgegriffen und sind heute die Grundlagen der meisten Programmiersprachen und ihrer Hilfsmittel – zum Beispiel das Model-View-Controller-System (MVC) oder ein grafischer Editor für Benutzeroberflächen.

Nachdem NeXT von Apple im Jahre 1996 aufgekauft worden ist, wurde die aktuelle Betriebssystemlinie Mac OS X auf der Basis von NeXTSTEP entwickelt. Später wurde auch das iPhone auf derselben Technologie aufgebaut. Auch wenn sich die Bedienung unterscheidet, so ist doch die Programmierung von iPhone, iPad und Mac OS X sehr ähnlich und alle diese Systeme können sich auf die Technologie berufen, die sich 25 Jahre lang bewährt hat und ständig weiterentwickelt worden ist, um sich dem Markt und seinen Anforderungen anzupassen.

Die Prinzipien und Grundlagen, die Sie in diesem Buch lernen werden, sind allerdings auch in vielen anderen Sprachen und Systemen anwendbar. So hat Apple vor Kurzem eine weitere, eigene Programmiersprache vorgestellt: Die Sprache Swift, die Objective-C in vielen Bereichen ergänzen soll. Apple hat in der Vergangenheit zwar auch einige Experimente gemacht, die fehlgeschlagen sind (wie beispielsweise eine enge Integration der Sprachen Python und Ruby). Apples letzte »eigene« Sprache, Dylan, hat heute auch nur noch historische Relevanz. Ich selbst, als Entwickler kommerziell erfolgreicher Projekte, werde daher mindestens noch 2+ Jahre warten müssen, bevor ich entscheiden kann, ob Swift sich durchsetzen wird oder nicht.

Aus diesem Grund wird das Wissen, das Sie sich in diesem Buch aneignen, auch noch Jahrzehnte später von Bedeutung sein, wenn andere, »modernere« Sprachen schon längst wieder von der Bildfläche verschwunden sind. Vor allem aber hat sie sich aufgrund ihrer langen Geschichte als verlässliche und ausgereifte Programmiersprache bewährt, wodurch sie einen guten Einstieg bietet.

1.2 Welche Programme Sie hier programmieren werden

Sie werden in diesem Buch die Grundlagen der Programmierung in der Programmiersprache Objective-C erlernen. Dazu werden Sie die Programmierung mit Xcode lernen. Das ist Apples eigenes System für die Programmierung von iPhone, iPad und Mac OS X. Einige Konzepte funktionieren auf allen Plattformen gleich, aber andere sind auch recht unterschiedlich. Daher zeige ich Ihnen Beispiele für alle diese Plattformen.

Abbildung 1.1 zeigt zwei Apps, die Sie in späteren Kapiteln schreiben werden: eine App zum Erzeugen von Passwörtern und ein einfaches Spiel, die beide auf dem iPhone laufen. Das Spiel werden Sie anschließend auch auf das iPad und auf Mac OS X portieren. Die Beispiele für das iPhone und das iPad werden zunächst nur auf dem sogenannten iOS Simulator laufen, das heißt nicht auf einem echten iPhone oder iPad.

IMG IMG IMG

Abb. 1.1 Beispiele der Apps, die Sie später für das iPhone entwickeln werden. Die ersten Bilder zeigen eine App für Passwörter und die zweiten ein Spiel mit Streichhölzern.

1.3 Was Sie mitbringen müssen

Um mit diesem Buch arbeiten zu können, sollten Sie mit einem Mac umgehen können und die Bedienung und Benutzerführung – wie beispielsweise Dateiverwaltung, Farbauswahl und Zeichensätze etc. – der Plattform verstehen und anwenden können. Programmierkenntnisse in anderen Sprachen sind von Vorteil, aber nicht zwingend erforderlich.

Um den Beispielen in diesem Buch folgen zu können, benötigen Sie einen Mac mit Mac OS X ab Version 10.9 aufwärts. Einige Beispiele sind für ein iPhone und ein iPad gedacht. Aber diese werden zunächst nur auf dem Mac mit dem Simulator ausgeführt. Dieser simuliert ein iPhone oder ein iPad auf dem Mac und sieht so ähnlich aus und verhält sich ähnlich wie diese Geräte.

Sie brauchen außerdem das bereits genannte Programm Xcode von Apple. Dieses enthält alles, um Programme zu schreiben und auszuführen. Sie können es kostenlos von Apple bekommen. Wie das geht, ist gleich zu Anfang in Kapitel 2 beschrieben.

Um die Beispiele tatsächlich auf einem echten iPhone oder iPad auszuführen, benötigen Sie zusätzlich eine Mitgliedschaft im Apple-Entwicklerprogramm, die 80 Euro pro Jahr kostet. Dazu gibt Apples Portalseite https://developer.apple.com/programs/ios/ auf Englisch weitere Informationen. Diese Mitgliedschaft ist für das eigentliche Erlernen der Programmierung allerdings nicht erforderlich.

Für ein Kapitel sollten Sie etwa ein bis zwei Stunden investieren, um es nachzuprogrammieren. Dadurch lernen Sie, wie Sie die Werkzeuge benutzen, insbesondere auch, wie Sie die Entwicklungsumgebung richtig einsetzen. Für die Übungen sollten Sie zusätzlich etwa ein bis drei Stunden Zeit investieren, in späteren Kapiteln u.U. auch mehr. Ich empfehle Ihnen hierbei, anfangs nicht mehr als eine Stunde pro Tag, auch am Wochenende nicht mehr. Die besten Ergebnisse erzielen Sie, wenn Sie kleine Schritte machen und auch gelegentlich die älteren Kapitel wiederholen. Wenn Sie diesen Ratschlägen folgen, beherrschen Sie am Ende die Grundlagen des Programmierens und können sich viele Dinge selbstständig erarbeiten.

1.4 Wie Sie mit diesem Buch Erfolg haben

Die einzelnen Kapitel dieses Buches bauen aufeinander auf. Das bedeutet, dass Sie versuchen sollten, die Kapitel in der Reihenfolge zu bearbeiten, in der sie in diesem Buch auftauchen. Auch empfehle ich Ihnen, insbesondere alle Beispiele auszuprobieren und auch selbst ein bisschen zu experimentieren. Dadurch lernen Sie erfahrungsgemäß am besten.

Die Codebeispiele, die im Buch beschrieben werden, können Sie auch von den Webseiten http://www.downloads.wrox-press.de und http://objective-c-sprachkurs.de herunterladen. Grundsätzlich empfehle ich Ihnen allerdings, die Beispiele Schritt für Schritt selbst nachzuprogrammieren. Denn sie enthalten nicht nur die fertigen Programmtexte, sondern auch die eigentlichen Arbeitsschritte, die typischerweise unter Xcode erforderlich sind, um bestimmte Ergebnisse zu erzielen. In späteren Kapiteln werde ich voraussetzen, dass Sie beispielsweise die Schriftgröße eines Textes selbstständig ändern können, ohne dass ich die dafür notwendigen Schritte wiederholen muss.

Am Ende der meisten Kapitel finden Sie ein Unterkapitel »Fortgeschrittenes«. Dort finden Sie zum einen weiterführende Anmerkungen zu den Themen, die Sie im Laufe des Kapitels kennengelernt haben. Zum anderen finden Sie dort Hinweise und Stichworte zu Themen, die über dieses Buch hinausgehen und mit denen Sie sich beschäftigen können, wenn Sie bei einem Thema weiter in die Tiefe gehen möchten. Dieses Unterkapitel ist für das Verständnis der folgenden Kapitel nicht zwingend notwendig und Sie können es beim ersten Lesen überspringen.

Außerdem finden Sie am Ende der meisten Kapitel Übungsaufgaben. Diese sollten Sie nicht überspringen, sondern damit experimentieren. Sie sind einerseits dazu gedacht, Sie anzuregen, neue Dinge auszuprobieren und zu bearbeiten. Andererseits geht es aber auch darum, dass Sie bereits erlernte Arbeitsschritte nochmals wiederholen. Dadurch geht Ihnen die Arbeit leichter von der Hand und Sie lernen einfacher und besser.

Die Übungen haben unterschiedliche Schwierigkeitsgrade. Einige werden Sie möglicherweise beim ersten Lesen nicht lösen können. Wenn Sie daher eine Übung nicht auf Anhieb schaffen, empfehle ich Ihnen, erstmal weiterzulesen und später das Kapitel nochmals durchzuarbeiten.

Die Lösungen der meisten Übungsaufgaben finden Sie in Anhang B. Einige Übungen in den späteren Kapiteln sind allerdings zu komplex und erfordern ein eigenes Projekt. Diese können Sie wiederum auf der oben genannten Webseite finden.

Es ist sehr wichtig, dass Sie Spaß daran haben, Programme zu schreiben und Probleme zu lösen. In der Praxis verbringen Sie die meiste Zeit nicht mit dem Schreiben von Programmen, sondern mit dem Lesen von Programmtexten anderer Entwickler sowie mit der Fehlersuche – sowohl in Ihrem eigenen Programm als auch in anderer Leute Programmtexten. Dies macht auch in der Gruppe viel Spaß. Darum treffen Sie sich mit anderen Leuten, nehmen Sie an CocoaHeads-Meetings teil und fragen Sie erfolgreiche Entwickler, wie diese bei ihren Programmen vorgegangen sind und vorgehen. CocoaHeads sind eine Gruppe von Interessierten, die sich jeden Monat zu Themen rund um Mac OS und iOS-Entwicklung treffen. Diese Treffen finden in allen größeren Städten statt. Die Treffpunkte in Ihrer Nähe finden Sie unter http://cocoaheads.org.

Kapitel 10 Verladen von Objekten – Alles über Datencontainer


Inhalt
  • Was sind Arrays: Daten sortiert nach Zahlen
  • Wie Sie Zahlen in Containern lagern
  • Praxisbeispiel: FizzBuzz mit Objekten
  • Was sind Dictionaries: Daten sortiert nach Strings
  • Wie Sie Daten speichern und laden

Bisher haben Sie Variablen kennengelernt, die »einfache« Daten wie beispielsweise eine Ganzzahl oder eine Fließkommazahl enthalten können. Und auch Zeiger auf Objekte, die komplizierte Daten und sogar Funktionalität enthalten können. Aber was machen Sie, wenn Sie noch gar nicht wissen, wie viele Informationen ein Benutzer Ihres Programmes speichern möchte?

Denken Sie mal an das Musikprogramm auf dem iPhone oder an iTunes auf dem Mac. Manche Benutzer haben vielleicht nur ein Dutzend Lieder, andere haben jedoch Tausende und diese sind in Dutzenden zum Teil verschachtelten Playlisten enthalten.

Die Antwort auf diese Fragen lautet: Container-Klassen. Ein anderer gängiger Begriff lautet Datenfelder. Ich bevorzuge für diese Klassen in diesem Kapitel weitgehend die englischsprachigen Begriffe, denn diese werden von Programmierern im Objective-C Umfeld sofort verstanden.

Eine Container-Klasse ist ein Objekt, das mehrere andere Objekte speichert und das auch in der Lage ist, Objekte zu entfernen oder hinzuzufügen. Container-Klassen werden von Apple z. B. für die Listen von Musikdateien und die Playlisten verwendet oder auch für die Sammlung von Adressen, die Sie gespeichert haben. Sie sind sehr leistungsfähige und flexible Objekte und bieten auch sehr umfangreiche und nützliche Funktionen an.

10.1 Arrays – sortiert nach Zahlen

Ein Array ist ein Container, der Objekte nach einem Index speichert. Es ist wie ein Karteikasten, dessen Karten durchlaufend nummeriert sind. Auf jeder dieser Karteikarten findet sich ein Zeiger auf ein Objective-C-Objekt. Dabei können im Array alle möglichen Objective-C-Objekte enthalten sein, es gibt keine Einschränkungen bzgl. des Datentyps.


Für Umsteiger
Datencontainer in anderen Sprachen wie C++ oder Java bieten die Möglichkeit sogenannter typisierter Arrays an. Diese dürfen nur einen bestimmten Typ haben. In Objective-C gibt es ausschliesslich untypisierte Arrays, die Zeiger auf beliebige Objective-C Objekte aufnehmen können.

Ein Array ist dabei selbst ein Objekt der Klasse NSArray. Die Klasse NSArray bietet nun eine Reihe von Funktionen an, um mit den Objekten zu arbeiten, die im Array enthalten sind – es wäre ja schließlich wenig sinnvoll, wenn Sie die Karteikarten nicht lesen könnten!

IMG

Abb. 10.1 Ein Beispiel für ein Array mit vier Objekten

Abbildung 10.1 zeigt ein Array, das vier Instanzen von Objekten aus Kapitel 8 enthält, ein Objekt vom Typ WSUnternehmer, zwei Objekte vom Typ WSAngestellter und ein Objekt vom Typ WSPraktikant. Beachten Sie dabei auch, dass die Zählung mit der Position 0 beginnt und – in diesem Beispiel mit vier Objekten – bei drei endet. Ja, genau, die virtuellen Karteikarten sind immer mit einem Index nummeriert, der bei Null anfängt. Das bedeutet, dass Sie das n.te Objekt immer an der Stelle mit Index n-1 finden.

10.1.1 Arrays benutzen – schnell und bequem

Genauso wie Sie einen NSString schnell und bequem mit der Schreibweise @"" erzeugen konnten, gibt es eine bequeme und einfache Schreibweise, um ein NSArray zu erzeugen. Dazu schreiben Sie einfach @[Elemente ...]. Um beispielsweise ein Objekt firma vom Typ NSArray mit den vier Objekten in der Abbildung 10.1 zu erzeugen, können Sie Folgendes schreiben:

// Erzeuge die 4 Objekte. 
WSUnternehmer* steve = [WSUnternehmer new]; 
WSAngestellter* woz = [WSAngestellter new]; 
WSAngestellter* tim = [WSAngestellter new]; 
WSPraktikant* larry = [WSPraktikant new]; 
 
// Erstelle ein Array aus diesen 4 Objekten. 
NSArray* firma = @[steve, woz, tim, larry];

Wenn Sie später wissen möchten, wie viele Objekte im Array enthalten sind, benutzen Sie die Methode count:

 int mitarbeiter = [firma count];

Im Falle des Beispiels oben enthält die Variable mitarbeiter nun die Zahl vier.

Um wieder auf einzelne Objekte zuzugreifen, die im Array enthalten sind, schreiben Sie einfach firma[index], wobei index eine Variable oder eine Zahl zwischen 0 und der Zahl der Elemente minus 1 sein darf. Beispielsweise liefert

 firma[1]

das Objekt woz zurück. Auf diesem Wege können Sie auf alle Elemente des Arrays zugreifen und diese dann weiterbenutzen, wenn Sie wollen.

10.1.2 Arrays, die zu Mutanten werden

Dadurch könnte das Array firma alle Mitarbeiter enthalten. Da es aber vom Typ NSArray ist, kann es aber nicht mehr verändert werden. D. h., es wäre unmöglich, neue Mitarbeiter einzustellen und vorhandene zu entlassen. Beides passt nicht wirklich zu einer modernen Firma, wo wir solche Operationen sehr häufig und schnell vornehmen wollen. Zu diesem Zweck gibt es von NSArray eine wichtige Unterklasse, nämlich das NSMutableArray. Letzteres bietet Methoden zum Hinzufügen und Entfernen von Objekten an, die NSArray fehlen. Damit ist NSArray »nur« eine einfache Form des Karteikastens, dessen Karten von Anfang an festgelegt sind. Beachten Sie, dass Sie die Objekte selbst immer noch ändern können, d. h., Sie können mit den Instanzen steve, woz, tim und larry alles machen, als wenn diese nicht in ein Array gesteckt worden wären.

Um ein NSMutableArray zu erzeugen, können Sie mit einem Array anfangen und dann die Methode mutableCopy verwenden:

 NSMutableArray* flexibel = [@[steve, woz, tim, larry] mutableCopy];

Auf diesem Wege erhalten Sie ein Objekt flexibel, bei dem Sie Objekte hinzufügen und entfernen können. An dieser Stelle zahlt sich aber auch einmal mehr die objektorientierte Welt aus: Alles, was Sie weiter oben über NSArray gelernt haben, funktioniert genauso bei NSMutableArray! D.h., Sie können die Zahl der Objekte mit count abfragen und einzelne Objekte mit den eckigen Klammern direkt bekommen.

// Ein Array mit allen meinen Songtiteln. 
NSMutableArray* meineSongs = [ 
  @[@"Wenn Sie auf diesen Tango schwört", 
    @"95 Manntage", 
    @"10 kleine Minderleister", 
    @"Wo sind all die Arrays hin", 
    @"Blackberry fields never"] mutableCopy]; 
 
// Gib alle Titel der Reihe nach aus. 

  printf("%s\n", [meineSongs[index] UTF8String]);