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

Jetzt 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.4In den Tiefen der SpeicherverwaltungZur nächsten Überschrift

Wer, wie, was? Wieso, weshalb, warum?

Im letzten Abschnitt haben Sie erfahren, welche Möglichkeiten Sie haben, die Speicherverwaltung zu beeinflussen; also beispielsweise, wie Sie Speicherlecks durch Referenzzyklen vermeiden können, indem Sie schwache Referenzen verwenden. Das reicht an notwendigen Kenntnissen über die Speicherverwaltung für Ihre ersten Apps vollkommen aus, und Sie können beim ersten Lesen des Buches diesen Abschnitt gerne überspringen. Allerdings kommen Sie früher oder später an einen Punkt, wo Sie mehr über die Speicherverwaltung erfahren wollen oder müssen, und dann können Sie in diesem Abschnitt mehr darüber erfahren.

In diesem Abschnitt geht es also darum, wie Apple starke und schwache Referenzen für die Speicherverwaltung umgesetzt hat. Die Speicherverwaltung nennt Apple Automatic Reference Counting (ARC), was sich mit automatisches Referenzzählen übersetzen lässt.

Um jedoch das automatische Referenzzählen richtig verstehen zu können, sollten Sie sich vorher mit dem manuellen Referenzzählen auseinandersetzen. Zum einen verstehen Sie so besser, warum ARC auch seine Grenzen hat und Sie manchmal helfend eingreifen müssen, und zum anderen finden Sie noch viel Sourcecode im Netz, der auf manuellem Referenzzählen basiert. Allerdings hat auch das manuelle Referenzzählen von Objective-C nichts mit der fehlerträchtigen Speicherverwaltung zu tun, mit der man als C- oder C++-Programmierer kämpfen muss.


Rheinwerk Computing - Zum Seitenanfang

2.4.1Manuelles ReferenzzählenZur nächsten ÜberschriftZur vorigen Überschrift

Die Speicherverwaltung in Cocoa Touch basiert auf dem Zählen von Referenzen, das das nachfolgende Beispiel veranschaulicht.

Ein Hausarzt schickt seinen Patienten ins Krankenhaus. Dort untersuchen und behandeln ihn verschiedene Fachärzte, indem der Patient bei jedem Arzt die Sprechstunde besucht. Die Ärzte schicken den Patienten zu weiteren Ärzten im Krankenhaus, bestellen ihn zu erneuten Untersuchungen oder Behandlungen oder teilen ihm ihren Befund mit. Da der Patient glücklicherweise nicht schwer erkrankt ist, fallen die Befunde immer negativ aus beziehungsweise sind die Behandlungen immer erfolgreich. Die Frage ist nun: Wann weiß der Patient, dass er geheilt und entlassen ist?

Ein Arzt kann nicht allein über die Entlassung des Patienten entscheiden. Wenn jedoch andererseits kein Arzt den Patienten entlässt, belegt der Patient weiter ein Bett im Krankenhaus, und irgendwann kann es keine weiteren Patienten mehr aufnehmen, da lauter gesunde Menschen die Betten belegen, die sich ihrerseits langweilen und das Pflegepersonal nerven. Wie kann die Klinikleitung dieses Problem lösen? Die Ärzte können sich natürlich regelmäßig abstimmen und zu gegebener Zeit dem Patienten seine Entlassung mitteilen. Das wäre indes sehr aufwendig, und die Ärzte verbrächten viel Zeit mit der Abstimmung, in der sie besser andere Patienten behandeln könnten. Eine andere Möglichkeit besteht darin, dem Patienten einen Laufzettel zu geben. Darauf bekommt jeder Arzt, den der Patient noch aufsuchen muss, einen Strich. Wenn ein Arzt mit der Behandlung des Patienten fertig ist, entfernt er diesen Strich wieder von dem Zettel. Der Patient darf nach Hause gehen, wenn er keinen Strich mehr auf dem Zettel hat.

In diesem Beispiel ist der Patient ein Bild für ein dynamisch erzeugtes Objekt, die Ärzte sind Referenzen auf dieses Objekt, und die Entlassung aus dem Krankenhaus entspricht der Freigabe des Objekts aus dem Speicher. Und so funktioniert es auch in Cocoa Touch: So, wie die einzelnen Ärzte den Patienten nicht entlassen können, können die einzelnen Referenzen nicht entscheiden, wann das Laufzeitsystem das von ihnen referenzierte Objekt freigeben soll.

Wenn das Programm das Objekt zu früh freigibt, entsteht ein Dangling Pointer. Im Krankenhausbeispiel würde ein Arzt vergebens auf den Patienten warten, der schon zu Hause gemütlich im Sessel sitzt. Analog entsteht im Hauptspeicher ein Leck, wenn keine Referenz das referenzierte Objekt endgültig freigibt. Das entspricht dem gesunden Patienten im Krankenhaus, den kein Arzt mehr sehen will. Da er nie die Information bekommt, dass er gehen darf, liegt er bis zum Sankt-Nimmerleins-Tag im Krankenhaus.

Die zentrale Frage der Speicherverwaltung dreht sich also darum, wann sie ein Objekt freigeben kann. Referenzen in Objective-C beziehungsweise C sind einfach nur Speicheradressen, die von beliebigen Programmbereichen auf das Objekt zeigen können. Eine Lösung, die auf die selbständige Abstimmung dieser Zeiger setzt, kommt deswegen nicht in Frage. Die Ärzte im Krankenhaus haben ja auch Besseres zu tun, als sich ständig darüber auszutauschen, welcher Patient nun nach Hause darf und welcher noch bleiben muss. Diesen Mehraufwand würde keine gesetzliche Krankenkasse bezahlen, und da nun mal nicht jeder Mitglied einer privaten Krankenversicherung ist, muss also eine andere Lösung her.

Objective-C verwendet auch eine Strichliste in Form eines Zählers, so dass sich jedes Objekt selbst merken kann, wie viele Referenzen auf es zeigen. Im Gegensatz zum Laufzettel des Patienten zählt das Objekt allerdings nur die Anzahl der Referenzen, genauer der starken Referenzen. Es merkt sich nicht, welche Referenzen auf es zeigen. Der Zähler reicht jedoch vollkommen aus, um festzustellen, ab wann es keine starke Referenz mehr auf das Objekt gibt, und dann kann es die Laufzeitumgebung aus dem Hauptspeicher entfernen.

Die folgenden Abbildungen zeigen dieses Prinzip. Jede der drei Personen (Objekte) besitzt einen Referenzzähler (Reference Counter). Jeder neue Verweis auf das Objekt kann diesen Referenzzähler erhöhen, damit das Objekt weiß, dass eine weitere Referenz existiert. Beim Wegfall dieses Verweises muss dann der Referenzzähler dekrementiert werden. Das Objekt kann nun feststellen, wann das Programm den letzten Verweis auf es löst, und sich aus dem Speicher entfernen. In Abbildung 2.38 zeigen Fred und Jeff auf Ophelia, die dementsprechend einen Referenzzähler von 2 hat.

