Zrozumienie liczenia referencji za pomocą Cocoa i Objective-C

[5]}właśnie zaczynam patrzeć na Objective-C i Cocoa z myślą o zabawie z iPhone SDK. Jestem w miarę zadowolony z koncepcji C malloc i free, ale schemat liczenia referencji Cocoa mnie raczej zdezorientował. Powiedziano mi, że jest bardzo elegancki, gdy to zrozumiesz, ale jeszcze nie skończyłem z garb.

Jak release, retain i autorelease Pracy i jakie są konwencje dotyczące ich wykorzystania?

(lub nie, co przeczytałeś, co pomogło ci go zdobyć?)

Author: Peter Mortensen, 2008-08-09

14 answers

Zacznijmy od retain i release; autorelease to naprawdę tylko szczególny przypadek, gdy zrozumiesz podstawowe pojęcia.

W Cocoa, każdy obiekt śledzi, ile razy jest odwoływany (konkretnie, klasa bazowa NSObject implementuje to). Wywołując retain na obiekcie, mówisz mu, że chcesz zwiększyć jego liczbę referencji o jeden. Wywołując release, mówisz obiektowi, że go puszczasz, a jego liczba referencji jest zmniejszona. Jeśli po wywołaniu release, liczba referencji wynosi teraz zero, wtedy pamięć tego obiektu jest zwalniana przez system.

Podstawowy sposób różni się od malloc i free jest taki, że dany obiekt nie musi martwić się o Inne części systemu, ponieważ zwolniono pamięć, której używali. Zakładając, że wszyscy bawią się i zachowują/wypuszczają zgodnie z zasadami, gdy jeden fragment kodu zachowuje, a następnie zwalnia obiekt, każdy inny fragment kodu odwołujący się do obiektu będzie / align = "left" /

To, co czasami może być mylące, to znajomość 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 (jeśli na przykład jest to zmienna członkowska w klasie), to muszę się upewnić, że liczba referencji obiektu wie o mnie. Jak opisano powyżej, Liczba referencji obiektu jest zwiększana przez wywołanie retain. Przez Konwencję, jest również inkrementowany (ustawiony na 1, naprawdę), gdy obiekt zostanie wytworzony przy pomocy metody "init". W każdym z tych przypadków moim obowiązkiem jest zadzwonić release do obiektu, kiedy z nim skończę. Jeśli tego nie zrobię, będzie 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 dla autorelease. Autorelease jest używany jako wygodny (a czasami konieczny) sposób, aby powiedzieć systemowi, aby zwolnił ten obiekt po pewnym czasie. Z punktu widzenia hydrauliki, gdy wywołane jest autorelease, bieżący wątek NSAutoreleasePool jest powiadamiany o wywołaniu. NSAutoreleasePool wie teraz, że gdy otrzyma możliwość (po bieżącej iteracji pętli zdarzeń), może wywołać release na obiekcie. Z naszego punktu widzenia jako programistów, zajmuje się wywołaniem release dla nas, więc nie musimy (a w rzeczywistości nie powinniśmy).

Ważne jest, aby pamiętać, że (ponownie, zgodnie z konwencją) wszystkie metody tworzenia obiektów class zwracają obiekt z autorelementem. Na przykład w poniższym przykładzie zmienna " s " ma liczbę odniesień równą 1, ale po zakończeniu pętli zdarzenia zostanie ona zniszczona.

NSString* s = [NSString stringWithString:@"Hello World"];

Jeśli chcesz trzymać się tego łańcucha, musisz wywołać retain jawnie, a następnie jawnie release po zakończeniu.

Rozważ następujący (bardzo wymyślny) bit kodu, a zobaczysz sytuację, w której autorelease jest wymagany:

