Kiedy powinienem zwolnić obiekty w - (void) viewDidUnload zamiast w -dealloc?

103

Do czego służy -(void)viewDidUnload?

Czy nie mógłbym po prostu zwolnić wszystkiego -dealloc? Gdyby widok się rozładował, i tak nie zostałby -deallocwywołany?

Dzięki
źródło

Odpowiedzi:

51

Oprócz tego, co już zostało wskazane, chciałem bardziej szczegółowo omówić logikę stojącą za -viewDidUnload.

Jednym z najważniejszych powodów, dla których warto ją wdrożyć, jest to, że UIViewControllerpodklasy zwykle zawierają również odniesienia do różnych podwidoków w hierarchii widoków. Te właściwości mogły zostać ustawione IBOutletspodczas ładowania z końcówki lub -loadViewna przykład programowo wewnątrz .

Dodatkowa własność widoków podrzędnych polega na tym UIViewController, że nawet jeśli jego widok zostanie usunięty z hierarchii widoków i zwolniony w celu oszczędzania pamięci, przez który podglądy podrzędne są również zwalniane przez widok, w rzeczywistości nie zostaną one cofnięte, ponieważ UIViewControllersam nadal zawiera własne zaległe zachowując również odniesienia do tych obiektów. Zwolnienie UIViewControllerdodatkowej własności tych obiektów gwarantuje, że zostaną one również zwolnione w celu zwolnienia pamięci.

Obiekty, które tutaj zwalniasz, są zwykle odtwarzane i ustawiane ponownie, gdy UIViewControllerwidok pochodzi re-loadedze stalówki lub za pomocą implementacji -loadView.

Należy również pamiętać, że UIViewController viewwłaściwość jest nilw momencie wywołania tej metody.

Sean Patrick Murphy
źródło
1
Powinieneś przeczytać developer.apple.com/library/ios/#featuredarticles/…, aby zrozumieć cykl życia kontrolera widoku / podglądu
Paul Solt
21

Jak mówi dokumentacja :

Jest wywoływana w warunkach małej ilości pamięci, kiedy kontroler widoku musi zwolnić swój widok i wszystkie obiekty skojarzone z tym widokiem, aby zwolnić pamięć.

W tej samej sytuacji deallocsię nie nazywa. Ta metoda jest dostępna tylko w systemie OS3 i nowszych. Radzenie sobie z tą samą sytuacją w iPhone OS 2.x było prawdziwym bólem!

Aktualizacja z lipca 2015 r . : należy zauważyć, że viewDidUnloadw systemie iOS 6 została wycofana, ponieważ „Widoki nie są już czyszczone w warunkach małej ilości pamięci, więc ta metoda nigdy nie jest wywoływana”. Tak więc, współczesna rada nie polega na tym, aby się tym martwić i używać dealloc.

Stephen Darlington
źródło
6
Również z dokumentacji: „Należy to zrobić tylko dla obiektów, które można łatwo odtworzyć później, w metodzie viewDidLoad lub w innych częściach aplikacji. Nie należy używać tej metody do udostępniania danych użytkownika lub innych informacji, których nie można łatwo odtworzyć ”. To jest pytanie, które też sobie zadałem, dzięki!
leolobato,
A jeśli widok jest obecnie widoczny? Czy nie byłoby źle go porzucić z powodu ostrzeżenia o małej ilości pamięci? ;) to aplikacja byłaby pusta i pusta. Nie rozumiem sensu zwalniania widoku z powodu małej ilości pamięci. Jeśli nie widzę widoku, zawsze zwalniam cały kontroler. Chociaż mam główny kontroler widoku, który pozostaje nienaruszony i zarządza całym ładowaniem / wyładowywaniem swoich podrzędnych kontrolerów widoku ...
Dziękuję
Nie, nie użyjesz tego, jeśli po prostu zamienisz jeden widok na inny. Pomyśl o przypadku, w którym masz „stos” widoków z kontrolerem UINavigationController. Widoczny jest tylko jeden widok i jeśli masz ostrzeżenie dotyczące pamięci, możesz zwolnić wszystkie niewidoczne.
Stephen Darlington
Jak kontrolujesz, że viewDidUnload nie jest wywoływana w bieżącym widocznym widoku, jak zauważyło Thanks?
arielcamus
1
viewDidUnload nie zostanie wywołane w aktualnie widocznym widoku, tylko w widokach, które nie są widoczne.
progrmr
9

Dzieje się tak, ponieważ zazwyczaj ustawiasz @propertyas "(nonatomic, retain)"i jako taki ustawiacz, który jest tworzony dla ciebie, zwalnia bieżący obiekt, a następnie zachowuje argument, tj.