Mit der Zeit merkt Jeff, dass er bei Ophelia keine Chancen mehr hat und Celia [Anm.: Nicht im Bild(e)] sowieso viel netter ist. Er lässt Ophelia fallen, und die starke Referenz von Jeff auf Ophelia fällt weg. Ophelia besitzt daher nur noch einen Referenzzähler von 1.

Abbildung

Abbildung 2.38 Ophelia erfreut sich großer Beliebtheit.

Abbildung

Abbildung 2.39 Ophelia, pass auf, du hast nur noch den Fred!

Jetzt ist Ophelia nicht mehr so beliebt. Fred hat ihre Spielchen auch langsam satt, und sein Interesse an ihr lässt stark nach. Deswegen entfernt auch Fred seine Referenz auf sie, womit sowohl die Beliebtheit als auch der Referenzzähler von Ophelia auf 0 sinken. Sie stellt nun fest, dass niemand sie mehr braucht, entfernt sich somit aus dem Speicher und verwandelt sich in das in Abbildung 2.40 gezeigte Wölkchen im Datennirwana; arme Ophelia.

Das Verwalten der Referenzzähler macht genau den Unterschied zwischen manueller und automatischer Speicherverwaltung. Bei der manuellen Variante gibt es die drei Methodenaufrufe retain, release und autorelease. Die Cocoa-Wurzelklassen, von denen Sie in der Regel nur NSObject für Ihre Klassen verwenden, stellen diese Methoden bereit.

Abbildung

Abbildung 2.40 Ophelia, du bist raus.

Der Referenzzähler

Das Beispiel gibt Werte für die Referenzzähler nur zur Veranschaulichung an. In der Praxis sehen Sie selten so einen einfachen Werteverlauf. Der Sinn des Referenzzählens ist es ja gerade, dass beliebige Referenzen auf Ihre Objekte bestehen können. Außerdem gibt es Objekte – wie beispielsweise Zeichenkettenkonstanten oder Singletons –, die das Referenzzählen nur vortäuschen.

Langer Rede kurzer Sinn: Gehen Sie niemals von bestimmten Werten für den Referenzzähler aus. Das taugt nicht zur Analyse der Speicherverwaltung oder gar zur Suche nach Speicherverwaltungsfehlern.

Um den Referenzzähler eines Objekts um eins zu erhöhen, senden Sie ein retain an das Objekt, und mit release können Sie den Referenzzähler um eins verringern. Als Aufrufer dieser Methoden sollten Sie sich jedoch nicht darum kümmern, wie viele Verweise auf ein Objekt zeigen.

Dieses Verhalten entspricht übrigens dem Umgang mit Dateien im Dateisystem Ihres Computers. Auch dort ist für das Vorhandensein einer Datei maßgeblich, wie viele Verzeichniseinträge auf diese Datei zeigen. Wenn Sie eine Datei anlegen, gibt es zunächst genau einen Eintrag für die Datei, nämlich von dem Verzeichnis, in dem die Datei liegt. Das Verzeichnis weiß, dass diese Datei in ihm existiert, und erlaubt den Zugriff auf diese Datei. Die Anzahl der Einträge kann sich erhöhen, wenn Sie oder das Betriebssystem Hardlinks auf die Datei setzen. Die Anzahl der Einträge verringert sich, wenn Sie Hardlinks wieder entfernen. [Anm.: Zwischen der ursprünglichen Datei und einem Hardlink besteht kein Unterschied. Sie können auch zuerst die Datei löschen; ihr Inhalt bleibt so lange erhalten, wie es noch Hardlinks darauf gibt. Machen Sie das aber bloß nicht mit Softlinks!] Ist die Anzahl der Verzeichniseinträge bei 0 angekommen, löscht das Betriebssystem auch den Dateiinhalt. Konsequenterweise heißt der Systemaufruf zum Löschen einer Datei unlink und nicht delete. Das Konzept des Referenzzählens ist also nicht nur auf die iOS-Programmierung beschränkt, sondern ein gängiges Mittel, die Existenzberechtigung von Objekten in komplexen Systemen zu verwalten.

Die Methode autorelease markiert ein Objekt für einen späteren, automatischen Aufruf der Methode release. Sie dürfen das Objekt also nach dem Aufruf von autorelease noch verwenden. Sie legt das Objekt in den Autoreleasepool, den Sie ja schon kennengelernt haben. Jeder Aufruf von autorelease legt das Objekt ein weiteres Mal in den Autoreleasepool, so dass das Objekt für jedes autorelease jeweils ein release erhält, wenn die App den Autoreleasepool zerstört.


Rheinwerk Computing - Zum Seitenanfang

2.4.2Die Speicherverwaltungsregeln für das manuelle ReferenzzählenZur nächsten ÜberschriftZur vorigen Überschrift

Durch die vorgenannten Methodenaufrufe legen Sie bei manuellem Referenzzählen also fest, ob ein Verweis das referenzierte Objekt im Speicher hält oder nicht. Wenn eine Referenz das Objekt nicht halten soll, also eine schwache Referenz, brauchen Sie dafür auch keine der genannten Methoden aufzurufen. Aus Sicht der Speicherverwaltung sind also nur die starken beziehungsweise haltenden Referenzen interessant. Wann jedoch hält eine Referenz ein Objekt?

Apple hat dafür vier einfache Regeln aufgestellt:

  1. Eine Referenz hält ein Objekt, wenn der Name seiner Erzeugungsmethode mit alloc, new, copy oder mutableCopy beginnt. Erzeugen Sie hingegen ein Objekt über eine Methode, deren Name nicht auf diese Regel passt, dann ist die Referenz kein Halter des Objektes. Sie sollten das auch unbedingt bei der Benennung Ihrer eigenen Methoden beachten.
  2. Wenn Sie die Methode retain eines Objekts aufrufen, ist der Verweis ein Halter des Objekts.
  3. Für jeden haltenden Verweis auf ein Objekt müssen Sie entweder die Methode release oder autorelease genau einmal aufrufen.
  4. In der Methode dealloc sollten Sie immer alle Verweise auf andere Objekte freigegeben, die die Attribute des Objekts halten. Die Laufzeitumgebung ruft diese Methode unmittelbar vor der Freigabe des Speichers auf. Sie sollten in dieser Methode als Letztes immer die dealloc-Methode der Superklasse aufrufen. Sie dürfen allerdings sonst dealloc niemals direkt aufrufen.

