Bei ARC (Automatic Reference Counting) müssen wir zum größten Teil überhaupt nicht an die Speicherverwaltung mit Objective-C-Objekten denken. Es ist nicht mehr erlaubt, NSAutoreleasePool
s zu erstellen , es gibt jedoch eine neue Syntax:
@autoreleasepool {
…
}
Meine Frage ist, warum sollte ich das jemals brauchen, wenn ich nicht manuell freigeben / automatisch freigeben soll?
EDIT: Um zusammenzufassen, was ich aus all den Antworten und Kommentaren herausgeholt habe:
Neue Syntax:
@autoreleasepool { … }
ist eine neue Syntax für
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];
Wichtiger:
- ARC verwendet
autorelease
ebenso wierelease
. - Dazu ist ein Pool für die automatische Freigabe erforderlich.
- ARC erstellt den Auto-Release-Pool nicht für Sie. Jedoch:
- Der Haupt-Thread jeder Cocoa-App enthält bereits einen Autorelease-Pool.
- Es gibt zwei Fälle, in denen Sie möglicherweise davon Gebrauch machen möchten
@autoreleasepool
:- Wenn Sie sich in einem sekundären Thread befinden und kein Pool für die automatische Freigabe vorhanden ist, müssen Sie einen eigenen erstellen, um Lecks zu vermeiden, z
myRunLoop(…) { @autoreleasepool { … } return success; }
. - Wenn Sie einen lokaleren Pool erstellen möchten, wie @mattjgalloway in seiner Antwort gezeigt hat.
- Wenn Sie sich in einem sekundären Thread befinden und kein Pool für die automatische Freigabe vorhanden ist, müssen Sie einen eigenen erstellen, um Lecks zu vermeiden, z
Antworten:
ARC entfernt keine Retains, Releases und Autoreleases, sondern fügt nur die für Sie erforderlichen hinzu. Es gibt also immer noch Aufrufe zum Beibehalten, es gibt immer noch Aufrufe zum Freigeben, es gibt immer noch Aufrufe zum Autorelease und es gibt immer noch Pools für die automatische Freigabe.
Eine der anderen Änderungen, die sie mit dem neuen Clang 3.0-Compiler und ARC vorgenommen haben, ist, dass sie durch
NSAutoReleasePool
die@autoreleasepool
Compiler-Direktive ersetzt wurden.NSAutoReleasePool
war sowieso immer ein bisschen ein spezielles "Objekt" und sie haben es so gemacht, dass die Syntax der Verwendung eines Objekts nicht mit einem Objekt verwechselt wird, so dass es im Allgemeinen etwas einfacher ist.Grundsätzlich brauchen Sie also,
@autoreleasepool
weil es immer noch Pools mit automatischer Freigabe gibt, über die Sie sich Sorgen machen müssen. Sie müssen sich nur nicht um das Hinzufügen vonautorelease
Anrufen kümmern .Ein Beispiel für die Verwendung eines Pools mit automatischer Freigabe:
Sicherlich ein äußerst ausgeklügeltes Beispiel, aber wenn Sie nicht das
@autoreleasepool
Innere der äußerenfor
Schleife hätten, würden Sie später 100000000 Objekte freigeben, anstatt jedes Mal 10000 um die äußerefor
Schleife herum.Update: Siehe auch diese Antwort - https://stackoverflow.com/a/7950636/1068248 - warum
@autoreleasepool
es nichts mit ARC zu tun hat.Update: Ich habe mir die Interna angesehen, was hier vor sich geht, und es in meinem Blog geschrieben . Wenn Sie dort einen Blick darauf werfen, werden Sie genau sehen, was ARC tut und wie
@autoreleasepool
der Compiler den neuen Stil und die Einführung eines Bereichs verwendet, um Informationen darüber abzuleiten, welche Aufbewahrungen, Releases und Autoreleases erforderlich sind.quelle
@autoreleasepool
für mich hinzu? Wenn ich nicht kontrolliere, was automatisch freigegeben oder freigegeben wird (ARC erledigt das für mich), wie sollte ich wissen, wann ich einen Autorelease-Pool einrichten muss?@autoreleasepool
veröffentlicht nichts automatisch. Es wird ein Autorelease-Pool erstellt, sodass bei Erreichen des Blockendes alle Objekte, die von ARC automatisch freigegeben wurden, während der Block aktiv war, Freigabemeldungen gesendet werden. Das Advanced Memory Management-Programmierhandbuch von Apple erklärt dies folgendermaßen:quelle
release
Nachricht, aber wenn die Anzahl der Aufbewahrungen> 1 ist, wird das Objekt NICHT freigegeben.Menschen verstehen ARC oft falsch für irgendeine Art von Müllabfuhr oder dergleichen. Die Wahrheit ist, dass die Mitarbeiter von Apple nach einiger Zeit (dank llvm- und clang-Projekten) erkannt haben, dass die Speicherverwaltung von Objective-C (alle
retains
undreleases
usw.) zur Kompilierungszeit vollständig automatisiert werden kann . Dies geschieht, indem Sie den Code lesen, noch bevor er ausgeführt wird! :) :)Dazu gibt es nur eine Bedingung: Wir MÜSSEN die Regeln befolgen , sonst könnte der Compiler den Prozess zur Kompilierungszeit nicht automatisieren. Also, um sicherzustellen , dass wir nie die Regeln brechen, werden wir nicht explizit Schreib erlaubt
release
,retain
usw. Diese Anrufe werden automatisch in unseren Code durch den Compiler injiziert. Daher intern noch wir habenautorelease
s,retain
,release
usw. Es ist nur brauchen wir sie nicht mehr zu schreiben.Das A von ARC erfolgt zur Kompilierungszeit automatisch, was viel besser ist als zur Laufzeit wie bei der Speicherbereinigung.
Wir haben immer noch,
@autoreleasepool{...}
weil es keine der Regeln verletzt, wir können unseren Pool jederzeit erstellen / entleeren, wann immer wir es brauchen :).quelle
Dies liegt daran, dass Sie dem Compiler immer noch Hinweise geben müssen, wann sicher freigegebene Objekte den Gültigkeitsbereich verlassen können.
quelle
@autoreleasepool
weil ich nicht weiß, ob ARC sich möglicherweise dazu entschließt, etwas automatisch freizugeben?Zitiert aus https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html :
...
quelle
Autorelease-Pools sind erforderlich, um neu erstellte Objekte von einer Methode zurückzugeben. Betrachten Sie zB diesen Code:
Die in der Methode erstellte Zeichenfolge hat eine Aufbewahrungszahl von eins. Wer soll nun diese Anzahl mit einer Freigabe ausgleichen?
Die Methode selbst? Nicht möglich, es muss das erstellte Objekt zurückgeben, daher darf es nicht vor der Rückgabe freigegeben werden.
Der Aufrufer der Methode? Der Aufrufer erwartet nicht, dass ein Objekt abgerufen wird, das freigegeben werden muss. Der Methodenname bedeutet nicht, dass ein neues Objekt erstellt wird. Er gibt lediglich an, dass ein Objekt zurückgegeben wird, und dieses zurückgegebene Objekt kann ein neues Objekt sein, für das eine Freigabe erforderlich ist Nun, es gibt eine, die es nicht tut. Was die Methode zurückgibt, hängt möglicherweise sogar von einem internen Status ab, sodass der Aufrufer nicht wissen kann, ob er dieses Objekt freigeben muss und sich nicht darum kümmern muss.
Wenn der Aufrufer immer alle zurückgegebenen Objekte gemäß Konvention freigeben müsste, müsste jedes nicht neu erstellte Objekt immer beibehalten werden, bevor es von einer Methode zurückgegeben wird, und es müsste vom Aufrufer freigegeben werden, sobald es den Gültigkeitsbereich verlässt, es sei denn es wird wieder zurückgegeben. Dies wäre in vielen Fällen äußerst ineffizient, da in vielen Fällen eine Änderung der Aufbewahrungsanzahl vollständig vermieden werden kann, wenn der Aufrufer das zurückgegebene Objekt nicht immer freigibt.
Aus diesem Grund gibt es Autorelease-Pools, sodass die erste Methode tatsächlich wird
Der Aufruf
autorelease
auf einem Objekt fügt sie dem Autofreigabepool, aber was bedeutet das wirklich Mittel, um ein Objekt zu dem Autofreigabepool hinzufügen? Nun, es bedeutet, Ihrem System zu sagen: " Ich möchte, dass Sie dieses Objekt für mich freigeben, aber zu einem späteren Zeitpunkt, nicht jetzt. Es hat eine Aufbewahrungszahl, die durch eine Freigabe ausgeglichen werden muss, da sonst Speicherplatz verloren geht, aber ich kann das nicht selbst tun." Im Moment, da ich das Objekt brauche, um über meinen derzeitigen Bereich hinaus am Leben zu bleiben, und mein Anrufer es auch nicht für mich tun wird, weiß er nicht, dass dies getan werden muss. Fügen Sie es also Ihrem Pool hinzu und sobald Sie es aufgeräumt haben Pool, auch mein Objekt für mich aufräumen. "Mit ARC entscheidet der Compiler für Sie, wann ein Objekt beibehalten, wann ein Objekt freigegeben und wann es einem Autorelease-Pool hinzugefügt werden soll. Es sind jedoch weiterhin Autorelease-Pools erforderlich, um neu erstellte Objekte von Methoden zurückgeben zu können, ohne Speicher zu verlieren. Apple hat gerade einige raffinierte Optimierungen am generierten Code vorgenommen, die manchmal Autorelease-Pools zur Laufzeit eliminieren. Diese Optimierungen erfordern, dass sowohl der Anrufer als auch der Angerufene ARC verwenden (denken Sie daran, dass das Mischen von ARC und Nicht-ARC legal ist und auch offiziell unterstützt wird) und ob dies tatsächlich der Fall ist, kann nur zur Laufzeit bekannt sein.
Betrachten Sie diesen ARC-Code:
Der vom System generierte Code kann sich entweder wie der folgende Code verhalten (dies ist die sichere Version, mit der Sie ARC- und Nicht-ARC-Code frei mischen können):
(Beachten Sie, dass die Aufbewahrung / Freigabe im Anrufer nur eine defensive Sicherheitsaufbewahrung ist. Sie ist nicht unbedingt erforderlich. Ohne sie wäre der Code vollkommen korrekt.)
Oder es kann sich wie dieser Code verhalten, falls erkannt wird, dass beide zur Laufzeit ARC verwenden:
Wie Sie sehen können, eliminiert Apple die Atuorelease, also auch die verzögerte Objektfreigabe, wenn der Pool zerstört wird, sowie die Sicherheit. Um mehr darüber zu erfahren, wie das möglich ist und was wirklich hinter den Kulissen passiert, lesen Sie diesen Blog-Beitrag.
Nun zur eigentlichen Frage: Warum sollte man verwenden
@autoreleasepool
?Für die meisten Entwickler gibt es heute nur noch einen Grund, dieses Konstrukt in ihrem Code zu verwenden, nämlich den Speicherbedarf gegebenenfalls gering zu halten. Betrachten Sie zB diese Schleife:
Angenommen, jeder Aufruf von
tempObjectForData
kann eine neue erstellenTempObject
, die automatisch freigegeben wird. Die for-Schleife erstellt eine Million dieser temporären Objekte, die alle im aktuellen Autorelease-Pool gesammelt werden. Erst wenn dieser Pool zerstört wird, werden auch alle temporären Objekte zerstört. Bis dahin haben Sie eine Million dieser temporären Objekte im Speicher.Wenn Sie den Code stattdessen so schreiben:
Dann wird jedes Mal, wenn die for-Schleife ausgeführt wird, ein neuer Pool erstellt und am Ende jeder Schleifeniteration zerstört. Auf diese Weise bleibt zu jeder Zeit höchstens ein temporäres Objekt im Speicher hängen, obwohl die Schleife eine Million Mal ausgeführt wird.
In der Vergangenheit mussten Sie beim Verwalten von Threads (z. B. Verwenden
NSThread
) häufig auch Autorelease-Pools selbst verwalten, da nur der Haupt-Thread automatisch einen Autorelease-Pool für eine Cocoa / UIKit-App hat. Dies ist heute jedoch so ziemlich ein Vermächtnis, da Sie heute wahrscheinlich zunächst keine Threads verwenden würden. Sie würden GCDsDispatchQueue
oderNSOperationQueue
's verwenden und diese beiden verwalten einen Autorelease-Pool der obersten Ebene für Sie, der vor dem Ausführen eines Blocks / einer Aufgabe erstellt und nach Abschluss zerstört wurde.quelle
Es scheint viel Verwirrung zu diesem Thema zu geben (und mindestens 80 Leute, die jetzt wahrscheinlich verwirrt sind und denken, dass sie @autoreleasepool um ihren Code streuen müssen).
Wenn ein Projekt (einschließlich seiner Abhängigkeiten) ausschließlich ARC verwendet, muss @autoreleasepool niemals verwendet werden und wird nichts Nützliches tun. ARC behandelt das Freigeben von Objekten zum richtigen Zeitpunkt. Beispielsweise:
zeigt an:
Jedes Testobjekt wird freigegeben, sobald der Wert den Gültigkeitsbereich verlässt, ohne darauf zu warten, dass ein Autorelease-Pool beendet wird. (Dasselbe passiert mit dem NSNumber-Beispiel. Dadurch können wir nur den Dealloc beobachten.) ARC verwendet keine Autorelease.
Der Grund, warum @autoreleasepool weiterhin zulässig ist, sind gemischte ARC- und Nicht-ARC-Projekte, die noch nicht vollständig auf ARC umgestellt wurden.
Wenn Sie Nicht-ARC-Code aufrufen, wird möglicherweise ein automatisch freigegebenes Objekt zurückgegeben. In diesem Fall würde die obige Schleife lecken, da der aktuelle Autorelease-Pool niemals verlassen wird. Dort möchten Sie einen @autoreleasepool um den Codeblock legen.
Wenn Sie den ARC-Übergang vollständig abgeschlossen haben, vergessen Sie den Autoreleasepool.
quelle
+ (Testing *) testing { return [Testing new] }
. Dann werden Sie sehen, dass der Dealloc erst später aufgerufen wird. Dies wird behoben, wenn Sie das Innere der Schleife in einen@autoreleasepool
Block einschließen.+ (Testing *) testing { return [Testing new];} + (void) test { while(true) NSLog(@"p = %p", [self testing]);}
[UIImage imageWithData]
in die Gleichung einmischte, bemerkte ich plötzlich das traditionelleautorelease
Verhalten und musste das@autoreleasepool
maximale Gedächtnis auf einem vernünftigen Niveau halten.