Czy konieczne jest używanie autoreleasepool w programie Swift?

97

Na stronie 17 tej prezentacji WWDC14 jest napisane

Pracujesz z Objective-C? Nadal trzeba zarządzać pulami automatycznych
wydań autoreasepool {/ * kod * /}

Co to znaczy? Czy to oznacza, że ​​jeśli moja baza kodu nie ma żadnych plików Objective-C, autoreleasepool {}jest niepotrzebna?

W odpowiedzi na powiązane pytanie znajduje się przykład, który autoreleasepoolmoże być przydatny:

- (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);
            }
        }
    }
}

Jeśli powyższy kod zostanie przetłumaczony na Swift z autoreleasepoolupuszczeniem, czy Swift będzie wystarczająco inteligentny, aby wiedzieć, że numberzmienna powinna zostać wydana po pierwszej }(podobnie jak niektóre inne języki)?

Ethan
źródło
1
Wydaje się, że autoreleasepoolw języku Swift nie ma dokumentacji . I rozszerzony na swoje pytanie i poprosił go na forach dev .
Aaron Brager

Odpowiedzi:

199

autoreleasepoolWzór jest stosowany w Swift po powrocie autoreleaseprzedmiotów (utworzone przez którąkolwiek kod Objective-C lub za pomocą klas kakao). autoreleaseWzór funkcje Swift podobnie jak ma to miejsce w Objective-C. Na przykład rozważmy tę wersję Swift swojej metody (tworzenie instancji NSImage/ UIImageobiektów):

func useManyImages() {
    let filename = pathForResourceInBundle

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

Jeśli uruchomisz to w Instrumentach, zobaczysz wykres alokacji podobny do poniższego:

z puli autoreleasepool

Ale jeśli zrobisz to bez puli autorelease, zobaczysz, że szczytowe użycie pamięci jest wyższe:

bez puli autoreleasepool

autoreleasepoolPozwala wyraźnie zarządzać gdy autorelease obiekty są zwalniane w Swift, podobnie jak udało ci się w Objective-C.

Uwaga: w przypadku obiektów natywnych języka Swift na ogół nie otrzymujesz obiektów z automatycznym zwalnianiem. Dlatego w prezentacji wspomniano o zastrzeżeniu, że potrzeba tego tylko podczas „pracy z Objective-C”, chociaż chciałbym, aby Apple było bardziej jasne w tej kwestii. Ale jeśli masz do czynienia z obiektami Objective-C (w tym klasami Cocoa), mogą to być obiekty autorelease, w takim przypadku ta szybka interpretacja @autoreleasepoolwzorca Objective-C jest nadal przydatna.

Obrabować
źródło
2
Na wszystkie te pytania, możesz napisać własną klasę i mają to zrobić printlnin deinit, i staje się dość łatwo sprawdzić dokładnie, kiedy obiekty są zwalniane. Lub obserwuj to w Instrumentach. W odpowiedzi na twoje pytanie wydaje się, że obiekty Swift są zwracane z funkcji z liczbą zachowań +1 (nie obiektów autorelease), a wywołujący bezproblemowo zarządza własnością od tego momentu (np. Jeśli i kiedy zwrócony obiekt znajdzie się poza zakresem, jest natychmiast zwalniany, a nie umieszczany w puli automatycznego ładowania).
Rob
3
@StevenHernandez Pula autorelease ma bardzo niewiele wspólnego z wyciekami. Wyciek jest spowodowany przez niewykorzystany obiekt. Z drugiej strony, pula autowydzielania jest po prostu zbiorem obiektów, których wydanie jest odroczone do momentu opróżnienia puli. Pule nie kontrolują, czy coś jest zwolnione, czy nie, ale raczej tylko czas takiego zwolnienia. Wracając do widoku mapy, nie możesz kontrolować tego, co robi buforowanie (wykorzystuje pamięć, ale nie prawdziwy wyciek), ani nic nie robić, jeśli wystąpił prawdziwy wyciek (i nie jestem świadomy żadnych znaczących wycieków widoku mapy, chociaż historycznie przypadkowe, skromne wycieki w UIKit).
Rob
2
@matt Tak, widziałem podobne zachowanie. Powtórzyłem więc moje ćwiczenie z NSImage/ UIImageobiektami i bardziej konsekwentnie pokazałem problem (i szczerze mówiąc, jest to bardziej powszechny przykład problemu, ponieważ szczytowe użycie pamięci jest często problematyczne tylko w przypadku większych obiektów; praktycznym przykładem może być rutynowa zmiana rozmiaru grupy obrazów). Odtworzyłem również zachowanie wywołujące kod Objective-C, który jawnie tworzył obiekty autorelease. Nie zrozumcie mnie źle: myślę, że potrzebujemy puli autowydzielania w Swift rzadziej niż w Objective-C, ale nadal ma do odegrania rolę.
Rob
1
Znalazłem przykład, który działa! Wystarczy pathForResource:ofType:wielokrotnie dzwonić do NSBundle .
mat
1
Mój pathForResource:ofType:przykład nie działa już w Xcode 6.3 / Swift 1.2. :)
mat.
5

Jeśli użyjesz go w równoważnym kodzie Objective-C, użyjesz go w języku Swift.

czy Swift będzie na tyle sprytny, by wiedzieć, że zmienna liczbowa powinna zostać zwolniona po pierwszej}

Tylko jeśli robi to Objective-C. Oba działają zgodnie z regułami zarządzania pamięcią Cocoa.

Oczywiście ARC wie, że numberpod koniec tej iteracji pętli wychodzi poza zakres i jeśli ją zachowa, zwolni ją tam. Nie informuje to jednak o tym, czy obiekt został wydany automatycznie, ponieważ -[NSNumber numberWithInt:] mógł, ale nie musi, zwrócić instancję wydaną automatycznie. Nie możesz tego wiedzieć, ponieważ nie masz dostępu do źródła -[NSNumber numberWithInt:].

newacct
źródło
1
Jeśli Swift zachowuje się tak samo jak Objective-C, dlaczego w prezentacji wspomniano „Working with Objective-C”? konkretnie?
Ethan,
9
@Ethan Wydawałoby się, że natywne obiekty Swift nie są obiektami z automatycznym zwalnianiem, a autoreleasepoolkonstrukcja jest całkowicie niepotrzebna. Ale jeśli twój kod Swift obsługuje obiekty Objective-C (w tym obiekty Cocoa), te wykonują wzorce autorelease, a zatem autoreleasepoolkonstrukcja staje się użyteczna.
Rob
Rozumiem, że „pula autoreleasepool umożliwia jawne zarządzanie, kiedy obiekty autowydzielania są zwalniane w języku Swift”, ale dlaczego miałbym to robić? Dlaczego kompilator nie może / nie może tego zrobić za mnie? Musiałem dodać własną automatyczną pule, aby maszyna VM nie przechodziła przez dach w ciasnej pętli manipulacji ogromnymi strunami. Było dla mnie oczywiste, gdzie należy go dodać i zadziałało idealnie. Dlaczego kompilator nie mógł tego zrobić? Czy można uczynić kompilator mądrzejszym, aby wykonał dobrą robotę?
vonlost