Die erste Regel gilt übrigens nicht nur für die Methoden aus der Klasse des Objekts, sondern für alle Methoden. Dabei ist die Methode alloc die einzig wirkliche Erzeugungsmethode für Objekte. Die anderen Methoden geben nur über alloc erzeugte Objekte zurück. Diese Regel gilt übrigens auch, wenn die Methode das Objekt nicht neu erzeugt, wie das beispielsweise bei einem Objektcache der Fall sein kann.

Tipp

Die Speicherverwaltungsregeln sind fundamental. Nehmen Sie sich die Zeit, sich damit ausreichend vertraut zu machen. Es zahlt sich aus. Bedenken Sie auch, dass die erste Regel eine Namenskonvention ist. Sie erlaubt Ihnen zum einen, erzeugte Objekte richtig zu verwalten. Andererseits gibt sie Ihnen auch ein Schema vor, wie Sie Ihre Methoden zu benennen haben.

Die folgenden Beispiele sollen Ihnen die Regeln veranschaulichen:

NSNumber *theAge = [[NSNumber alloc] initWithInt:15];
NSLog(@"age=%@", theAge);
[theAge release];

Listing 2.91 Objekterzeugung über eine »alloc-init«-Kette

Hier passt der Aufruf in der ersten Zeile auf die erste Regel. Der Verweis theAge hält also die Zahl. Sie müssen das Objekt deshalb über die Methode release freigeben. Ein Beispiel, bei dem die erste Regel nicht zutrifft, ist das folgende:

NSNumber *theAge = [NSNumber numberWithInt:15];
NSLog(@"age=%@", theAge);

Listing 2.92 Objekterzeugung über einen Convenience-Konstruktor

Der Methodenname in der ersten Zeile passt nicht zur ersten Regel. Die Variable theAge hält also die Zahl nicht, und Sie dürfen dem Objekt kein release schicken. Klassenmethoden wie die in der ersten Zeile nennt man übrigens auch Convenience-Konstruktoren.

Für eine Methode, die ein neues Objekt erzeugt, gibt es also zwei Möglichkeiten. Wenn ihr Name zur ersten Regel passt, muss sie das Objekt halten, das sie zurückgibt. Bei der Rückgabe übergibt sie die Halterschaft an den Aufrufer.

- (NSNumber *)newAge {
return [[NSNumber alloc] initWithInt:15];
}
- (void)printAge {
NSNumber *theAge = [self newAge];
NSLog(@"age=%@", theAge);
[theAge release];
}

Listing 2.93 Objekthaltende Erzeugung über eine Methode

In diesem Beispiel gibt die Methode newAge die Halterschaft an die Variable theAge in der aufrufenden Methode printAge ab, und Sie müssen das Objekt über diese Variable freigeben. Alternativ können Sie auch einen Namen wählen, der nicht auf die erste Regel passt. Dann sollte die Methode so aussehen:

- (NSNumber *)age {
return [NSNumber numberWithAge:15];
}
- (void)printAge {
NSNumber *theAge = [self age];
NSLog(@"age=%@", theAge);
}

Listing 2.94 Objekterzeugung über eine Methode mit einen Convenience-Konstruktor

Die aufrufende Methode printAge darf das Objekt in diesem Fall also nicht durch einen Aufruf von release freigeben.

Dieses Beispiel verwendet einen Convenience-Konstruktor für die Objekterzeugung. Viele Cocoa-Klassen stellen jedoch keinen solchen Konstruktor zur Verfügung. In diesem Fall muss die Methode das Objekt auch durch einen alloc-init-Aufruf erzeugen. Die aufrufende Methode darf allerdings aufgrund der Namenskonvention kein Halter des Objekts sein, und die Methode muss das Objekt freigeben. Sie dürfen es jedoch nach einem Aufruf von release nicht mehr verwenden, da die Laufzeitumgebung es dadurch ja bereits aus dem Speicher gelöscht haben könnte.

In dieser Situation hilft der Autoreleasepool, und Sie können autorelease verwenden, so dass der release-Aufruf erst zu einem späteren Zeitpunkt erfolgt.

- (NSNumber *)age {
NSNumber *theAge = [[NSNumber alloc] initWithInt:15];
return [theAge autorelease];
}

Listing 2.95 Objekterzeugung über eine Methode und den Autoreleasepool

Die Beispiele oben erläutern die Speicherverwaltungsregeln auf Methodenebene. Die folgenden Beispiele veranschaulichen die Regeln auf Klassenebene, wenn Sie Attribute verwenden. Hier ist die Deklaration für die Beispielklasse:

@interface Person : NSObject {
@private
NSNumber *age;
}
– (NSNumber *)age;
– (void)setAge:(NSNumber *)inAge;
@end

Listing 2.96 Klasse mit Methoden für den Attributzugriff

Der Getter age hat eine einfache Implementierung, da Sie hier keine Speicherverwaltungsoperationen durchführen müssen:

- (NSNumber *)age {
return age;
}

Listing 2.97 Methode zum Lesen eines Attributwertes

Ein Objekt der Klasse Person hält ja bereits die Zahl für das Alter, und der Methodenname passt nicht zur ersten Regel. Also brauchen Sie sich hier nicht weiter um die Speicherverwaltung zu kümmern.

Die vierte Regel fordert, dass die Methode dealloc alle Verweise auf die gehaltenen Objekte freigibt. Außerdem soll sie die Methode der Superklasse aufrufen. Für dieses Beispiel sieht diese Methode also so aus:

- (void)dealloc {
[age release];
[super dealloc];
}

Listing 2.98 Freigabe eines Attributwertes in der Methode »dealloc«

Sie müssen jetzt nur noch die Methode zum Setzen des Alters implementieren. Hier können Sie hingegen nicht nur einfach dem Attribut age den neuen Wert zuweisen. Es könnte ja bereits einen Wert besitzen, und diesen Wert müssen Sie natürlich zuerst freigeben.

- (void)setAge:(NSNumber *)inAge {
[age autorelease]; // Achtung! release kann hier
// einen Dangling Pointer erzeugen
age = [inAge retain];
}

Listing 2.99 Setzen eines Attributwerts

In der ersten Anweisung verwendet die Methode autorelease anstatt release, da ein release an dieser Stelle zu einem Laufzeitfehler führen könnte, wenn age und inAge bereits vor dem Aufruf auf das gleiche Objekt verweisen (siehe Abbildung 2.41).

Abbildung

Abbildung 2.41 Freigabe des Verweises »age« über »release«

Ein release gäbe dieses Objekt hingegen unmittelbar frei, wenn das Attribut sein letzter Halter wäre, und es entstünde ein Dangling Pointer. Schon der nachfolgende retain-Aufruf würde hier wahrscheinlich direkt zu einem Laufzeitfehler führen. Durch die Verwendung von autorelease bleibt das Objekt zunächst erhalten, und der release-Aufruf erfolgt erst später. Das vermeidet einen Dangling Pointer in der dritten Zeile. Sie können die Methode auch ohne autorelease formulieren, indem Sie zuerst das retain und danach das release senden:

