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

Inhaltsverzeichnis
Geleitwort
Vorwort
1 Hello iPhone
2 Grundlagen
3 Views und Viewcontroller
4 Alles unter Kontrolle
5 Daten, Tabellen und Controller
6 Models, Layer, Animationen
7 Programmieren, aber sicher
8 Datenserialisierung und Internetzugriff
9 Jahrmarkt der Nützlichkeiten
A Sicherer Entwicklungszyklus
Stichwort

Download:
- ZIP, ca. 49,9 MB
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
Galileo Computing
1000 S., geb., mit DVD
39,90 Euro, ISBN 978-3-8362-1915-0
Pfeil 2 Grundlagen
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.3 Speicherverwaltung, Propertys und Key-Value-Coding
Pfeil 2.3.1 Der Stack
Pfeil 2.3.2 Der Heap
Pfeil 2.3.3 Referenzen zählen
Pfeil 2.3.4 Die Speicherverwaltungsregeln
Pfeil 2.3.5 Autorelease-Pools
Pfeil 2.3.6 Propertys und Accessoren
Pfeil 2.3.7 Key-Value-Coding
Pfeil 2.3.8 Abschließende Bemerkungen
Pfeil 2.4 Automatisches Referenzenzählen
Pfeil 2.4.1 Zurück in die Zukunft
Pfeil 2.4.2 Weakie und die starken Zeiger
Pfeil 2.4.3 Autorelease-Pools
Pfeil 2.4.4 Einzelgänger
Pfeil 2.4.5 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

Galileo Computing - Zum Seitenanfang

2.6 BlöckeZur nächsten Überschrift

Icon

Erst 2010 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 Vater von Objective-C, Smalltalk, kannte bereits das Konzept von Blöcken als elementaren Bestandteil. Blöcke sind in Smalltalk selber als Objekte konzipiert, können also Nachrichten empfangen. Genauso verhält es sich in Objektive-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.

Ein Beispiel für Blöcke: Ein Fischhändler verkauft tagein, tagaus auf dem Markt Ma-krelen. 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 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 noch 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.123 Erzeugung einer Blockfunktion

Listing 2.123 erzeugt eine Blockfunktion, speichert sie 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 außerhalb des Blocks definiert wurden. Dabei speichert das Laufzeitsystem diese Werte allerdings als Kontext der Blockfunktion ab. Wenn Sie die Werte der Variablen ändern, verändern Sie diese Werte in der Blockfunktion nicht. Wenn Sie also die Zeilen aus Listing 2.124 an Listing 2.123 anhängen, erhalten Sie den gleichen Verkaufspreis wie oben und nicht 18,90 Euro. Die Blockfunktion merkt sich also die Variablenwerte bis zu ihrer Ausführung.

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

Listing 2.124 Eine Änderung der Variablen ändert nicht die Blockfunktion.

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 Blockfunktionen

Blockfunktionen 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 Blockfunktionen in Ihren Programmen verwenden möchten, sollten Sie wie in Listing 2.126 dafür über typedef eigene Typen definieren. Das vereinfacht Ihnen die Deklaration von Variablen.

Wie bereits erwähnt wurde, 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 wichtig, dass Sie die Blockfunktion nur innerhalb des Bereichs verwenden dürfen, in dem Sie sie deklariert haben. Folgendes dürfen Sie beispielsweise nicht machen:

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

Listing 2.125 Bereichsverletzungen mit Blöcken

Die letzte Anweisung in Listing 2.125 ist eine Bereichsverletzung, weil sie die Blockfunktion außerhalb des deklarierenden Bereichs verwendet (der durch /* Anfang */ und /* Ende */ gekennzeichnet ist). Sie dürfen eine Blockfunktion 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 Referenzenzä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 Referenzenzählen verwenden: [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.126 Das Kopieren von Blöcken

Automatisches Referenzenzählen und Blockfunktionen

Automatisches Referenzenzählen vereinfacht die Handhabung von Blockfunktionen 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 Blockfunktionen 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 die Blockfunktion 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.127 Blockfunktion als Property-Wert


Galileo Computing - Zum Seitenanfang

2.6.1 RückruffunktionenZur vorigen Überschrift

Durch Blockfunktionen 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 *)inAlertViewZeilenumbruch
clickedButtonAtIndex:(NSUInteger)inButtonIndex {
if(inAlertView.tag == 10) {
// Anweisungen für ersten Alertview
}
else {
// Anweisungen für zweiten Alertview
}
}

Listing 2.128 Unterscheidung von Alertviews durch Tags

Die Anweisungen für die Unterscheidung der Alertviews sind in Listing 2.128 in Fettdruck hervorgehoben. Zu dieser Fallunterscheidung kommen dann noch die Fallunterscheidungen für die Buttons der Alertviews, und so entstehen schnell sehr lange und schwer verständliche Methoden. Durch Blockfunktionen 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 nachfolgenden Beispiels finden Sie auf der DVD unter Code/Apps/Storyboard/MultipleAlertViews oder im Github-Repository zum Buch im Unterverzeichnis https://github.com/Cocoaneheads/iPhone/tree/Auflage_2/Apps/Storyboard/MultipleAlertViews.

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

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

@interface ViewController()<UIAlertViewDelegate>

@property (nonatomic, copy) AlertViewCallbackZeilenumbruch
alertViewCallback;

@end

Listing 2.129 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 = Zeilenumbruch
^(UIAlertView *inAlertView, NSUInteger inButton) {
if(inButton > 0) {
NSString *theName = inButton == 1 ? Zeilenumbruch
@"dragonfly.jpg" : @"flower.jpg";

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

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

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

Listing 2.130 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 Referenzenzählen verwendet, reicht die Zuweisung des Callbacks an eine lokale Variable aus, da diese die Referenz hält.

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

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

Listing 2.131 Aufruf des Callbacks



Ihr Kommentar

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







<< zurück
  Zum Katalog
Zum Katalog: Apps programmieren für iPhone und iPad

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: Apps entwickeln für iPhone und iPad - Videotraining






 Apps entwickeln für
 iPhone und iPad -
 Videotraining


Zum Katalog: Apps mit HTML5 und CSS3






 Apps mit HTML5
 und CSS3


Zum Katalog: iPhone- und iPad-Apps entwickeln






 iPhone- und
 iPad-Apps entwickeln


Zum Katalog: Android 4






 Android 4


Zum Katalog: Android-Apps entwickeln - Videotraining






 Android-Apps
 entwickeln -
 Videotraining


Zum Katalog: Windows Store Apps mit XAML und C#






 Windows Store Apps
 mit XAML und C#


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 2013
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.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de