Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Geleitwort
Vorwort
1 Hello iPhone
2 Die Reise nach iOS
3 Sehen und anfassen
4 Alles unter Kontrolle
5 Daten, Tabellen und Controller
6 Models, Layer, Animationen
7 Programmieren, aber sicher
8 Datenserialisierung und Internetzugriff
9 Multimedia
10 Jahrmarkt der Nützlichkeiten
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
Apps programmieren für iPhone und iPad von Klaus M. Rodewig, Clemens Wagner
Das umfassende Handbuch
Buch: Apps programmieren für iPhone und iPad

Apps programmieren für iPhone und iPad
Rheinwerk Computing
1172 S., geb., mit DVD
49,90 Euro, ISBN 978-3-8362-2734-6
Pfeil 2 Die Reise nach iOS
Pfeil 2.1 Objektorientierte Programmierung
Pfeil 2.1.1 Objekte und Abstraktion
Pfeil 2.1.2 Vererbung
Pfeil 2.1.3 Überschreiben von Methoden und spätes Binden
Pfeil 2.1.4 Objektorientierung in Objective-C
Pfeil 2.1.5 Die Architektur von iOS-Programmen
Pfeil 2.2 Hefte raus, Klassenarbeit!
Pfeil 2.2.1 Controller und View in der Praxis
Pfeil 2.2.2 Modellbau
Pfeil 2.2.3 Initializer und Methoden
Pfeil 2.2.4 Vererbung
Pfeil 2.2.5 Kategorien
Pfeil 2.2.6 Protokolle
Pfeil 2.2.7 Vorwärtsdeklarationen
Pfeil 2.2.8 Kommunikation zwischen den Schichten
Pfeil 2.2.9 Delegation
Pfeil 2.2.10 Key-Value-Coding
Pfeil 2.3 Speicherverwaltung und Propertys
Pfeil 2.3.1 Stack und Heap
Pfeil 2.3.2 Starke und schwache Referenzen
Pfeil 2.3.3 Autoreleasepools
Pfeil 2.3.4 Propertys und Accessoren
Pfeil 2.4 In den Tiefen der Speicherverwaltung
Pfeil 2.4.1 Manuelles Referenzzählen
Pfeil 2.4.2 Die Speicherverwaltungsregeln für das manuelle Referenzzählen
Pfeil 2.4.3 Autoreleasepools
Pfeil 2.4.4 Der Referenzzähler
Pfeil 2.4.5 Automatisches Referenzzählen
Pfeil 2.4.6 Weakie und die starken Zeiger
Pfeil 2.4.7 Einzelgänger
Pfeil 2.4.8 Migration bestehender Projekte
Pfeil 2.5 Das Foundation-Framework
Pfeil 2.5.1 Mutables und Immutables
Pfeil 2.5.2 Elementare Klassen
Pfeil 2.5.3 Collections
Pfeil 2.6 Blöcke
Pfeil 2.6.1 Rückruffunktionen
Pfeil 2.7 Namenskonventionen
Pfeil 2.8 Zusammenfassung

Rheinwerk Computing - Zum Seitenanfang

2.6BlöckeZur nächsten Überschrift

Mit iOS 4 hat Apple Blöcke eingeführt, und seit iOS 5 nimmt der Anteil der API-Funktionen in iOS, die Blöcke verwenden oder unterstützen, rapide zu. Auch wenn Blöcke erst in jüngster Zeit Einzug in Objective-C gehalten haben, ist die Idee dahinter schon alt, älter als Objective-C. Ein Vorgänger von Objective-C, Smalltalk, kannte bereits das Konzept von Blöcken als elementaren Bestandteil. Blöcke sind in Smalltalk selbst als Objekte konzipiert, können also Nachrichten empfangen. Genauso verhält es sich in Objective-C. Auch dort ist ein Block ein Objekt und kann dementsprechend wie ein Objekt in die Kommunikation einbezogen werden.

Das theoretische Konzept für Blöcke, die Closures, stammt aus der funktionalen Programmierung und wurde erstmalig mit LISP in einer Programmiersprache umgesetzt. Es erfreut sich in den letzten Jahren zunehmender Beliebtheit, was sich auch an der Integration dieses Konzepts in andere Programmiersprachen – wie jüngst Java und C++ – zeigt.

Zunächst ein Beispiel für Blöcke: Ein Fischhändler verkauft tagein, tagaus auf dem Markt Makrelen. Die Kunden kommen zu ihm, um eine bestimmte Menge an Fisch zu kaufen. Dafür geben sie ihm einen entsprechenden Geldbetrag. Der Fischhändler braucht also eine Funktion, die einer Menge Fisch einen Geldbetrag zuordnet. Seine Makrelen bekommt der Fischhändler auf dem Großmarkt, wo sich die Kilopreise für Makrelen jedoch jeden Tag ändern.