- (void)setAge:(NSNumber *)inAge {
[inAge retain];
[age release];
age = inAge;
}

Listing 2.100 Setter mit Vertauschung von »retain« und »release«

Eine dritte Möglichkeit ist der Vergleich des Attributs mit dem Parameter (siehe Listing 2.101). Diese Variante hat den Vorteil, dass die Methode nur die wirklich notwendigen Speicherverwaltungsoperationen ausführt.

- (void)setAge:(NSNumber *)inAge {
if(age != inAge) {
[age release];
age = [inAge retain];
}
}

Listing 2.101 Setter mit »if«-Abfrage

Was passiert hingegen in der Methode setAge:, wenn der neue Wert nil ist oder wenn das Attribut age auf nil verweist? In Objective-C können Sie ja an nil jede beliebige Nachricht senden, und das ist im Gegensatz zu vielen anderen Programmiersprachen kein Fehler. Ein solcher Methodenaufruf führt keine Anweisungen aus, und das Ergebnis ist immer nil oder 0, wenn die Methode einen primitiven Datentyp zurückgibt.

Vorsicht

Sie sollten bei der Verwendung eines Setters niemals davon ausgehen, dass er eine bestimmte Variante verwendet. Das ist ein Implementierungsdetail, und die gehen Sie bekanntlich nichts an. Bei der ersten Variante überlebt der alte Wert immer den Setter-Aufruf, weil Sie ihn ja in den Autoreleasepool legen. Bei den beiden anderen Varianten ist das allerdings nicht unbedingt der Fall.

Sie können also auch die Methode setAge: in dealloc für die Freigabe des Age-Objekts verwenden.

- (void)dealloc {
[self setAge:nil];
[super dealloc];
}

Listing 2.102 Freigabe eines Attributwertes durch Verwendung des Setters

Die Verwendung der Methode statt der direkten Freigabe durch release hat den Nebeneffekt, dass sie das Attribut auf einen definierten Wert setzt. Es verweist danach nicht mehr auf den ungültigen Speicherbereich des freigegebenen Objekts. In der Methode dealloc ist die Auswirkung nicht so groß. Falls Sie hingegen ein Attribut an einer anderen Stelle freigeben wollen, macht das natürlich schon einen Unterschied.

Accessoren und manuelle Speicherverwaltung

Durch die Verwendung des Setters können Sie also eine Referenz auf ein Objekt in einem Schritt freigeben und ihr einen neuen, definierten Wert zuweisen. Das erleichtert die manuelle Speicherverwaltung erheblich und erhöht auch die Lesbarkeit des Codes. Sie sollten also möglichst Setter-Aufrufe gegenüber direkten Attributzugriffen bevorzugen.

Diese Methoden können Sie auch über die Punktnotation aufrufen. Sie lehnt sich an die Syntax anderer objektorientierter Sprachen an. Mit Punktnotation sieht die dealloc-Methode dann so aus:

- (void)dealloc {
self.age = nil;
[super dealloc];
}

Listing 2.103 Freigabe eines Attributwertes mit Punktnotation

In Abschnitt 2.2.2, »Modellbau«, haben wir Ihnen von der Verwendung von Attributdeklarationen in der Klassendeklaration abgeraten, weil sie eine Verletzung des Geheimnisprinzips darstellen. Bei der manuellen Speicherverwaltung kommt noch der Umstand hinzu, dass Sie sich darum im Setter kümmern müssen. Die Verwendung von Propertys ist hier also wesentlich einfacher.

Wenn das Programm ein Objekt für den weiteren Verlauf nicht mehr benötigt, muss man bei manueller Speicherverwaltung den vom Objekt verwendeten Speicher freigeben, um nicht unnötig Ressourcen zu belegen. Das Freigeben des Speichers erfolgt dabei über die Methode release. Die Nachricht release kann dabei den Aufruf der Methode dealloc bewirken, über die ein Objekt seine verwendeten Ressourcen freigibt. Das sind in der Regel andere, vom Objekt referenzierte Objekte. Sie sollten diese Methode jedoch niemals direkt aufrufen, da das ausschließlich die Methode release übernimmt.

Eine typische Implementierung von dealloc sieht so aus wie in Listing 2.103. Sie geben zuerst die Ressourcen frei und rufen danach die Implementierung der Superklasse auf. Das ist allerdings nur bei manuellem Referenzzählen notwendig und erlaubt. Bei der automatischen Variante ist der Aufruf von dealloc in der Superklasse ein Fehler, den der Compiler mit einer entsprechenden Meldung quittiert.

Garbage Collection

Auf dem Mac gibt es seit OS X 10.5 (Leopard) eine automatische Garbage Collection. Dabei muss sich der Programmierer keine Sorgen mehr um das Freigeben von Speicher machen, da der Garbage Collector automatisch alle Objekte freigibt, auf die keine Referenzen mehr zeigen. Er sammelt also den Objektmüll aus dem Speicher und wirft ihn weg.

Es gibt dazu keine Entsprechung unter iOS. Das liegt daran, dass das Müllsammeln extrem rechenaufwendig ist. Ab iOS 5 geht Apple deshalb mit dem automatischen Referenzzählen einen anderen Weg, der eine Synthese aus der Bequemlichkeit der Garbage Collection und der Geschwindigkeit des Referenzzählens ist. Dabei ist automatisches Referenzzählen eine reine Erweiterung des manuellen Referenzzählens, die nichts mit Garbage Collection zu tun hat, und seit der Einführung von OS X 10.8 entwickelt Apple auch die Garbage Collection unter OS X nicht mehr weiter.


Rheinwerk Computing - Zum Seitenanfang

2.4.3AutoreleasepoolsZur nächsten ÜberschriftZur vorigen Überschrift

Autoreleasepools (ARP) vereinfachen die Speicherverwaltung bei manuellem Referenzzählen sehr stark, da Sie damit in vielen Fällen die Objekte dann freigeben können, wenn es für Sie günstig ist. Sie brauchen also diesen Objekten kein release mehr zu schicken, wenn Sie es wirklich nicht mehr benötigen.

Übernimm du mal

Durch das Einfügen eines Objekts in den Autoreleasepool geben Sie die Halterschaft des Objekts von Ihrem Code an den Pool ab. Auf das Krankenhausbeispiel bezogen bedeutet das, dass ein Arzt die Behandlung des Patienten an einen Fachkollegen abgibt, damit er Golf spielen kann. Der Fachkollege untersucht den Patienten und sorgt für die Entfernung des Striches auf dem Laufzettel.

