Retain, Release und Autorelease
Ingo / 30. Aug 2009, 11:38 Uhr.
Gespeichert in: Grundlagen
string2 = string1), dann wird in Wirklichkeit nur der Zeiger kopiert. Beide Variablen verweisen auf das gleiche Objekt.Das hat nachhaltige Folgen. Schickt man Methoden an das Originalobjekt, dann wirkt sich das genauso auf den zweiten "Zeiger" aus und umgekehrt, genau wie bei einer Datei auf der Festplatte und einem Alias (unter Windows auch 'Verknüpfung' genannt).
Was passiert also, wenn ein Programmteil ein Objekt per
release freigibt (siehe Artikel Objekte erstellen) und ein anderer Programmteil greift nun doch nochmal über einen anderen Zeiger auf das Objekt zu? Das Programm stürzt gnadenlos ab!Um dieses Problem zu umgehen, hat man in Cocoa die Möglichkeit geschaffen einem Objekt zu sagen, dass man es noch benötigt. Gibt dann ein anderer Programmteil das Objekt per
release frei, wird es nicht wirklich freigegeben. Es wird erst freigegeben, wenn alle beteiligten Programmteile per release bestätigt haben, dass sie das Objekt nicht mehr benötigen. Die Objekte zählen intern mit, wie oft sie benötigt werden. Den "Anspruch" auf ein Objekt meldet man mit retain an:NSString* string2; // Einen neuen Zeiger definieren
string2 = string1; // Eine Kopie des Zeigers 'string1' anfertigen
[string2 retain]; // Dem System mitteilen, dass wir 'string2' eine Weile benötigen
[mach_was_sinnvolles];
[string2 release]; // wir geben das Objekt wieder freiNochmals zur Verdeutlichung: Man kann das
retain oder release oben auch an string1 schicken. Es handelt sich ja um das gleiche Objekt, nur unter einem anderen Namen!Wie man sicher schnell erkennt, macht ein
retain auf ein Objekt meistens nur Sinn, wenn es über mehrere Methoden hinweg erhalten bleiben soll. Innerhalb eines einzelnen Codeblocks kann ich ja schließlich auch den ersten Zeiger benutzen. Hierzu seht ihr in unserem Videocast mehr! Wir behandeln das Thema Speicherverwaltung ausführlich ab der Folge '#007: Speicher, Zeiger und Objekte'.Alle Methoden, die mit
init oder copy beginnen, machen übrigens automatisch ein retain. Hier ist also zum Ende der Benutzung nur noch ein release notwendig.Auch wenn man ein Objekt als Parameter an ein anderes Objekt übergibt, dann muss sich der "Empfänger" selbst darum kümmern, ob er das Objekt noch eine Weile braucht, zum Beispiel wenn man einen String an ein NSTextField übergibt. Man muss also nicht für andere Objekte mitdenken:
[meinTextFeld setStringValue:string1];
// Hier muss man nicht für das Textfeld mitdenken.
// Das Textfeld macht selbst ein retain auf string1, wenn es den String behalten will.Weiss man zu Beginn einer Codeblocks bereits, dass man ein selbsterstelltes Objekt nur kurzfristig benötigt, dann kann man auch die Funktion
autorelease benutzen und es damit dem System überlassen, dass dem Objekt irgendwann nach Beendigung des aktuellen Codeblocks ein release geschickt wird:Manchmal ist es erwünscht, dass man doch eine komplett neue Kopie eines Objektes erhält, die somit auch nicht mehr mit dem Originalobjekt in Verbindung steht. Hierzu gibt es die Methode
copy. Diese Methode kopiert eimal das komplette Objekt in einen neuen Speicherbereich und führt auch schon ein retain aus:NSString* string1 = @"Hallo Welt!";
NSString* string2 = [string1 copy];
// Erzeugt eine Kopie von string1. Es gibt also nun zwei Objekte.
// Entsprechend wird auch doppelt so viel Speicherplatz verbraucht.Mit
retain und release muss man sehr gut aufpassen! Ein retain zuviel, schon wird der Speicherbereich nicht mehr freigegeben. Ein release zuviel, schon stürzt das Programm ab!
12 Kommentare
|
Permalink
|
Trackback-Info
Kommentar hinzufügen
12. Sven am 28. Jun 2010, 07:29 Uhr
Hallo!
Vielen Dank für die Ganze Mühe! Weiter so :P
Wo ist der Unterschied zwischen autorealease und sich garnicht um Release und retain kümmern?
Hallo!
Vielen Dank für die Ganze Mühe! Weiter so :P
Wo ist der Unterschied zwischen autorealease und sich garnicht um Release und retain kümmern?
11. Ralph Bergmann am 6. Apr 2010, 14:48 Uhr
Hat sich da nicht ein kleiner Fehler eingeschlichen?
Du schreibst immer, dass init den retainCount um 1 erhöhen. Doch dies ist doch garnicht so.
Laut http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html erhöhen alloc und copy, nicht aber init.
Ralph
Hat sich da nicht ein kleiner Fehler eingeschlichen?
Du schreibst immer, dass init den retainCount um 1 erhöhen. Doch dies ist doch garnicht so.
Laut http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html erhöhen alloc und copy, nicht aber init.
Ralph
10. ingo am 24. Jan 2010, 20:21 Uhr
@Witold:
Es gibt eine einfache Regel: man muss seine Objekte immer selbst retainen und entsprechend auch wieder releasen, außer bei init- oder copy-Methoden.
wenn Du Dir also eine Variable über einen längeren Zeitraum bunkern willst, in Deiner eigenen Instanzvariablen, dann musst Du auch retainen.
In Deinem Fall merkst Du Dir das Objekt ja nur kurz in einer Variablen innerhalb Deiner Methode. Da muss man dann auch nicht retainen, weil die Variable ja sowieso mit Beendigung der Methode futsch ist. Das kann höchstens Probleme machen, wenn mehrere parallele Threads mit der Variablen arbeiten wollen.
Mit addObject wiederum übergibst Du das Objekt einem anderen Objekt, in diesem Falle ein Array. Wenn ein solches Array meint, dass irgendein retain oder release nötig ist, dann hat es das gefälligst selbst zu tun. Ein Objekt ist also immer für seine eigenen Variablen verantwortlich.
@Witold:
Es gibt eine einfache Regel: man muss seine Objekte immer selbst retainen und entsprechend auch wieder releasen, außer bei init- oder copy-Methoden.
wenn Du Dir also eine Variable über einen längeren Zeitraum bunkern willst, in Deiner eigenen Instanzvariablen, dann musst Du auch retainen.
In Deinem Fall merkst Du Dir das Objekt ja nur kurz in einer Variablen innerhalb Deiner Methode. Da muss man dann auch nicht retainen, weil die Variable ja sowieso mit Beendigung der Methode futsch ist. Das kann höchstens Probleme machen, wenn mehrere parallele Threads mit der Variablen arbeiten wollen.
Mit addObject wiederum übergibst Du das Objekt einem anderen Objekt, in diesem Falle ein Array. Wenn ein solches Array meint, dass irgendein retain oder release nötig ist, dann hat es das gefälligst selbst zu tun. Ein Objekt ist also immer für seine eigenen Variablen verantwortlich.
9. Witold am 24. Jan 2010, 11:58 Uhr
Hallo,
vielen Dank für den guten Artikel.
Ich habe eine kurze Frage, die ich nicht verstehe:
Folgender Code:
NSMutableArray *a = [[NSMutableArray alloc] init];
NSMutableString *s = [[NSMutableString alloc] initWithString: @"test"];
// hier ist der retain count von s 1
[a addObject: s];
// hier ist der retain count von s 2 (pointer in s und im Array a)
[s release];
NSMutableString *s2 = [a lastObject];
Wenn ich mir den Retain-Cound von s2 ausgebe, steht da 1.
Müsste er nicht 2 sein, weil sowohl s2 wie auch das Array a drauf zeigen?
Woran liegt das?
Vielen Dank für einen Hinweis!
Gruß, Witold
Hallo,
vielen Dank für den guten Artikel.
Ich habe eine kurze Frage, die ich nicht verstehe:
Folgender Code:
NSMutableArray *a = [[NSMutableArray alloc] init];
NSMutableString *s = [[NSMutableString alloc] initWithString: @"test"];
// hier ist der retain count von s 1
[a addObject: s];
// hier ist der retain count von s 2 (pointer in s und im Array a)
[s release];
NSMutableString *s2 = [a lastObject];
Wenn ich mir den Retain-Cound von s2 ausgebe, steht da 1.
Müsste er nicht 2 sein, weil sowohl s2 wie auch das Array a drauf zeigen?
Woran liegt das?
Vielen Dank für einen Hinweis!
Gruß, Witold
8. ingo am 19. Oct 2009, 23:43 Uhr
@Jörg:
Richtig. @"..." ist ein Makro und erstellt zur Kompilerzeit ein Stringobjekt. Man kann alle Methoden an einen solchen String schicken, die man auch an einen NSString schicken kann. Allerdings wird ein solcher Makrostring niemals durch ein release freigegeben!
@Jörg:
Richtig. @"..." ist ein Makro und erstellt zur Kompilerzeit ein Stringobjekt. Man kann alle Methoden an einen solchen String schicken, die man auch an einen NSString schicken kann. Allerdings wird ein solcher Makrostring niemals durch ein release freigegeben!
7. Jörg am 19. Oct 2009, 23:22 Uhr
Hallo,
jetzt mal eine (vielleicht) blöde Frage, was ist der Unterschied zwischen:
NSString* string1 = @"Hallo Welt!";
und
NSString* string1 = [[NSString alloc] initWithString:@"Hallo Welt!"];
Wird bei der ersten Variante explizit ein alloc gemacht? Weil wie es bisher gesehen habe muss bei der ersten kein release gemacht werden und der Zugriff darauf hat genauso funktioniert. Diese eine Sache fehlt mir noch um es ganz zu verstehen.
Danke!
Hallo,
jetzt mal eine (vielleicht) blöde Frage, was ist der Unterschied zwischen:
NSString* string1 = @"Hallo Welt!";
und
NSString* string1 = [[NSString alloc] initWithString:@"Hallo Welt!"];
Wird bei der ersten Variante explizit ein alloc gemacht? Weil wie es bisher gesehen habe muss bei der ersten kein release gemacht werden und der Zugriff darauf hat genauso funktioniert. Diese eine Sache fehlt mir noch um es ganz zu verstehen.
Danke!
6. Paul am 23. Sep 2009, 15:03 Uhr
Super,
danke. So hatte ich mir das schon fast gedacht. Dann habe ich das jetzt auch verstanden.
Vielen Dank und macht weiter so!
Super,
danke. So hatte ich mir das schon fast gedacht. Dann habe ich das jetzt auch verstanden.
Vielen Dank und macht weiter so!
5. ingo am 23. Sep 2009, 15:00 Uhr
Hi Paul,
string1 und string2 sind ja derselbe String, soweit ist es ja klar. Als string1 ist er ja höchswahrscheinlich auch irgendwie per retain reserviert worden, also muss ein weiteres release abgeschickt werden. Beispiel:
NSString* string1 = [[NSString alloc] initWithString:@"Hallo Welt!"]; // der retainCount ist nun 1, dank dem "init"
NSString* string2 = string1; // der retainCount ist weiterhin 1
[string2 retain]; // der retainCount ist nun 2
[string1 release]; // der retainCount ist wieder 1
[string2 release]; // der retainCount geht auf 0, der Speicher wird vom System freigegeben!
Hätte man in der ersten Zeile noch ein autorelease hinzugefügt, dann dürfte man string1 NICHT releasen, da dies irgendwann durch das System passiert!
Hi Paul,
string1 und string2 sind ja derselbe String, soweit ist es ja klar. Als string1 ist er ja höchswahrscheinlich auch irgendwie per retain reserviert worden, also muss ein weiteres release abgeschickt werden. Beispiel:
NSString* string1 = [[NSString alloc] initWithString:@"Hallo Welt!"]; // der retainCount ist nun 1, dank dem "init"
NSString* string2 = string1; // der retainCount ist weiterhin 1
[string2 retain]; // der retainCount ist nun 2
[string1 release]; // der retainCount ist wieder 1
[string2 release]; // der retainCount geht auf 0, der Speicher wird vom System freigegeben!
Hätte man in der ersten Zeile noch ein autorelease hinzugefügt, dann dürfte man string1 NICHT releasen, da dies irgendwann durch das System passiert!
4. Paul am 23. Sep 2009, 14:37 Uhr
Hallo Leute,
ich habe noch eine Frage zu diesem Beispiel:
NSString* string2; // Einen neuen Zeiger definieren
string2 = string1; // Eine Kopie des Zeigers 'string1' anfertigen
[string2 retain]; // Dem System mitteilen, dass wir 'string2' eine Weile benötigen
[mach_was_sinnvolles];
[string2 release]; // wir geben das Objekt wieder frei
Wenn ich den kompletten Speicher wieder freigeben will muss ich dann noch ein [string 1 release] machen oder ist dieser automatisch freigegeben, weil ich den Zeiger mit string2 erstellt habe??
Danke!
Paul
Hallo Leute,
ich habe noch eine Frage zu diesem Beispiel:
NSString* string2; // Einen neuen Zeiger definieren
string2 = string1; // Eine Kopie des Zeigers 'string1' anfertigen
[string2 retain]; // Dem System mitteilen, dass wir 'string2' eine Weile benötigen
[mach_was_sinnvolles];
[string2 release]; // wir geben das Objekt wieder frei
Wenn ich den kompletten Speicher wieder freigeben will muss ich dann noch ein [string 1 release] machen oder ist dieser automatisch freigegeben, weil ich den Zeiger mit string2 erstellt habe??
Danke!
Paul
3. Mathias (Speedy) am 15. Sep 2009, 22:48 Uhr
Aha, also gibt's sowas wie einen Garbage Collector auch bei Objective-C - hab' ich gar nicht gewusst.
Aber wenn's das beim iPhone nicht gibt, bin ich froh, dass ich's jetzt dank Ingo & Peter lerne ;) - danke
Aha, also gibt's sowas wie einen Garbage Collector auch bei Objective-C - hab' ich gar nicht gewusst.
Aber wenn's das beim iPhone nicht gibt, bin ich froh, dass ich's jetzt dank Ingo & Peter lerne ;) - danke
2. peter am 15. Sep 2009, 07:44 Uhr
Hallo Sebastian,
der Einstieg wäre evtl. einfacher, der Kopfschmerz danach umso heftiger. Denn das iPhone unterstützt keinen GC und spätestens dann müsste man doch mit dem Speicherkram befassen.
Peter
Hallo Sebastian,
der Einstieg wäre evtl. einfacher, der Kopfschmerz danach umso heftiger. Denn das iPhone unterstützt keinen GC und spätestens dann müsste man doch mit dem Speicherkram befassen.
Peter
1. Sebastian R. am 15. Sep 2009, 07:02 Uhr
Ich würde mich freuen, wenn ihr bei Gelegenheit mal was zum Cocoa Garbage Collection Framework sagen könntet. Ist der GC nicht - gerade für Cocoa-Einsteiger, eine gute Alternative zum manuellen Speichermanagement (was natürlich nicht heißen soll, dass das Hintergrundwissen über Speichermanagement unwichtig ist)? Gerade alle, die aus der Java-Ecke kommen, würden sich über GC in Cocoa sicher freuen ;-)
Ich würde mich freuen, wenn ihr bei Gelegenheit mal was zum Cocoa Garbage Collection Framework sagen könntet. Ist der GC nicht - gerade für Cocoa-Einsteiger, eine gute Alternative zum manuellen Speichermanagement (was natürlich nicht heißen soll, dass das Hintergrundwissen über Speichermanagement unwichtig ist)? Gerade alle, die aus der Java-Ecke kommen, würden sich über GC in Cocoa sicher freuen ;-)