Zacznijmy od retain
i release
; autorelease
to naprawdę wyjątkowy przypadek, gdy zrozumiesz podstawowe pojęcia.
W kakao każdy obiekt śledzi, ile razy jest przywoływany (w szczególności NSObject
klasa bazowa implementuje to). Wzywając retain
obiekt, mówisz mu, że chcesz zwiększyć jego liczbę odwołań o jeden. Dzwoniąc release
, mówisz obiektowi, że go puszczasz, a jego liczba referencyjna jest zmniejszana. Jeśli po wywołaniu release
liczba odwołań wynosi teraz zero, to pamięć tego obiektu jest zwalniana przez system.
Podstawowy sposób, w jaki to różni się od malloc
i free
polega na tym, że dany obiekt nie musi martwić się o awarię innych części systemu, ponieważ zwolniłeś pamięć, której używał. Zakładając, że wszyscy bawią się i zachowują / zwalniają zgodnie z regułami, gdy jeden fragment kodu zachowuje, a następnie zwalnia obiekt, żaden inny fragment kodu również odwołujący się do obiektu pozostanie niezmieniony.
Czasami mylące może być poznanie okoliczności, w których należy zadzwonić retain
i release
. Moja ogólna zasada jest taka, że jeśli chcę trzymać się obiektu przez jakiś czas (na przykład jeśli jest to zmienna składowa w klasie), muszę się upewnić, że liczba odwołań do obiektu wie o mnie. Jak opisano powyżej, liczba odwołań do obiektu jest zwiększana przez wywołanie retain
. Zgodnie z konwencją, jest on również zwiększany (naprawdę ustawiony na 1), gdy obiekt jest tworzony metodą „init”. W każdym z tych przypadków moim obowiązkiem jest wywołanie release
obiektu, kiedy z nim skończę. Jeśli tego nie zrobię, nastąpi wyciek pamięci.
Przykład tworzenia obiektu:
NSString* s = [[NSString alloc] init]; // Ref count is 1
[s retain]; // Ref count is 2 - silly
// to do this after init
[s release]; // Ref count is back to 1
[s release]; // Ref count is 0, object is freed
Teraz autorelease
. Autorelease służy jako wygodny (i czasami konieczny) sposób nakazania systemowi zwolnienia tego obiektu po chwili. Z punktu widzenia hydrauliki, gdy autorelease
jest wywoływany, bieżący wątek NSAutoreleasePool
jest powiadamiany o wywołaniu. NSAutoreleasePool
Teraz wie, że po to ma możliwość (po bieżącej iteracji pętli zdarzenia), to może zadzwonić release
na obiekcie. Z naszego punktu widzenia jako programistów dba o to, release
aby nas wzywać , więc nie musimy (a właściwie nie powinniśmy).
Ważne jest, aby pamiętać, że (ponownie zgodnie z konwencją) wszystkie metody klas tworzenia obiektów zwracają obiekt z autouzyskiwania. Na przykład w poniższym przykładzie zmienna „s” ma liczbę odwołań równą 1, ale po zakończeniu pętli zdarzenia zostanie zniszczona.
NSString* s = [NSString stringWithString:@"Hello World"];
Jeśli chcesz zawiesić się na tym łańcuchu, musisz wywołać go retain
jawnie, a następnie jawnie release
, gdy skończysz.
Rozważ następujący (bardzo wymyślny) fragment kodu, a zobaczysz sytuację, w której autorelease
jest to wymagane:
- (NSString*)createHelloWorldString
{
NSString* s = [[NSString alloc] initWithString:@"Hello World"];
// Now what? We want to return s, but we've upped its reference count.
// The caller shouldn't be responsible for releasing it, since we're the
// ones that created it. If we call release, however, the reference
// count will hit zero and bad memory will be returned to the caller.
// The answer is to call autorelease before returning the string. By
// explicitly calling autorelease, we pass the responsibility for
// releasing the string on to the thread's NSAutoreleasePool, which will
// happen at some later time. The consequence is that the returned string
// will still be valid for the caller of this function.
return [s autorelease];
}
Zdaję sobie sprawę, że to wszystko jest nieco zagmatwane - w pewnym momencie jednak kliknie. Oto kilka odniesień, które pomogą Ci zacząć:
- Wprowadzenie firmy Apple do zarządzania pamięcią.
- Cocoa Programming for Mac OS X (4th Edition) , autorstwa Aarona Hillegasa - bardzo dobrze napisana książka z wieloma świetnymi przykładami. Brzmi jak samouczek.
- Jeśli naprawdę nurkujesz, możesz udać się na Big Nerd Ranch . To ośrodek szkoleniowy prowadzony przez Aarona Hillegasa - autora wspomnianej książki. Byłem tam kilka lat temu na kursie Intro to Cocoa i był to świetny sposób na naukę.
NSString* s = [[NSString alloc] initWithString:@"Hello World"];
zwraca obiekt z autouzupełniania (tak jak go piszesz), dlaczego muszę zrobić areturn [s autorelease];
i ustawić go ponownie na „autorelease”, a nie tylkoreturn s
?[[NSString alloc] initWithString:@"Hello World"]
NIE zwróci automatycznie udostępnianego obiektu. Za każdym razem, gdyalloc
jest wywoływana, liczba odwołań jest ustawiana na 1 i to kod jest odpowiedzialny za upewnienie się, że zostanie wydany.[NSString stringWithString:]
Wezwanie, z drugiej strony, nie zwracają autoreleased obiekt.Jeśli rozumiesz proces zatrzymania / zwolnienia, istnieją dwie złote zasady, które są oczywiste dla doświadczonych programistów Cocoa, ale niestety rzadko są jasno określone dla nowicjuszy.
Jeśli funkcja zwracająca obiekt ma
alloc
,create
lubcopy
w jego imieniu, a następnie obiekt jest twoje. Musisz zadzwonić,[object release]
kiedy skończysz. LubCFRelease(object)
, jeśli jest to obiekt Core-Foundation.Jeśli NIE ma w nazwie żadnego z tych słów, to obiekt należy do kogoś innego. Musisz wywołać,
[object retain]
jeśli chcesz zachować obiekt po zakończeniu funkcji.Dobrze by było, gdybyś przestrzegał tej konwencji w funkcjach, które sam tworzysz.
(Nitpickers: Tak, niestety istnieje kilka wywołań API, które są wyjątkami od tych reguł, ale są one rzadkie).
źródło
Jeśli piszesz kod dla pulpitu i możesz kierować reklamy na Mac OS X 10.5, powinieneś przynajmniej przyjrzeć się używaniu czyszczenia pamięci Objective-C. To naprawdę uprości większość twojego rozwoju - dlatego Apple włożyło cały wysiłek w stworzenie go w pierwszej kolejności i sprawienie, by działał dobrze.
Jeśli chodzi o zasady zarządzania pamięcią, gdy nie używasz GC:
+alloc/+allocWithZone:
,+new
,-copy
lub-mutableCopy
lub jeśli-retain
obiekt, pacjent przyjmuje go na własność i należy upewnić się, że jest wysyłany-release
.-release
.-release
, możesz wysłać go samodzielnie lub możesz wysłać obiekt,-autorelease
a bieżąca pula autorelease wyśle go-release
(raz na odebranie-autorelease
), gdy pula zostanie opróżniona.Zwykle
-autorelease
jest używany jako sposób na zapewnienie, że obiekty będą żyły przez czas trwania bieżącego zdarzenia, ale są później czyszczone, ponieważ istnieje pula automatycznego uwalniania, która otacza przetwarzanie zdarzeń Cocoa. W kakao o wiele bardziej powszechne jest zwracanie obiektów wywołującego, które są automatycznie zwalniane, niż zwracanie obiektów, które sam wywołujący musi zwolnić.źródło
Objective-C korzysta z liczenia referencji , co oznacza, że każdy obiekt ma liczbę odwołań. Utworzony obiekt ma liczbę odwołań równą „1”. Mówiąc najprościej, gdy odwołuje się do obiektu (tj. Jest gdzieś przechowywany), zostaje on „zachowany”, co oznacza, że jego liczba odwołań jest zwiększona o jeden. Gdy obiekt nie jest już potrzebny, jest „zwalniany”, co oznacza, że jego liczba referencyjna jest zmniejszana o jeden.
Gdy liczba odwołań do obiektu wynosi 0, obiekt zostaje zwolniony. To jest podstawowe liczenie referencji.
W przypadku niektórych języków referencje są automatycznie zwiększane i zmniejszane, ale cel-c nie jest jednym z tych języków. Dlatego programista jest odpowiedzialny za przechowywanie i zwalnianie.
Typowy sposób pisania metody to:
Problem konieczności pamiętania o zwolnieniu wszelkich pozyskanych zasobów w kodzie jest zarówno żmudny, jak i podatny na błędy. Objective-C wprowadza kolejną koncepcję, która ma to znacznie ułatwić: pule Autorelease. Pule autorelease to specjalne obiekty instalowane w każdym wątku. Są dość prostą klasą, jeśli spojrzysz na NSAutoreleasePool.
Gdy do obiektu zostanie wysłana wiadomość „autorelease”, obiekt będzie szukał puli autorelease na stosie dla bieżącego wątku. Doda obiekt do listy jako obiekt, do którego ma zostać wysłany komunikat „o zwolnieniu” w pewnym momencie w przyszłości, czyli zwykle wtedy, gdy pula jest zwalniana.
Biorąc powyższy kod, możesz przepisać go na krótszy i łatwiejszy do odczytania, mówiąc:
Ponieważ obiekt jest zwolniony automatycznie, nie musimy już jawnie wywoływać na nim „wydania”. Dzieje się tak, ponieważ wiemy, że część automatycznej puli wydań zrobi to za nas później.
Mam nadzieję, że to pomoże. Artykuł w Wikipedii jest całkiem dobry na temat liczenia referencji. Więcej informacji o pulach autowyrejestrowań można znaleźć tutaj . Pamiętaj również, że jeśli tworzysz dla systemu Mac OS X 10.5 i nowszych, możesz powiedzieć Xcode, aby kompilował z włączoną funkcją czyszczenia pamięci, co pozwala całkowicie zignorować zachowanie / zwolnienie / autorelease.
źródło
Joshua (# 6591) - Rzeczy do zbierania śmieci w systemie Mac OS X 10.5 wydają się całkiem fajne, ale nie są dostępne dla iPhone'a (lub jeśli chcesz, aby Twoja aplikacja działała na wersjach Mac OS X wcześniejszych niż 10.5).
Ponadto, jeśli piszesz bibliotekę lub coś, co może być ponownie użyte, korzystanie z trybu GC blokuje każdego używającego kodu również w trybie GC, więc jak rozumiem, każdy, kto próbuje napisać kod do wielokrotnego użytku, ma tendencję do zarządzania pamięć ręcznie.
źródło
Jak zawsze, kiedy ludzie zaczynają próbować przeformułować materiał referencyjny, prawie zawsze coś jest nie tak lub podają niepełny opis.
Firma Apple zapewnia pełny opis systemu zarządzania pamięcią Cocoa w Przewodniku programowania zarządzania pamięcią dla Cocoa , na końcu którego znajduje się krótkie, ale dokładne podsumowanie reguł zarządzania pamięcią .
źródło
Nie będę dodawać do szczegółów dotyczących zachowania / wydania innych niż myślisz o upuszczeniu 50 $ i zdobyciu książki Hillegass, ale zdecydowanie sugerowałbym rozpoczęcie korzystania z narzędzi Instruments na bardzo wczesnym etapie tworzenia aplikacji (nawet pierwszy!). Aby to zrobić, Uruchom-> Rozpocznij z narzędziami wydajności. Zacząłbym od Leaks, który jest tylko jednym z wielu dostępnych instrumentów, ale pomoże ci pokazać, kiedy zapomniałeś wydać. To, jak wiele informacji zostanie przedstawionych, przestaje zniechęcać. Ale zapoznaj się z tym samouczkiem, aby szybko wstać:
KAKAOWY TUTORIAL: USUWANIE WYCIEKÓW PAMIĘCI ZA POMOCĄ INSTRUMENTÓW
Właściwie próba wymuszenia wycieków może być z kolei lepszym sposobem nauczenia się, jak im zapobiegać! Powodzenia ;)
źródło
Autorelease tak nie zatrzymuje przedmiotu. Autorelease po prostu umieszcza go w kolejce do wydania później. Nie chcesz mieć tam oświadczenia o zwolnieniu.
źródło
Mój typowy zbiór artykułów o zarządzaniu pamięcią Cocoa:
zarządzanie pamięcią kakaową
źródło
Dostępny jest bezpłatny screencast z sieci iDeveloperTV
Zarządzanie pamięcią w Objective-C
źródło
Odpowiedź NilObject to dobry początek. Oto dodatkowe informacje dotyczące ręcznego zarządzania pamięcią ( wymagane na iPhonie ).
Jeśli osobiście jesteś
alloc/init
obiektem, ma on liczbę referencyjną równą 1. Jesteś odpowiedzialny za sprzątanie po nim, gdy nie jest już potrzebny, dzwoniąc[foo release]
lub[foo autorelease]
. release czyści go od razu, podczas gdy autorelease dodaje obiekt do puli autorelease, która automatycznie zwolni go w późniejszym czasie.autorelease jest przeznaczone głównie dla sytuacji, gdy masz metodę, która musi zwrócić dany obiekt ( więc nie możesz go ręcznie zwolnić, w przeciwnym razie zwrócisz obiekt zerowy ), ale nie chcesz też go zatrzymać .
Jeśli zdobędziesz obiekt, w którym nie wywołałeś funkcji assign / init, aby go pobrać - na przykład:
ale chcesz trzymać się tego obiektu, musisz wywołać [foo retain]. W przeciwnym razie możliwe, że tak się stanie
autoreleased
i będziesz trzymać się zerowego odniesienia (tak jak w powyższymstringWithString
przykładzie ). Kiedy już jej nie potrzebujesz, zadzwoń[foo release]
.źródło
Powyższe odpowiedzi zawierają jasne powtórzenia tego, co mówi dokumentacja; problem, na który natrafia większość nowych ludzi, to przypadki nieudokumentowane. Na przykład:
Autorelease : dokumenty twierdzą, że spowoduje to wydanie „w pewnym momencie w przyszłości”. KIEDY?! Zasadniczo możesz liczyć na to, że obiekt będzie w pobliżu, dopóki nie wyjdziesz z kodu z powrotem do pętli zdarzeń systemowych. System MOŻE zwolnić obiekt w dowolnym momencie po bieżącym cyklu zdarzenia. (Myślę, że Matt powiedział to wcześniej.)
Ciągi statyczne :
NSString *foo = @"bar";
- czy musisz to zachować lub zwolnić? Nie. A może...
Reguła tworzenia : jeśli ją stworzyłeś, jesteś jej właścicielem i oczekuje się, że ją opublikujesz.
Ogólnie rzecz biorąc, nowi programiści Cocoa popełniają błędy, ponieważ nie rozumieją, które procedury zwracają obiekt z rozszerzeniem
retainCount > 0
.Oto fragment z bardzo prostych reguł zarządzania pamięcią w kakao :
Punkt pierwszy mówi: jeśli zadzwoniłeś
alloc
(lubnew fooCopy
), musisz wywołać zwolnienie na tym obiekcie.Drugi punktor mówi: jeśli używasz wygodnego konstruktora i chcesz, aby obiekt kręcił się w pobliżu (jak w przypadku obrazu do narysowania później), musisz go zachować (a następnie zwolnić).
Trzecia powinna być oczywista.
źródło
Wiele dobrych informacji na temat cocoadev:
źródło
Jak już kilka osób wspomniało, Apple Intro to Memory Management jest zdecydowanie najlepszym miejscem do rozpoczęcia.
Jednym z przydatnych linków, o których jeszcze nie widziałem, jest Praktyczne zarządzanie pamięcią . Znajdziesz go w środku dokumentów Apple, jeśli je przeczytasz, ale warto to zrobić bezpośrednio. Jest to genialne podsumowanie reguł zarządzania pamięcią z przykładami i typowymi błędami (w zasadzie to, co próbują wyjaśnić inne odpowiedzi, ale nie tak dobrze).
źródło