Ist es notwendig, Autoreleasepool in einem Swift-Programm zu verwenden?

95

Auf Seite 17 dieser WWDC14-Präsentation heißt es

Arbeiten mit Objective-C? Autorelease-Pools müssen noch verwaltet werden Autorelease-Pool
{/ * code * /}

Was bedeutet das? Bedeutet dies, dass autoreleasepool {}es unnötig ist , wenn meine Codebasis keine Objective-C-Dateien enthält ?

In einer Antwort auf eine verwandte Frage gibt es ein Beispiel, wo autoreleasepooles nützlich sein kann:

- (void)useALoadOfNumbers {
    for (int j = 0; j < 10000; ++j) {
        @autoreleasepool {
            for (int i = 0; i < 10000; ++i) {
                NSNumber *number = [NSNumber numberWithInt:(i+j)];
                NSLog(@"number = %p", number);
            }
        }
    }
}

Wenn der obige Code mit autoreleasepoolgelöscht in Swift übersetzt wird, ist Swift dann klug genug zu wissen, dass die numberVariable nach dem ersten freigegeben werden sollte }(wie es einige andere Sprachen tun)?

Ethan
quelle
1
autoreleasepoolIn Swift scheint es keine Dokumentation zu geben . Ich habe Ihre Frage erweitert und in den Entwicklerforen gestellt .
Aaron Brager

Antworten:

197

Das autoreleasepoolMuster wird in Swift verwendet, wenn autoreleaseObjekte zurückgegeben werden (entweder von Ihrem Objective-C-Code oder mithilfe von Cocoa-Klassen erstellt). Das autoreleaseMuster in Swift funktioniert ähnlich wie in Objective-C. Betrachten Sie zum Beispiel diese schnelle Wiedergabe Ihrer Methode (Instanziieren NSImage/ UIImageObjekte):

func useManyImages() {
    let filename = pathForResourceInBundle

    for _ in 0 ..< 5 {
        autoreleasepool {
            for _ in 0 ..< 1000 {
                let image = NSImage(contentsOfFile: filename)
            }
        }
    }
}

Wenn Sie dies in Instruments ausführen, wird ein Zuordnungsdiagramm wie das folgende angezeigt:

mit Autoreleasepool

Wenn Sie dies jedoch ohne den Autorelease-Pool tun, werden Sie feststellen, dass die maximale Speichernutzung höher ist:

ohne Autoreleasepool

Mit dieser autoreleasepoolOption können Sie explizit verwalten, wann Autorelease-Objekte in Swift freigegeben werden, genau wie dies in Objective-C möglich war.

Hinweis: Wenn Sie mit nativen Swift-Objekten arbeiten, erhalten Sie im Allgemeinen keine Autorelease-Objekte. Aus diesem Grund wurde in der Präsentation die Einschränkung erwähnt, dass dies nur bei der "Arbeit mit Objective-C" erforderlich ist, obwohl ich mir wünschte, Apple wäre in diesem Punkt klarer. Wenn Sie jedoch mit Objective-C-Objekten (einschließlich Cocoa-Klassen) arbeiten, handelt es sich möglicherweise um Autorelease-Objekte. In diesem Fall ist diese schnelle Wiedergabe des Objective-C- @autoreleasepoolMusters weiterhin nützlich.