Bislang brauchten Sie sich um die Erzeugung und Zerstörung von Autoreleasepools nicht zu kümmern. Es gibt jedoch einige Situationen, in denen das notwendig ist.

Die Direktive @autoreleasepool haben Sie ja bereits kennengelernt. Sie klammern in dem Block dieser Direktive einfach die Anweisungen, die diesen Pool verwenden sollen.

@autoreleasepool {
...
}

Listing 2.104 Anlegen eines Autoreleasepools über die Direktive

Allerdings gibt es diese Direktive erst seit Xcode 4.2. Ältere Versionen erzeugen Autoreleasepools nur über die Klasse NSAutoreleasePool, indem Sie ein Objekt dieser Klasse anlegen und im selben Block auch wieder freigeben. Alle Aufrufe von autorelease, die zwischen diesen beiden Anweisungen stattfinden, nutzen dann diesen Pool. Der Inhalt von Listing 2.105 entspricht dem von Listing 2.104.

NSAutoreleasePool *thePool = [[NSAutoreleasePool alloc] init];
...
[thePool drain];

Listing 2.105 Anlegen eines Autoreleasepools über die Foundation-Klasse

Die Nachricht drain an den Autoreleasepool hat unter iOS die gleiche Wirkung wie release. Der Unterschied dieser beiden Methoden macht sich nur unter OS X bei einem Programm mit Garbage Collection bemerkbar. Sie dürfen also unter iOS auch ruhig release verwenden. Die Verwendung von drain hat sich allerdings inzwischen eingebürgert. Sie sollten allerdings die Klasse NSAutoreleasePool nicht mehr verwenden, und mit automatischem Referenzzählen ist ihre Nutzung sogar auch nicht mehr erlaubt.

Es gibt drei wichtige Situationen, in denen Sie einen Pool selbst verwalten müssen:

  1. in der main-Funktion
  2. bei der Verwendung von Threads
  3. und um Speicherüberläufe zu vermeiden

Die »main«-Funktion

Die main-Funkion legt Xcode bei der Erzeugung eines neuen Projekts automatisch für Sie an, und die Funktion sieht dann ungefähr so aus:

int main(int argc, char *argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil,
NSStringFromClass([AppDelegate class]);
}
}

Listing 2.106 Eine »main«-Funktion mit »@autoreleasepool«

Niemals ohne Autoreleasepool

In Objective-C sollte die main-Funktion immer einen Autoreleasepool verwalten, auch wenn Sie ihn anscheinend nicht verwenden. Cocoa verlässt sich auf die Existenz des Pools, und daher kann die Verwendung einer Cocoa-Klasse außerhalb eines Pools zu folgender Fehlermeldung führen:

Object 0xXXX of class YYY autoreleased with no pool in place – just leaking – break on objc_autoreleaseNoPool() to debug

Threads

Ein Thread ist ein eigenständiger Ausführungsstrang in einem Programm. Über Threads können Sie erreichen, dass Ihr Programm mehrere Aufgaben gleichzeitig erledigt. Beispielsweise kann Safari unter OS X eine Datei herunterladen, während Sie mit dem Browser durch das Internet surfen. In der Regel brauchen Sie Threads allerdings nur in seltenen Fällen. In Cocoa Touch gibt es häufig elegantere und effizientere Möglichkeiten, nebenläufige Programme zu schreiben.

Wie dem auch sei, jeder Thread braucht einen eigenen Autoreleasepool. Am einfachsten können Sie einen Thread über die Methode performSelectorInBackground:withObject: starten, wie Listing 2.107 zeigt:

- (void)startParsingWithFile:(NSString *)inPath {
[self perfomSelectorInBackground:@selector(parseWithFile:)
withObject:inPath];
}
- (void)parseWithFile:(NSString *)inPath {
@autoreleasepool {
...
}
}

Listing 2.107 Starten eines Threads

Die Methode startParsingWithFile: startet dabei die Methode parseWithFile: in einem eigenen Thread, wobei Letztere einen eigenen Autoreleasepool besitzt.

Vermeidung von Speicherüberläufen

Die exzessive Nutzung des Autoreleasepools, also wenn zu viele Objekte im Pool liegen, kann zu einem Speicherüberlauf führen. Das kann selbst dann passieren, wenn Sie sich strikt an die Speicherverwaltungsregeln gehalten haben, und liegt daran, dass der Autoreleasepool alle Objekte über seine gesamte Lebenszeit hält. Häufig ist das indes nicht notwendig, und der Pool könnte zumindest einen Teil der Objekte auch früher freigeben.

Sie können zwar dem Autoreleasepool nicht explizit mitteilen, welche Objekte Sie freigeben wollen; es lassen sich jedoch Autoreleasepools ineinander verschachteln. Dann sammelt immer der innerste Pool die Objekte auf, denen Sie ein autorelease senden. Wenn Sie diesen inneren Pool freigeben, gibt er auch alle enthaltenen Objekte frei. Dadurch können Sie verhindern, dass der Berg der nicht mehr verwendeten Objekte zu stark anwächst. Da dieses Problem häufig bei großen Schleifen auftritt, reicht es in der Regel aus, einen Autoreleasepool innerhalb der Schleife zu platzieren. Ein Beispiel dafür haben Sie bereits in Listing 2.85 gesehen.


Rheinwerk Computing - Zum Seitenanfang

2.4.4Der ReferenzzählerZur nächsten ÜberschriftZur vorigen Überschrift

Es gibt noch eine weitere Methode aus dem Bereich des Referenzzählens. Sie heißt retainCount und steht Ihnen nur beim manuellen Referenzzählen zur Verfügung. Damit können Sie den Wert des Referenzzählers eines Objekts auslesen. Diese Methode erwähnen wir hier allerdings nur der Vollständigkeit halber. Sie sollten diese Methode keinesfalls zur Überprüfung oder gar zur Steuerung Ihrer Speicherverwaltung verwenden. Es gibt mehrere Gründe, die dagegen sprechen:

  • Das Referenzzählen dient dazu, beliebig viele Verweise auf ein Objekt zu verwalten. Es gibt also häufig Verweise, die Sie nicht kennen. Diese Verweise verändern natürlich den Zähler.
  • Es gibt Objekte, wie beispielsweise Zeichenkettenkonstanten oder Singletons, die das Referenzzählen nur vortäuschen. Der Referenzzähler hat in diesem Fall immer den gleichen, sehr hohen Wert.
  • Der lesende Zugriff auf eine atomare Property erhöht den Zählerwert, obwohl die Anzahl der retain- und release-Aufrufe auf dem Objekt ausgeglichen bleibt.
  • Es gibt wesentlich effizientere Möglichkeiten, die Speicherverwaltung Ihrer App zu überprüfen. Halten Sie sich lieber an die oben aufgeführten Speicherverwaltungsregeln. Zur Belohnung werden Ihnen der Objective-C-Compiler, der Analyzer, der Debugger und das Programm Instruments viel effektiver helfen, Fehler in der Speicherverwaltung zu entdecken. [Anm.: Diese Tools lernen Sie im Laufe des Buches natürlich noch genauer kennen.]