- (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 trochę mylące - w pewnym momencie jednak kliknie. Oto kilka odniesień, które pomogą Ci zacząć:

  • Apple ' s wprowadzenie do zarządzania pamięcią.
  • Cocoa Programming for Mac OS X (4th Edition) , autorstwa Aarona Hillegasa - bardzo dobrze napisana książka z mnóstwem świetnych przykładów. Czyta się jak samouczek.
  • Jeśli naprawdę nurkujesz, możesz udać się na wielkie Ranczo nerdów. Jest to ośrodek szkoleniowy prowadzony przez Aarona Hillegasa-autora wspomnianej książki. Uczestniczyłem tam kilka lat temu w kursie Intro to Cocoa i był to świetny sposób na ucz się.
 148
Author: Matt Dillard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-30 20:41:22

Jeśli rozumiesz proces zachowywania / uwalniania, to istnieją dwie złote zasady, które są" duh " oczywiste dla uznanych programistów Cocoa, ale niestety rzadko są pisane wyraźnie dla początkujących.

  1. Jeśli funkcja zwracająca obiekt ma alloc, create albo copy w jego nazwie wtedy obiekt jest Twój. Musisz zadzwonić [object release] Kiedy skończysz z tym. Lub CFRelease(object), jeśli jest to obiekt Core-Foundation.

  2. Jeśli nie ma jednego z tych słów w swoim nazwa następnie 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ś również przestrzegał tej konwencji w funkcjach, które sam tworzysz.

(Nitpickers: tak, jest niestety kilka wywołań API, które są wyjątkami od tych zasad, ale są one rzadkie).

 10
Author: Andrew Grant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-19 03:41:31

Jeśli piszesz kod na pulpit i możesz kierować na Mac OS X 10.5, powinieneś przynajmniej rozważyć użycie Objective - C garbage collection. 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, aby działał dobrze.

Co do zasad zarządzania pamięcią, gdy nie używasz GC:

  • jeśli utworzysz nowy obiekt za pomocą +alloc/+allocWithZone:, +new, -copy lub -mutableCopy lub jeśli -retain obiekt, bierzesz własność i musi zapewnić jej wysłanie -release.
  • Jeśli otrzymujesz obiekt w jakikolwiek inny sposób, jesteś Nie jego właścicielem i powinieneś Nie upewnić się, że został wysłany -release.
  • jeśli chcesz mieć pewność, że obiekt jest wysyłany -release możesz wysłać go samodzielnie lub możesz wysłać obiekt -autorelease i bieżąca pula autorelease wyśle go-release (raz na odebrany -autorelease), gdy pula zostanie opróżniona.

Zazwyczaj -autorelease jest używany jako sposób upewnij się, że obiekty są żywe przez cały czas trwania bieżącego zdarzenia, ale są oczyszczane po jego zakończeniu, ponieważ istnieje Pula autoodtwarzania, która otacza przetwarzanie zdarzeń przez Cocoa. W Cocoa bardziej powszechne jest zwracanie obiektów do wywołującego, które są autoreleasowane, niż zwracanie obiektów, które sam wywołujący musi zwolnić.

 8
Author: Chris Hanson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-09 22:27:12

Objective-C używa zliczania referencji , co oznacza, że każdy obiekt ma liczbę referencji. Gdy obiekt jest tworzony, jego liczba referencji wynosi "1". Po prostu mówiąc, gdy obiekt jest odwołany (tj. przechowywany gdzieś), zostaje "zachowany", co oznacza, że jego liczba referencji jest zwiększona o jeden. Gdy obiekt nie jest już potrzebny, jest "zwolniony", co oznacza, że jego liczba referencji jest zmniejszana o jeden.

Gdy liczba referencji obiektu wynosi 0, obiekt jest zwalniany. To jest podstawowe liczenie referencji.

W niektórych językach odniesienia są automatycznie zwiększane i zmniejszane, ale objective-c nie jest jednym z tych języków. W ten sposób programista jest odpowiedzialny za zatrzymywanie i zwalnianie.

Typowy sposób zapisu metody to:

id myVar = [someObject someMessage];
.... do something ....;
[myVar release];
return someValue;
Problem konieczności pamiętania o uwolnieniu wszelkich nabytych zasobów w kodzie jest zarówno żmudny, jak i podatny na błędy. Objective-C wprowadza inną koncepcję, która ma na celu ułatwienie tego: Pule Autorelease. Pule autoelease to specjalne obiekty, które są instalowane w każdym wątku. Są to dość proste klasy, jeśli spojrzeć na NSAutoreleasePool.

Gdy obiekt otrzyma wiadomość "autorelease", obiekt będzie szukał wszystkich pul autorelease znajdujących się na stosie dla bieżącego wątku. Doda obiekt do listy jako obiekt, do którego w pewnym momencie w przyszłości zostanie wysłany komunikat "release", czyli zazwyczaj po zwolnieniu puli.

Biorąc powyższy kod, ty można go przepisać, aby był krótszy i łatwiejszy do odczytania, mówiąc:

id myVar = [[someObject someMessage] autorelease];
... do something ...;
return someValue;

Ponieważ obiekt jest autorelowany, nie musimy już jawnie wywoływać na nim "release". Dzieje się tak, ponieważ wiemy, że jakaś Pula autorelease zrobi to za nas później.

Mam nadzieję, że to pomoże. Artykuł w Wikipedii jest całkiem dobry o liczeniu referencji. Więcej informacji o autorelease pools można znaleźć tutaj . Należy również pamiętać, że jeśli budujesz dla Mac OS X 10.5 i nowszych, można powiedzieć Xcode budować z włączonym garbage collection, co pozwala całkowicie zignorować zachowaj/zwolnij / autorelease.
 6
Author: NilObject,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-09 03:48:10

Joshua (#6591) - rzeczy do zbierania śmieci w Mac OS X 10.5 wydaje się całkiem fajne, ale nie jest dostępny dla iPhone (lub jeśli chcesz, aby Twoja aplikacja działała na wersji przed 10.5 Mac OS X).

Ponadto, jeśli piszesz bibliotekę lub coś, co może być ponownie użyte, użycie trybu GC blokuje każdego, kto używa kodu, również używając trybu GC, więc jak rozumiem, każdy, kto próbuje napisać kod wielokrotnego użytku, ma tendencję do ręcznego zarządzania pamięcią.

 6
Author: Matt Sheppard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-09 03:59:35

Jak zawsze, kiedy ludzie zaczynają próbować przeformułować materiał odniesienia, prawie zawsze coś źle rozumieją lub podają niekompletny opis.

Apple dostarcza pełny opis systemu zarządzania pamięcią Cocoa w Memory Management Programming Guide for Cocoa, na końcu którego znajduje się krótkie, ale dokładne podsumowanie reguł zarządzania pamięcią.
 6
Author: mmalc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-20 00:42:12

Nie będę dodawać do specyfiki zachowania / wydania innego niż może chcesz pomyśleć o zrzuceniu $50 i uzyskaniu Książki Hillegass, ale zdecydowanie sugerowałbym korzystanie z narzędzi Instruments na bardzo wczesnym etapie rozwoju aplikacji (nawet pierwszej!). Aby to zrobić, uruchom- > Start with performance tools. Zacznę od przecieków, który jest tylko jednym z wielu dostępnych instrumentów, ale pomoże Ci pokazać, gdy zapomnisz o wydaniu. To przestaje zniechęcać ile informacje, które zostaną ci przedstawione. Ale sprawdź ten samouczek, aby wstać i działać szybko:
COCOA TUTORIAL: naprawianie wycieków pamięci za pomocą instrumentów

Właściwie próba wymuszenia przecieków może być lepszym sposobem na nauczenie się, jak im zapobiegać! Powodzenia;)

 6
Author: Rob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-27 13:24:21

Matt Dillard napisał :

Return [[s autorelease] release];

Autorelease nie zachowuje obiektu. Autorelease po prostu umieszcza go w kolejce do wydania później. Nie chcesz mieć tam Oświadczenia o zwolnieniu.

 5
Author: NilObject,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:32:05

Mój zwykły zbiór artykułów o zarządzaniu pamięcią Cocoa:

Cocoa memory management

 5
Author: NANNAV,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-18 11:32:49

Jest darmowy screencast dostępny z sieci iDeveloperTV

Zarządzanie pamięcią w Objective-C

 4
Author: Abizern,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-17 06:54:52

Odpowiedź NilObject to dobry początek. Oto kilka dodatkowych informacji dotyczących ręcznego zarządzania pamięcią (wymagane na iPhonie ).

Jeśli osobiście alloc/init obiekt, ma liczbę odniesień równą 1. Jesteś odpowiedzialny za sprzątanie po nim, gdy nie jest już potrzebny, dzwoniąc [foo release] lub [foo autorelease]. release usuwa obiekt od razu, podczas gdy autorelease dodaje obiekt do puli autorelease, która automatycznie go zwolni w późniejszym czasie.

Autorelease jest przede wszystkim wtedy, gdy masz metodę, która musi zwrócić dany obiekt (, więc nie możesz go ręcznie zwolnić, w przeciwnym razie zwrócisz zerowy obiekt ), ale nie chcesz go trzymać.

Jeśli uzyskasz obiekt, w którym nie wywołałeś alloc/INIT, aby go uzyskać, na przykład:

foo = [NSString stringWithString:@"hello"];

Ale chcesz trzymać się tego obiektu, Musisz zadzwonić [foo]. W przeciwnym razie możliwe, że dostanie autoreleased i będziesz trzymał się nil reference (jak w powyższym przykładzie stringWithString ). Gdy już go nie potrzebujesz, zadzwoń [foo release].

 4
Author: Mike McMaster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-25 21:13:23

Powyższe odpowiedzi dają jasne powtórzenia tego, co mówi dokumentacja; problem, na który napotyka większość nowych ludzi, to nieudokumentowane przypadki. Na przykład:

  • Autorelease: dokumenty mówią, ż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 zamkniesz kodu z powrotem w pętli zdarzeń systemowych. System może zwolnić obiekt w dowolnym momencie po bieżącym cyklu zdarzeń. (Myślę, że Matt powiedział, że, wcześniej.)

  • Struny statyczne: NSString *foo = @"bar"; -- Musisz to zatrzymać lub zwolnić? Nie. A może

    -(void)getBar {
        return @"bar";
    }
    

    ...

    NSString *foo = [self getBar]; // still no need to retain or release
    
  • Reguła tworzenia : jeśli go utworzyłeś, jesteś właścicielem i oczekujesz, że go wypuścisz.

Ogólnie rzecz biorąc, nowym programistom Cocoa nie udaje się zrozumieć, które procedury zwracają obiekt z retainCount > 0.

Oto fragment z bardzo prostych zasad zarządzania pamięcią W Kakao :

Zasady liczby retencji

  • w obrębie danego bloku użycie-copy, - alloc i-retain powinno być równe użyciu-release i-autorelease.
  • obiekty utworzone przy użyciu wygodnych konstruktorów (np. NSString ' s stringWithString) są uważane za autoreleased.
  • zaimplementuj metodę a-dealloc, aby zwolnić instancevariables, które posiadasz

Pierwszy pocisk mówi: jeśli zadzwoniłeś alloc (lub new fooCopy), potrzebujesz aby wywołać release na tym obiekcie.

Drugi bullet mówi: Jeśli używasz wygodnego konstruktora i potrzebujesz, aby obiekt wisiał wokół (jak w przypadku obrazu, który zostanie narysowany później), musisz go zachować (a następnie zwolnić).

Trzeci powinien być oczywisty.

 2
Author: Olie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-01 17:01:06

Dużo dobrych informacji o cocoadev też:

 1
Author: charles,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-15 20:46:23

Jak już kilka osób wspomniało, Intro Apple do zarządzania pamięcią jest zdecydowanie najlepszym miejscem do rozpoczęcia.

Jeden przydatny link, o którym jeszcze nie wspomniałem, to Praktyczne zarządzanie pamięcią . Znajdziesz go w środku dokumentów Apple, jeśli je przeczytasz, ale warto połączyć bezpośrednio. Jest to genialne podsumowanie zasad zarządzania pamięcią z przykładami i typowymi błędami (w zasadzie co inne odpowiedzi tutaj próbują wyjaśnić, ale nie jako cóż).

 0
Author: Brian Moeskau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-06 03:01:11