self.property = nil;

... robi coś w rodzaju:

[property release];
property = [nil retain];

Dlatego zabijasz dwa ptaki jednym kamieniem: zarządzanie pamięcią (zwalnianie istniejącego obiektu) i przypisywanie wskaźnika do zera (ponieważ wysłanie jakiejkolwiek wiadomości do wskaźnika zerowego zwróci zero).

Mam nadzieję, że to pomoże.

gazzaaa87
źródło
8

Pamiętaj, że viewDidUnloadjest to metoda w kontrolerze widoku, a nie w widoku. Metoda widoku dealloc zostanie wywołana, gdy widok zostanie zwolniony, ale metoda kontrolera widoku dealloc może zostać wywołana dopiero później.

Jeśli pojawi się ostrzeżenie o małej ilości pamięci, a widok nie jest wyświetlany, co stanie się na przykład za każdym razem, gdy użyjesz UIImagePickerController, aby umożliwić użytkownikowi zrobienie zdjęcia, widok zostanie wyładowany i będzie musiał zostać ponownie załadowany.

David Maymudes
źródło
to ma sens. Co się stanie, jeśli zawsze porzucę cały kontroler widoku? To właśnie robię. W tym przypadku nie muszę się zbytnio zajmować -viewDidUnload, prawda? Nigdy nie miałem sytuacji, w której porzuciłbym tylko widok, ponieważ zawsze upuszczam cały kontroler, jeśli i tak nie jest widoczny.
Dzięki
cóż, po prostu pamiętaj, że w przypadku, gdy twój widok jest wyświetlany, ale masz widok pełnoekranowy, taki jak ImagePicker na górze, twój widok może zostać rozładowany, nawet jeśli nie planowałeś tego.
David Maymudes
6

Wniosek:

Kontrolery widoku mają właściwość widoku. Zazwyczaj końcówka lub fragment kodu dodaje inne widoki do tego widoku. Dzieje się tak często wewnątrz metody -viewDidLoad, na przykład:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self createManyViewsAndAddThemToSelfDotView];
}

ponadto plik nib może utworzyć przycisk i dołączyć go do widoku kontrolera widoku.

W systemie iPhone OS 2.2, kiedy -didReceiveMemoryWarning został wywołany z systemu, trzeba było zwolnić coś, aby zwolnić pamięć. Możesz zwolnić widok całego kontrolera widoku, jeśli to ma sens. Lub po prostu duża zawartość zajmująca pamięć.

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}

Teraz w nowym OS 3.0 jest metoda -viewDidUnload, która zostanie wywołana z systemu, gdy widok zostanie wyładowany z powodu małej ilości pamięci (proszę mnie poprawić: kiedy dokładnie to się wywoła?)

-viewDidUnload służy do zwalniania wszystkich obiektów, których właścicielem był zarówno sam kontroler widoku, jak i widok. Powód: jeśli kontroler widoku przechowuje odwołania do elementów podrzędnych widoku, tj. Przycisku, przywoływane widoki potomne nie zostaną zwolnione, ponieważ ich liczba zachowań wynosi> = 1. Po ich zwolnieniu w -viewDidUnload można je zwolnić z pamięci.

Dzięki
źródło
1
pamiętaj, że wyładuj, aby wykonać self.button = nil;, a nie [zwolnienie przycisku] ;.
mk12
6

Przestarzały widok Apple ViewWillUnload, teraz powinieneś użyć didReceiveMemoryWarning lub dealloc, aby zwolnić swoje objetcs.

W systemie iOS 6 metody viewWillUnload i viewDidUnload UIViewController są teraz przestarzałe. Jeśli używasz tych metod do udostępniania danych, użyj zamiast tego metody didReceiveMemoryWarning. Możesz również użyć tej metody, aby zwolnić odwołania do widoku kontrolera widoku, jeśli nie jest używany. Zanim to zrobisz, musisz sprawdzić, czy widoku nie ma w oknie.

Guilherme Torres Castro
źródło
5

Jeśli kontroler widoku zostanie pobrany ze stosu kontrolera nawigacji i nie zostanie zachowany nigdzie indziej, zostanie cofnięty, a dealloc zostanie wywołana zamiast viewDidUnload. Powinieneś zwolnić widoki utworzone w loadView w dealloc, ale nie jest konieczne ustawianie zmiennych na nil, ponieważ wkrótce po wywołaniu dealloc zmienne już nie będą istnieć.

Colin
źródło
3

Możesz zwolnić wszystkie podziały, które trzymasz, na przykład ten UIImageView, który zachowałeś w metodzie loadView, lub jeszcze lepiej obraz, który był w tym UIImageView.

drvdijk
źródło