rauben
quelle
2
Bei all diesen Fragen können Sie Ihre eigene Klasse schreiben und ein printlnIn deinitausführen lassen, und es wird ziemlich einfach, genau zu überprüfen, wann Objekte freigegeben werden. Oder beobachten Sie es in Instrumenten. Bei der Beantwortung Ihrer Frage werden anscheinend Swift-Objekte von Funktionen mit +1 Anzahl beibehalten (keine Autorelease-Objekte) zurückgegeben, und der Aufrufer verwaltet den Besitz ab diesem Zeitpunkt nahtlos (z. B. wenn das zurückgegebene Objekt außerhalb des Gültigkeitsbereichs liegt). Es wird sofort freigegeben und nicht in einen Autorelease-Pool gestellt.
Rob
3
@StevenHernandez Ein Autorelease-Pool hat sehr wenig mit Lecks zu tun. Ein Leck wird durch ein nicht freigegebenes Objekt verursacht. Der Autorelease-Pool ist dagegen nur eine Sammlung von Objekten, für die die Freigabe verschoben wird, bis der Pool geleert ist. Pools steuern nicht, ob etwas freigegeben wird oder nicht, sondern lediglich den Zeitpunkt einer solchen Freigabe. In Bezug auf die Kartenansicht können Sie weder steuern, was das Caching bewirkt (verwendet Speicher, aber kein echtes Leck), noch etwas tun, wenn es ein echtes Leck gab (und mir sind keine signifikanten Lecks in der Kartenansicht bekannt, obwohl es in der Vergangenheit solche gab zufällige, bescheidene Lecks in UIKit).
Rob
2
@matt Ja, ich habe ein ähnliches Verhalten gesehen. Also wiederholte ich meine Übung mit NSImage/ UIImageobjects und manifestierte das Problem konsistenter (und ehrlich gesagt ist dies ein häufigeres Beispiel für das Problem, da die maximale Speichernutzung oft nur bei größeren Objekten problematisch ist; ein praktisches Beispiel hierfür könnte sein eine Routine zur Größenänderung einer Reihe von Bildern). Ich habe auch das Verhalten reproduziert, das Objective-C-Code aufruft, der explizit Autorelease-Objekte erstellt hat. Versteh mich nicht falsch: Ich denke, wir brauchen in Swift seltener Autorelease-Pools als in Objective-C, aber es spielt immer noch eine Rolle.
Rob
1
Ich habe ein Beispiel gefunden, das funktioniert! Rufen Sie einfach pathForResource:ofType:wiederholt NSBundle an .
Matt
1
Mein pathForResource:ofType:Beispiel funktioniert nicht mehr in Xcode 6.3 / Swift 1.2. :)
Matt
4

Wenn Sie es im entsprechenden Objective-C-Code verwenden würden, würden Sie es in Swift verwenden.

wird Swift klug genug sein zu wissen, dass die Zahlenvariable nach dem ersten freigegeben werden sollte}

Nur wenn Objective-C dies tut. Beide arbeiten nach den Regeln für die Speicherverwaltung von Cocoa.

Natürlich weiß ARC, dass dies numberam Ende dieser Iteration der Schleife nicht mehr möglich ist, und wenn es beibehalten wird, wird es dort freigegeben. Dies sagt Ihnen jedoch nicht, ob das Objekt automatisch freigegeben wurde, da -[NSNumber numberWithInt:] möglicherweise eine automatisch freigegebene Instanz zurückgegeben wurde oder nicht . Sie können es auf keinen Fall wissen, da Sie keinen Zugriff auf die Quelle von haben -[NSNumber numberWithInt:].

newacct
quelle
1
Wenn sich Swift dafür genauso verhält wie Objective-C, warum wurde in der Präsentation "Arbeiten mit Objective-C" erwähnt? speziell?
Ethan
9
@Ethan Es scheint, dass native Swift-Objekte keine Autorelease-Objekte sind und das autoreleasepoolKonstrukt völlig unnötig ist. Wenn Ihr Swift-Code jedoch Objective-C-Objekte (einschließlich Cocoa-Objekte) verarbeitet, folgen diese Autorelease-Mustern, sodass das autoreleasepoolKonstrukt nützlich wird.
Rob
Ich erhalte die Meldung "Mit dem Autorelease-Pool können Sie explizit verwalten, wann Autorelease-Objekte in Swift freigegeben werden", aber warum sollte ich das tun wollen? Warum macht / kann der Compiler das nicht für mich? Ich musste meinen eigenen Autoreleasepool hinzufügen, um zu verhindern, dass VM in einer engen Schleife großer String-Manipulationen durch das Dach geht. Mir war klar, wo es hinzugefügt werden sollte, und es funktionierte perfekt. Warum konnte der Compiler das nicht? Könnte der Compiler schlauer gemacht werden, um gute Arbeit zu leisten?
vonlost