Rheinwerk Computing - Zum Seitenanfang

2.4.5Automatisches ReferenzzählenZur nächsten ÜberschriftZur vorigen Überschrift

Die LLVM-Compiler ab Version 3.0 stellen mit dem Automatic Reference Counting (ARC) eine Erweiterung des bestehenden Speicherverwaltungssystems zur Verfügung. Auch wenn es erst mit Xcode 4.2 und iOS 5 zum Standardumfang des SDKs gehört, soll es laut Aussage von Apple bereits unter iOS 4.0 bei Benutzung geeigneter Compiler funktionieren. Das automatische Referenzzählen ist, bis auf eine Ausnahme, keine Erweiterung des Laufzeitsystems, sondern eine reine Compiler-Erweiterung.

Die Idee, die dahinter steht, ist einfach und brillant. Die Speicherverwaltungsregeln sind einfach und maschinell überprüfbar. Mit dem Analyzer steht Ihnen sogar ein Tool zur Verfügung, mit dem Sie eine Menge Speicherverwaltungsfehler automatisiert entdecken können. Die Idee von ARC ist, die Verfahren des Analyzers zu verwenden, um die Speicherverwaltungsanweisungen mit der Übersetzung automatisch in den Code einzufügen. Laut Apple sorgt der Compiler mit ARC indes nicht nur für eine korrekte Speicherverwaltung, sondern auch für schnelleren Code.

Der Compiler analysiert beim automatischen Referenzzählen den Programmcode und ergänzt ihn um die notwendigen Anweisungen, damit bei der Ausführung des Programms weder Speicherlecks noch Dangling Pointer entstehen. Da ja mehrere Köche bekanntlich einen schlechten Einfluss auf den Brei ausüben, bedeutet das im Umkehrschluss für Sie, dass auch Sie Ihre Finger von der Speicherverwaltung zu lassen haben. Sie dürfen die Methoden retain, release und autorelease weder aufrufen noch in Ihren Klassen überschreiben. Außerdem ist das Anlegen und Verwenden von Objekten der Klasse NSAutoreleasePool strengstens untersagt. Zuwiderhandlungen werden mit Übersetzungsfehlern geahndet.

Die folgende Methode stellt beispielsweise unter ARC keinen Verstoß gegen die Speicherverwaltungsregeln dar:

- (NSString *)newName {
return [NSString stringWithFormat:@"%@ %@",
self.firstName, self.lastName];
}

Der ARC-Compiler bemerkt vielmehr die Missachtung der ersten Speicherverwaltungsregel und korrigiert den Code während der Übersetzung, indem er ein retain einfügt (die Änderungen sind hervorgehoben):

- (NSString *)newName {
return [[NSString stringWithFormat:@"%@ %@",
self.firstName, self.lastName] retain];
}

Rheinwerk Computing - Zum Seitenanfang

2.4.6Weakie und die starken ZeigerZur nächsten ÜberschriftZur vorigen Überschrift

Wenn Sie eine Variable oder ein Attribut deklarieren, setzt der Compiler ihren Wert zunächst auf nil und legt sie als starke Referenz an, die der generierte Code am Ende ihres Gültigkeitsbereiches (Blocks) wieder freigibt. Die Deklaration

{
NSString *theName;
...
}

wandelt der Compiler bei der Übersetzung also in

{
__strong NSString *theName = nil;
...
[theName release];
}

um. Bei Attributen erzeugt der Compiler die release-Anweisung natürlich nicht im Block, sondern im dealloc der Klasse.

Der Speicherverwaltungstyp __strong legt dabei fest, dass die Variable das referenzierte Objekt über ein retain hält. Die Initialisierung mit nil erhöht die Stabilität Ihres Codes, da Sie dadurch eine Nachricht an die Objektreferenz senden können, ohne sie explizit vorher zu initialisieren. Die Zuweisung ist außerdem für das korrekte Funktionieren von weiteren Zuweisungen notwendig. Denn aus

theName = theNewName;

erzeugt der Compiler:

[theNewName retain];
NSString
*theOldName = theName;
theName = theNewName;
[theOldName
release];

Ohne die Initialisierung auf nil enthielte die letzte Zeile in der Regel einen Dangling Pointer.

Das Gegenstück zu __strong ist __weak. Es legt die Variable als schwache Referenz an, und somit erzeugt der Compiler keine Speicherverwaltungsnachrichten wie retain und release zu dieser Referenz. So gesehen verhält es sich wie weak bei Propertys. Diesen Typ müssen Sie natürlich immer explizit angeben. Der Compiler erweitert auch hierbei Ihren Programmcode. Das Laufzeitsystem setzt solche schwachen Referenzen automatisch auf nil, wenn das Programm das Objekt löscht, auf das die schwache Referenz zeigt.

Propertys und das automatische Referenzzählen

Der Speicherverwaltungstyp weak verhindert die Entstehung von Dangling Pointern. Zu diesem Typ gibt es keine Entsprechung beim manuellen Referenzzählen. Wenn Sie weak anstatt assign verwenden, setzt die Laufzeitumgebung den Property-Wert automatisch auf nil, wenn Ihr Programm das referenzierte Objekt freigibt. Sie können natürlich retain und copy wie gewohnt weiterverwenden. Jedoch ist die Verwendung von strong anstelle von retain bezüglich weak stringenter.

Analog zu den Modifizierern __strong und __weak gibt es die entsprechenden Parameter strong und weak für Propertys. Dabei ist strong nur ein Synonym für retain. Der ARC-Compiler erzeugt automatisch bei Propertys mit den Typen copy, retain und strong die entsprechende Freigabeanweisung in der dealloc-Methode der Klasse. Sie brauchen deswegen also diese Methode nicht zu implementieren.

Sie dürfen jedoch auch weiterhin dealloc überschreiben und für die Freigabe anderer Ressourcenarten (z. B. Filehandles, Beobachter, Sockets und Streams) verwenden. Der ARC-Compiler fügt am Ende einer selbstgeschriebenen dealloc-Methode automatisch den Aufruf der Supermethode ein, und Sie brauchen das nicht mehr zu tun. ARC ist eine Sonderform des Referenzzählens, die Ihnen in vielen Fällen die Speicherverwaltung erheblich vereinfacht.

Es besteht ein fundamentaler Unterschied zwischen weak und autorelease: Im Gegensatz zum Autoreleasepool hält eine schwache Referenz niemals ein Objekt. Der folgende Code ist zwar zulässig, führt allerdings zu einem Laufzeitfehler:

// Schnittstelle
@property(weak, nonatomic) UILabel *label;
@property(weak, nonatomic) UIButton *button;
// Implementierung
self.label = [[UILabel alloc] initWithFrame:theFrame];
self.button = [UIButton buttonWithType:UIButtonTypeInfoDark];
[self addSubview:self.label];
[self addSubview:self.button];

Listing 2.108 Problematische Verwendung einer »weak«-Property

Die »dealloc«-Methode und ARC

In einer ARC-Umgebung ist überall der explizite Aufruf von dealloc verboten, auch der Superaufruf am Ende von dealloc. Bei Zuwiderhandlung erhalten Sie die folgende Fehlermeldung:

ARC forbids explicit message send of 'dealloc'.

Der Compiler übersetzt diesen Code problemlos. Die Property label zeigt allerdings bei der Ausführung sofort nach der Zuweisung auf nil. Das liegt daran, dass der ARC-Compiler direkt nach der Erzeugung ein release an das Label sendet. Da es dann jedoch keine haltende Referenz mehr darauf gibt, setzt wiederum die Laufzeitumgebung alle schwachen Referenzen auf das Label – also auch die Property – auf nil. Der ARC-Compiler erkennt übrigens dieses Problem und warnt Sie mit der Meldung assigning retained object to weak property; object will be released after assignment.

Bei dem Button tritt dieses Problem nicht auf, da ihn das Programm über einen Convenience-Konstruktor erzeugt hat, der ihn in den Autoreleasepool legt. Der Pool hält seinerseits die Referenz, so dass die Laufzeitumgebung die Property button nicht auf nil setzt.

ARC != ARP

Wie Sie an dem Beispiel sehen, setzt automatisches Referenzzählen nicht die Speicherverwaltungsregeln außer Kraft. Eine Methode, die auf die erste Regel passt, gibt also nach wie vor die Halterschaft am zurückgegebenen Objekt ab.

Sie können das Problem bei dem Label umgehen, indem Sie eine lokale Variable verwenden:

// Schnittstelle
@property(weak, nonatomic) UILabel *label;
@property(weak, nonatomic) UIButton *button;
// Implementierung
UILabel *theLabel = [[UILabel alloc] initWithFrame:theFrame];
self.label = theLabel;
self.button = [UIButton buttonWithType:UIButtonTypeInfoDark];
[self addSubview:self.label];
[self addSubview:self.button];

Listing 2.109 Unproblematische Verwendung der »weak«-Property

Diese Variante umgeht das Problem aus Listing 2.108, weil hier die lokale Variable das Label hält.

Speicherwaltungsregeln und ARC

An dem Beispiel aus Listing 2.108 sehen Sie, wie wichtig es ist, die Speicherverwaltungsregeln verstanden zu haben – auch bei Verwendung von ARC. Zwar vereinfacht Ihnen ARC die Handhabung der Speicherverwaltung erheblich, allerdings ist es trotzdem wichtig, zu wissen, wie die Speicherverwaltung funktioniert.

Es gibt noch zwei weitere mögliche Speicherverwaltungstypen. Durch den Modifizierer __unsafe_unretained können Sie erreichen, dass sich eine Variable wie ein gewöhnlicher Zeiger in C verhält. Der Compiler verändert den Code für diese Variablen also nicht.

Der Modifizierer __autoreleasing veranlasst den Compiler, ein retain und ein autorelease bei der Zuweisung an den Wert zu senden. Er ist der Standardtyp für Ausgabeparameter – also Zeiger auf Objektreferenzen. Ein typisches Beispiel dafür sind Fehlerobjekte, die viele Cocoa-Methoden zurückgeben:

- (void)readWithError:(NSError **)outError {
...
if(!success) {
*outError = ...;
}
}

Das übersetzt der Compiler in:

- (void)readWithError:(__autoreleasing NSError **)outError {
...
if(!success) {
*outError = [[... retain] autorelease];
}
}

Automatisches Referenzzählen und ältere iOS-Versionen

Sie dürfen das automatische Referenzzählen auch verwenden, wenn Ihre App unter iOS 4 laufen soll. Allerdings unterstützt diese iOS-Version keine schwachen Referenzen; Sie dürfen dort also weder weak noch __weak verwenden, da die Laufzeitumgebung die entsprechenden Funktionen nicht kennt. Diese Referenzen müssen Sie hingegen über assign oder __unsafe_unretained abbilden.


Rheinwerk Computing - Zum Seitenanfang

2.4.7EinzelgängerZur nächsten ÜberschriftZur vorigen Überschrift

Die Richtlinien für ARC-konformen Programmcode untersagen Ihnen nicht nur die Verwendung der Methoden retain, release und autorelease, sondern auch das Überschreiben dieser Methoden. Das mussten Sie bei älteren Versionen in der Regel sowieso selten machen. Für die Implementierung des Singleton-Entwurfsmusters beim manuellen Referenzzählen überschreibt man in der Regel diese Methoden, um sicherzustellen, dass es nur ein Objekt der Klasse gibt.

Pattern oder Antipattern?

Singletons gehören zu den 23 Entwurfsmustern, die Gamma, Helm, Johnson und Vlissides – die berühmte Gang of Four – in ihrem Buch über Entwurfsmuster [Anm.: E. Gamma, R. Helm, R. Johnson, J. Vlissides: Entwurfsmuster. Elemente wiederverwendbarer objektorientierter Software, München: Addison-Wesley 2004.] beschrieben haben. Allerdings betrachtet inzwischen auch eine große Anzahl von Entwicklern das Singleton-Muster als Antipattern, weil es leicht zu statischem und unflexiblem Programmcode führt. Auch Erich Gamma sieht das Muster inzwischen kritisch:

»When discussing which patterns to drop, we found that we still love them all. (Not reallyI’m in favor of dropping Singleton. Its use is almost always a design smell.)«

Wie dem auch sei, setzen Sie dieses Muster allenfalls in homöopathischen Dosen ein. Außerdem sollten Sie bei Ihren Singletons auch immer an die Einschränkungen durch das MVC-Architekturmuster denken. In der Regel gehört es zur Controller-Schicht und hat in der View- oder gar Modellschicht nichts verloren.

Wir stellen hier eine einfache Möglichkeit vor, wie Sie Singletons in ARC-Umgebungen implementieren können, obwohl wir der Ansicht von Erich Gamma zustimmen, dass sie fast immer ein Design Smell sind – also ein schlechtes Design. Andererseits muss jeder Programmierer selbst entscheiden, welche Muster und welches Design er bevorzugt.

Die einfachste Möglichkeit für die Implementierung eines Singletons ist eine Klassenmethode und eine statische Variable. Die statische Variable verweist dabei auf das Objekt und hält es im Speicher.

+ (id)sharedInstance {
static id sharedInstance;
if (sharedInstance == nil) {
sharedInstance = [[MySingleton alloc] init];
}
return sharedInstance;
}

Listing 2.110 Einfaches Singleton, über eine Klassenmethode implementiert

Allerdings vernachlässigt diese Implementierung eine Anforderung des Musters: Ein Singleton stellt sicher, dass es maximal ein Objekt dieser Klasse im Programm gibt. Diese Anforderung können Sie einhalten, indem Sie die Klassenmethode allocWithZone: überschreiben. Die Klassenmethode alloc verwendet diese Methode, um den Speicher für ein neues Objekt zu allozieren.

static id sharedInstance = nil;
+ (id)allocWithZone:(NSZone *)inZone {
if(sharedIntance == nil) {
sharedInstance = [super allocWithZone:inZone];
return sharedInstance;
}
else {
return nil;
}
}
+ (id)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}

Listing 2.111 Es darf nur eins geben.

Die statische Variable sharedInstance enthält die Referenz auf das Objekt. Die Klassenmethode allocWithZone: liefert nur dann diese Referenz, wenn Sie das Objekt auch tatsächlich erzeugt hat und ansonsten nil. Dieses Vorgehen hat den Vorteil, dass ein späterer Aufruf von [[MySingleton alloc] init] nicht zu einem erneuten Aufruf der Methode init auf dem bereits initialisierten Objekt führt.


Rheinwerk Computing - Zum Seitenanfang

2.4.8Migration bestehender ProjekteZur vorigen Überschrift

Das Verbot der drei Speicherverwaltungsmethoden retain, release und autorelease führt natürlich zu Programmcode, der ohne ARC eine fehlerhafte Speicherverwaltung hat. Da der Compiler nach wie vor auch die manuelle Speicherverwaltung unterstützt, müssen Sie ihm mitteilen, um welche Art von Code es sich handelt. Dazu gibt es das Kommandozeilenflag -fobjc-arc beziehungsweise die Einstellung Objective-C Automatic Reference Counting in den Build Settings des Programm-Targets (siehe Abbildung 2.42).

Abbildung

Abbildung 2.42 Automatisches Referenzzählen einschalten

Sie sollten allerdings diese Einstellung nicht einfach ändern. Um Ihren bestehenden Code für das automatische Referenzzählen umzuwandeln, können Sie stattdessen den Menüpunkt EditRefactorConvert to Objective-C ARC... aufrufen. Stellen Sie jedoch vorher das Schema auf einen iPhone-Simulator um (siehe Abbildung 2.43). Dadurch lassen sich Fehler bei der Migration verhindern.

Abbildung

Abbildung 2.43 Umstellen des Schemas auf den iPhone-Simulator

Nach dem Aufruf des Menüpunkts fragt Xcode Sie, welches Target Ihres Projekts Sie konvertieren möchten (siehe Abbildung 2.44). Die bislang erstellten Projekte enthalten jeweils zwei Targets, und es ist bei Projekten mit mehreren Targets nur in seltenen Fällen sinnvoll, gemischten Code zu haben. Die Auswahl fällt also in der Regel leicht. Des Weiteren können Sie die Konvertierung für einzelne Dateien ein- oder ausschalten, indem Sie das Dreieck neben dem Häkchen anklicken.

Abbildung

Abbildung 2.44 Auswahl des Konvertierungs-Targets

Wenn Sie Check anklicken, startet Xcode eine Prüfung für die Konvertierung. Wenn Xcode dabei keine Fehler mehr findet, zeigt es einen Dialog an, der Ihnen die nachfolgenden Schritte erläutert (siehe Abbildung 2.45). Klicken Sie einfach auf den Button Next, um fortzufahren.

Anschließend zeigt Xcode einen Fortschrittsdialog für die Konvertierung an. Falls Sie für das Projekt noch keine Snapshots eingeschaltet haben, fragt Xcode Sie danach.

Bitte recht freundlich

Ein Snapshot ist eine Momentaufnahme Ihres Projekts. Er enthält alle Dateien und Einstellungen des Projekts ab dem Zeitpunkt, an dem Xcode ihn erstellt hat. Sie können damit also Zwischenstände Ihrer Projekte speichern. Das ist praktisch bei größeren Änderungen an Ihrem Programmcode, besonders wenn Xcode sie automatisiert durchführt.

Über den Menüpunkt FileCreate Snapshot... lassen sich jederzeit auch eigene Zwischenstände speichern. Um einen Snapshot wiederherzustellen, verwenden Sie einfach den Menüpunkt FileRestore Snapshot...

Abbildung

Abbildung 2.45 Start der Konvertierung

Da Xcode bei größeren Projekten Ihren Code sehr stark verändert, sollten Sie auf jeden Fall Snapshots zulassen (siehe Abbildung 2.46).

Abbildung

Abbildung 2.46 Einschalten der Snapshots

Anschließend startet Xcode die Konvertierung Ihres Projekts. Es zeigt dabei den Fortschritt in einem Dialog an. Nach der Konvertierung zeigt Xcode ein Fenster mit den Änderungen an den einzelnen Dateien an (siehe Abbildung 2.47). Sie können in der linken Spalte die Datei auswählen, deren Änderungen Sie ansehen möchten. Daneben sehen Sie den alten Quellcode der ausgewählten Datei und auf der rechten Seite den geänderten Code. Die Ansicht hinterlegt die Änderungen dabei in Hellblau.

Abbildung

Abbildung 2.47 Anzeige der Änderungen

Wenn Sie schließlich auf den Button Save klicken, übernimmt Xcode alle Änderungen in Ihren Programmcode und passt auch die Compiler-Einstellungen in den Build Settings an. Nach diesem letzten Schritt haben Sie die Migration auf das automatische Referenzzählen abgeschlossen.

Ja, ich will

Auch wenn das automatische Referenzzählen der Standard für alle zukünftigen Entwicklungen und Projekte ist, müssen Sie Ihre Projekte nicht unbedingt konvertieren. Sie können auch Code mit und ohne ARC innerhalb einer App verwenden, wenn Sie die Programmteile mit jeweils den richtigen ARC-Einstellungen übersetzt haben. Das ist möglich, weil ja ARC auf dem manuellen Referenzzählen basiert. Falls Ihnen Xcode bei der Konvertierung zu viele Fehler meldet, können Sie die Umstellung also ruhig noch etwas auf die lange Bank schieben.



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< 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

Cookie-Einstellungen ändern


  Zum Rheinwerk-Shop
Zum Katalog: Apps programmieren für iPhone und iPad






Neuauflage: Apps programmieren für iPhone und iPad
Jetzt Buch 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


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo