Jakiego rodzaju przecieków automatyczne liczenie odniesień w Objective-C nie zapobiega lub minimalizuje?

235

Na platformach Mac i iOS wycieki pamięci są często spowodowane niepublikowanymi wskaźnikami. Tradycyjnie zawsze sprawą najwyższej wagi było sprawdzanie przydziałów, kopiowanie i przechowywanie, aby upewnić się, że każdy ma odpowiedni komunikat o zwolnieniu.

Zestaw narzędzi dostarczany z Xcode 4.2 wprowadza automatyczne zliczanie referencji (ARC) w najnowszej wersji kompilatora LLVM , co całkowicie rozwiązuje ten problem, umożliwiając kompilatorowi zarządzanie pamięcią za Ciebie. To całkiem fajne i skraca wiele niepotrzebnego, przyziemnego czasu programowania i zapobiega wielu nieostrożnym wyciekom pamięci, które można łatwo naprawić przy zachowaniu równowagi między zachowaniem a zwolnieniem. Nawet pule automatycznego wydawania muszą być zarządzane w różny sposób po włączeniu ARC dla aplikacji na komputery Mac i iOS (ponieważ nie należy już przydzielać własnych NSAutoreleasePool).

Ale jakie inne wycieki pamięci nie wykluczają, że wciąż muszę uważać?

Jako bonus, jakie są różnice między ARC na Mac OS X i iOS, a śmieciowaniem na Mac OS X?

BoltClock
źródło

Odpowiedzi:

262

Podstawowym problemem związanym z pamięcią, o którym wciąż musisz wiedzieć, jest zachowanie cykli. Dzieje się tak, gdy jeden obiekt ma silny wskaźnik do drugiego, ale obiekt docelowy ma silny wskaźnik z powrotem do oryginału. Nawet jeśli wszystkie inne odniesienia do tych obiektów zostaną usunięte, nadal będą się one trzymać i nie zostaną zwolnione. Może się to również zdarzyć pośrednio, przez łańcuch obiektów, który może mieć ostatni w łańcuchu odwołujący się do wcześniejszego obiektu.

Z tego powodu istnieją kwalifikatory __unsafe_unretainedi __weakwłasność. Ten pierwszy nie zatrzyma żadnego obiektu, na który wskazuje, ale pozostawia otwartą możliwość, że ten obiekt zniknie i będzie wskazywał na złą pamięć, podczas gdy drugi nie zatrzyma obiektu i automatycznie ustawi się na zero, gdy jego cel zostanie zwolniony. Z tych dwóch __weakjest ogólnie preferowany na platformach, które go obsługują.

Używałbyś tych kwalifikatorów do takich rzeczy jak delegaci, w których nie chcesz, aby obiekt zachował swoją delegację i potencjalnie doprowadził do cyklu.

Kolejną istotną kwestią związaną z pamięcią jest obsługa obiektów Core Foundation i pamięci przydzielanej przy użyciu malloc()takich typów char*. ARC nie zarządza tymi typami, tylko obiektami Objective-C, więc nadal będziesz musiał sobie z nimi poradzić. Typy Core Foundation mogą być szczególnie trudne, ponieważ czasami trzeba je połączyć w celu dopasowania do obiektów Objective-C i odwrotnie. Oznacza to, że kontrola musi być przenoszona w tę iz powrotem z ARC podczas mostkowania między typami CF i Objective-C. Dodano niektóre słowa kluczowe związane z tym mostowaniem, a Mike Ash ma świetny opis różnych przypadków mostkowania w swoim długim opisie ARC .

Oprócz tego istnieje kilka innych rzadszych, ale wciąż potencjalnie problematycznych przypadków, które szczegółowo opisano w opublikowanej specyfikacji .

Wiele z nowych zachowań, opartych na utrzymywaniu obiektów tak długo, jak długo jest do nich silny wskaźnik, jest bardzo podobne do wyrzucania elementów bezużytecznych na komputerze Mac. Podstawy techniczne są jednak bardzo różne. Ten styl zarządzania pamięcią opiera się na sztywnych zasadach zatrzymywania / zwalniania, których wszyscy musimy przestrzegać w Objective-C, zamiast procesu śmieciarza, który działa w regularnych odstępach czasu w celu czyszczenia obiektów, o których nie ma już mowy.

ARC po prostu przenosi powtarzające się zadania zarządzania pamięcią, które musieliśmy wykonywać od lat, i przenosi je do kompilatora, abyśmy nigdy więcej nie musieli się o nie martwić. W ten sposób nie występują problemy z zatrzymywaniem ani profile pamięci piłokształtnych występujące na platformach odśmiecania. Doświadczyłem obu tych rzeczy w moich śmieciowych aplikacjach Mac i chętnie sprawdzam, jak zachowują się pod ARC.

Aby uzyskać więcej informacji na temat wyrzucania elementów bezużytecznych w porównaniu do ARC, zobacz tę bardzo interesującą odpowiedź Chrisa Lattnera na liście mailingowej Objective-C , w której wymieniono wiele zalet ARC w porównaniu do odśmiecania Objective-C 2.0. Natknąłem się na kilka problemów GC, które opisuje.

Brad Larson
źródło
2
Dzięki za szczegółową odpowiedź. Miałem ten sam problem, w którym zdefiniowałem delegata w _unsafe_unretained i zawiesiłem swoją aplikację, później naprawiłem ją, zmieniając na silny, ale teraz ma przeciek pamięci. Więc zmieniłem go na słaby i działa jak urok.
chathuram
@ichathura Wow! Uratowałeś mnie od błota ARC. Ta sama awaria wystąpiła podczas korzystania z CMPopTipView.
Nianliang
@BradLarson: „nie masz problemów z zatrzymywaniem ani piłokształtnych profili pamięci spotykanych na platformach śmieciowych”. Spodziewałbym się gorszych profili pamięci zatrzymania i piłokształtnych od odzyskiwania opartego na zakresie i znacznie gorszej wydajności od liczenia referencji, więc chciałbym zobaczyć prawdziwe porównanie.
Jon Harrop
Brad, link Chrisa Lattnera nie żyje . Nie jestem w 100%, ale znalazłem ten inny link. Myślę, że to, co chciałeś link do: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/...
Honey
1
@ Kochanie - Dziękujemy za zwrócenie na to uwagi. Ten, który łączysz, jest nieco inny, ale martwy link zastąpiłem zarchiwizowaną wersją oryginalnej wiadomości. Jest w archiwach list mailowych, które powinny być gdzieś dostępne, ale sprawdzę, czy mogę znaleźć ich nową lokalizację.
Brad Larson
14

ARC nie pomoże ci z pamięcią inną niż ObjC, na przykład jeśli malloc()coś, nadal musisz tego free()zrobić.

ARC można oszukać, performSelector:jeśli kompilator nie może dowiedzieć się, co to jest selektor (kompilator wygeneruje ostrzeżenie o tym).

ARC będzie również generować kod zgodnie z konwencjami nazewnictwa ObjC, więc jeśli wymieszasz kod ARC i MRC, możesz uzyskać zaskakujące wyniki, jeśli kod MRC nie spełni oczekiwań kompilatora.

Paski
źródło
7

W mojej aplikacji wystąpiły wycieki pamięci z powodu następujących 4 problemów:

  1. Nie unieważnia NSTimerów podczas zamykania kontrolerów widoku
  2. Zapominanie o usunięciu obserwatorów do NSNotificationCenter podczas zamykania kontrolera widoku.
  3. Utrzymywanie silnych odniesień do siebie w blokach.
  4. Używanie silnych odniesień do delegatów we właściwościach kontrolera widoku

Na szczęście natknąłem się na następujący post na blogu i byłem w stanie je poprawić: http://www.reigndesign.com/blog/debugging-retain-cycles-in-objective-c-four-likely-culprits/

Ed-E G.
źródło
0

ARC nie będzie również zarządzać typami CoreFoundation. Możesz je „zmostkować” (za pomocą CFBridgingRelease()), ale tylko wtedy, gdy zamierzasz użyć go jako obiektu Objective-C / Cocoa. Zauważ, że CFBridgingRelease po prostu zmniejsza licznik zatrzymania CoreFoundation o 1 i przenosi go do ARC Celu-C.

MaddTheSane
źródło
0

Xcode 9 stanowi doskonałe narzędzie do znajdowania tego rodzaju problemów. Nazywa się to: „ Debuguj wykres pamięci ”. Używając go, możesz znaleźć wyciekający obiekt według typu klasy i możesz wyraźnie zobaczyć, kto ma do niego silne odniesienie, zwolnienie go stamtąd rozwiązuje twój problem. Wykrywa również cykle pamięci.

Zobacz więcej informacji o tym, jak go używać

WILL K.
źródło