Der Fischhändler muss sich also jeden Morgen auf dem Markt neu überlegen, wie viel er für das Kilogramm Makrele von seinen Kunden haben will. Sie kennen ja das alte Marktwirtschaftsspielchen: Verlangt er zu viel, kauft keiner den Fisch. Ist der Fisch zu billig, dann geht er pleite und kann seiner Frau Ilsebill nicht den ihr zustehenden Lebensstandard (Palast, Adelstitel usw.) erfüllen. Da der gute Mann hingegen nicht jeden Tag ausführliche Marktforschungsanalysen betreiben möchte, hat er die einfache Faustregel aufgestellt, den Fisch für den doppelten Einkaufspreis zu verkaufen. In die Verkaufsfunktion kommen also mit dem Einkaufspreis und dem Faktor zwei zusätzliche Werte ins Spiel.

Ohne Closures müssten Sie diese beiden Werte entweder als zusätzliche Funktionsparameter oder über globale Variablen umsetzen. Zusätzliche Funktionsparameter haben den Nachteil, dass Sie sie immer beim Funktionsaufruf verfügbar haben müssen. Globale Variablen schränken Ihre Funktion hingegen auf eine Variante ein. Wenn Sie beispielsweise mit einem Programm den Makrelenmarkt simulieren wollen, haben alle Fischverkäufer den gleichen Großmarktpreis bezahlt und berechnen den Verkaufspreis mit dem gleichen Faktor.

Mit Closures können Sie hingegen für jeden Fischverkäufer eine eigene Verkaufsfunktion erstellen. Das könnte dann so aussehen:

typedef double Money;
typedef double Weight;
Money thePurchasePrice = 4.20; // Großmarktpreis
double thePurchaseFactor = 2.0; // Aufschlagsfaktor
Money (^thePurchaseFunction)(Weight) = ^(Weight inWeight) {
return inWeight * thePurchasePrice * thePurchaseFactor;
};
Money thePrice = thePurchaseFunction(2.5); // = 21 Euro

Listing 2.127 Erzeugung eines Blocks

Listing 2.127 erzeugt einen Block, speichert ihn in der Variablen thePurchaseFunction und ruft sie einmal mit dem Wert 2,5 auf. Für die Berechnung verwendet sie die Variablen thePurchasePrice und thePurchaseFactor, die der Code außerhalb des Blocks definiert. Dabei speichert das Laufzeitsystem diese Werte allerdings als Kontext des Blocks ab. Wenn Sie die Werte der Variablen ändern, verändern Sie diese Werte in dem Block nicht. Wenn Sie also die Zeilen aus Listing 2.128 an Listing 2.127 anhängen, erhalten Sie den gleichen Verkaufspreis wie oben und nicht 18,90 Euro. Der Block merkt sich die Variablenwerte bis zu ihrer Ausführung.

thePurchaseFactor = 1.8;
thePrice = thePurchaseFunction(2.5); // = 21 Euro

Listing 2.128 Eine Änderung der Variablen ändert nicht den Block.

Das ist bei vielen Anwendungsfällen wichtig, da Sie häufig über einen Block beschreiben, was nach einer bestimmten Operation passieren soll. Wann Cocoa Touch diesen Block ausführt, kann Ihnen dabei egal sein. Er hat ja genau die Werte, die Sie bei seiner Erzeugung festgelegt haben.

Rückgabetyp von Blöcken

Blöcke sind sehr streng bezüglich ihres Rückgabetyps. Im Gegensatz zu vielen C-Anweisungen konvertieren sie den Wert nicht automatisch in den richtigen Typ. Das liegt daran, dass die Definition die Deklaration nicht kennt. Die Definition muss also den Rückgabetyp aus der Return-Anweisung extrahieren:

^(void) { return 1; } // Rückgabetyp int
^(void) { return 1.0; } // Rückgabetyp double
^(void) { return 1.0f; } // Rückgabetyp float

Sie müssen also sicherstellen, dass die Rückgabetypen der Blockdeklaration und ?definition übereinstimmen.

Das gilt übrigens auch, wenn der Block ein Objekt zurückgeben soll. Auch hier wandelt der Compiler den Rückgabetyp nicht automatisch in die Superklasse um:

id ^(theBlock)(void) = ^(void) { return @"Hello"; }; // Fehler
NSObject *^(theBlock)(void) { return @"Hello"; }; // Fehler

Sie müssen hier explizit den Rückgabetyp in den richtigen Rückgabetyp umwandeln:

id ^(theBlock)(void) = ^(void) { return (id)@"Hello"; }; // OK

Wenn Sie Blöcke in Ihren Programmen verwenden möchten, sollten Sie wie in Listing 2.130 dafür über typedef eigene Typen definieren. Das vereinfacht Ihnen die Deklaration von Variablen.

Wie bereits erwähnt, können Sie diese Funktion nicht nur aufrufen, sondern auch wie ein Objekt behandeln. Beispielsweise können Sie die Funktion als Parameterwert an eine andere Funktion oder eine Methode übergeben. Dabei ist es bei manuellem Referenzzählen wichtig, dass Sie den Block nur innerhalb des Bereichs verwenden dürfen, in dem Sie sie deklariert haben. Folgendes dürfen Sie beispielsweise nicht machen:

if(YES) {
thePurchaseFunction = ^(Weight inWeight) {
return inWeight * thePurchasePrice * 2.1;
};
}
thePrice = thePurchaseFuntion(3.1); // Fehler

Listing 2.129 Bereichsverletzungen mit Blöcken

Die letzte Anweisung in Listing 2.129 ist eine Bereichsverletzung, weil sie den Block außerhalb des deklarierenden Bereichs (if-Block) verwendet. Sie dürfen einen Block auch nicht einfach als Funktions- oder Methodenergebnis zurückgeben, da Sie auch hier ja den deklarierenden Block verlassen. Bei Bereichsverletzungen von Blöcken gibt Ihnen der Compiler in vielen Fällen keine Fehlermeldungen aus. Es kann sogar vorkommen, dass das Programm trotz einer Bereichsverletzung funktioniert.

Sie können Bereichsverletzungen vermeiden, indem Sie den Block kopieren. Hier treten die Objekteigenschaften von Blöcken zutage. Sie können nämlich auch an Blöcke die Methoden retain, release, autorelease und copy senden, wobei bei automatischem Referenzzählen natürlich nur die Methode copy erlaubt ist. Wenn Sie also einen Block aus einer Methode zurückgeben möchten, schicken Sie ihm ein copy und gegebenenfalls noch ein autorelease, wenn Sie manuelles Referenzzählen verwenden: [Anm.: Erinnern Sie sich noch an die erste Speicherverwaltungsregel?]

typedef double (^FunctionType)(double inValue);
- (FunctionType)createFunction {
return [[^(double inValue) {...} copy] autorelease];
}
- (double)evaluateFunctionWithValue:(double)inValue {
FunctionType theFunction = [self createFunction];
return theFunction(inValue);
}

Listing 2.130 Das Kopieren von Blöcken

Automatisches Referenzzählen und Blöcke

Automatisches Referenzzählen vereinfacht die Handhabung von Blöcken als Rückgabewerte. Sie können hier beispielsweise einfach

return ^(void){ ... };

schreiben. Der ARC-Compiler fügt automatisch die notwendigen Aufrufe von copy und autorelease ein:

return [[^(void){ ... } copy] autorelease];

Wenn Sie Blöcke in Objekten speichern möchten, ist es am einfachsten, dafür Propertys vom Typ copy zu verwenden. Sie können dann die Property-Werte wie bei anderen Propertys auch zuweisen. Durch den Speicherverwaltungstyp kopiert die Laufzeitumgebung automatisch der Block bei der Zuweisung:

// Typdeklaration
typedef double (^FunctionType)(double inValue);
// Property-Deklaration
@property(nonatomic, copy) FunctionType function;
// Zuweisung
self.function = ^(double inValue) { ... };
// Ausführung
self.function(2.5);

Listing 2.131 Block als Property-Wert


Rheinwerk Computing - Zum Seitenanfang

2.6.1RückruffunktionenZur vorigen Überschrift

Durch Blöcke lassen sich in vielen Fällen Fallunterscheidungen vermeiden, die den Programmcode häufig schwer verständlich machen. Ein typisches Beispiel dafür sind mehrere delegierende Objekte, die ein gemeinsames Delegate besitzen. Wenn ein Viewcontroller beispielsweise mehrere unterschiedliche Alertviews anzeigen kann, dann können die Alertviews den Controller als gemeinsames Delegate verwenden. Natürlich müssen die Delegate-Methoden die Alertviews wieder auseinanderhalten können, wozu Sie Tags verwenden können. Diese Lösung sieht dann ungefähr so aus:

- (IBAction)showFirstAlertView {
UIAlertView *theAlert = ...;
theAlert.tag = 10;
[theAlert show];
}
- (IBAction)showSecondAlertView {
UIAlertView *theAlert = ...;
theAlert.tag = 20;
[theAlert show];
}
- (void)alertView:(UIAlertView *)inAlertView
clickedButtonAtIndex:(NSUInteger)inButtonIndex {
if(inAlertView.tag == 10) {
// Anweisungen für ersten Alertview
}
else {
// Anweisungen für zweiten Alertview
}
}

Listing 2.132 Unterscheidung von Alertviews durch Tags

Die Anweisungen für die Unterscheidung der Alertviews sind in Listing 2.132 in Fettdruck hervorgehoben. Zu dieser Fallunterscheidung kommen die Fallunterscheidungen für die Buttons der Alertviews, und so entstehen schnell sehr lange und schwer verständliche Methoden. Durch Blöcke lässt sich der Code für die Ergebnisauswertung der Alertviews jeweils zu Ihrem Konstruktionsort verschieben und die Fallunterscheidung für die Trennung der Alertviews vermeiden.

Projektinformation

Den Quellcode des folgenden Beispiels finden Sie auf der DVD unter Code/Apps/iOS7/MultipleAlertViews oder im Github-Repository zum Buch im Unterverzeichnis https://github.com/Cocoaneheads/iPhone/tree/Auflage_3/Apps/iOS7/MultipleAlertView.

Dazu deklarieren Sie am besten zuerst einen eigenen Typ und eine Property für die Blöcke. Die Blöcke bekommen dabei die gleichen Parameter wie die Delegate-Methode des Alertviews. Die Property enthält jeweils den Block, die der Controller nach dem Drücken eines Buttons des aktuell angezeigten Alertviews aufrufen soll. Solche Blöcke nennt man auch Rückruffunktionen oder Callbacks.

typedef void (^AlertViewCallback)( 
UIAlertView *inAlertView, NSUInteger inButtonIndex);

@interface ViewController()<UIAlertViewDelegate>

@property (nonatomic, copy) AlertViewCallback alertViewCallback;

@end

Listing 2.133 Callback-Deklaration für die Alertviews

Das Beispielprojekt besitzt zwei Buttons, die beide einen Alertview öffnen. Im ersten Alertview können Sie ein Bild und im zweiten einen Titel für die Anzeige auswählen. Bei der Anzeige der Alerts müssen Sie nun der Callback-Property eine passende Rückruffunktion zuweisen.

- (IBAction)chooseImage:(id)inSender {
UIAlertView *theAlert .= [[UIAlertView alloc] ...];

self.alertViewCallback =
^(UIAlertView *inAlertView, NSUInteger inButton) {
if(inButton > 0) {
NSString *theName = inButton == 1 ?
@"dragonfly.jpg" : @"flower.jpg";

self.imageView.image =
[UIImage imageNamed:theName];
}
};
[theAlert show];
}

- (IBAction)chooseTitle:(id)inSender {
UIAlertView *theAlert = [[UIAlertView alloc] ...];

self.alertViewCallback =
^(UIAlertView *inAlertView, NSUInteger inButton) {
if(inButton > 0) {
self.titleLabel.text = inButton == 1 ?
NSLocalizedString(@"Dragonfly", @"") :
NSLocalizedString(@"Flower", @"");
}
};
[theAlert show];
}

Listing 2.134 Initialisierung des Callbacks bei Anzeige des Alerts

Die Delegate-Methode braucht jetzt nur noch die Rückruffunktion aufzurufen. Allerdings sollte sie die Referenz auf die Funktion zwischenspeichern, damit Sie die Property vor dem Aufruf auf nil setzen können. Dann kann eine Rückruffunktion den Wert der Property ändern, ohne dass es zu Komplikationen kommen kann. Da das Beispielprojekt automatisches Referenzzählen verwendet, reicht die Zuweisung des Callbacks an eine lokale Variable aus, da diese die Referenz hält.

- (void)alertView:(UIAlertView *)inAlertView 
clickedButtonAtIndex:(NSInteger)inButton {
AlertViewCallback theCallback = self.alertViewCallback;

self.alertViewCallback = nil;
theCallback(inAlertView, inButton);
}

Listing 2.135 Aufruf des Callbacks



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.

>> Zum Feedback-Formular
<< zurück




Copyright © Rheinwerk Verlag GmbH, Bonn 2014
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de


  Zum Katalog
Zum Katalog: Apps programmieren für iPhone und iPad






Neuauflage: Apps programmieren für iPhone und iPad
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Einstieg in Objective-C 2.0 und Cocoa






Einstieg in Objective-C 2.0 und Cocoa


Zum Katalog: Spieleprogrammierung mit Android Studio






Spieleprogrammierung mit Android Studio


Zum Katalog: Android 5






Android 5


Zum Katalog: iPhone und iPad-Apps entwickeln






iPhone und iPad-Apps entwickeln


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo