Czy ustawiam właściwości na zero w dealloc, gdy używam ARC?

125

Próbuję nauczyć się automatycznego liczenia referencji w iOS 5. Teraz pierwsza część tego pytania powinna być łatwa:

  1. Czy to prawda, że NIE muszę pisać jawnych instrukcji release-property w moim dealloc, gdy używam ARC? Innymi słowy, czy to prawda, że ​​poniższe elementy NIE wymagają wyraźnego cofnięcia przydziału?

    @interface MyClass : NSObject
    @property (strong, nonatomic) NSObject* myProperty;
    @end
    
    @implementation MyClass
    @synthesize myProperty;
    @end
    
  2. Moje następne i ważniejsze pytanie pochodzi z wiersza w dokumencie Przejście na wersję ARC :

    Nie musisz (a nawet nie możesz) zwolnić zmiennych instancji, ale może być konieczne wywołanie funkcji [self setDelegate: nil] na klasach systemowych i innym kodzie, który nie jest kompilowany przy użyciu ARC.

    To nasuwa pytanie: skąd mam wiedzieć, które klasy systemowe nie są kompilowane z ARC? Kiedy powinienem tworzyć własne dealloc i jawnie ustawiać silnie zachowujące właściwości na zero? Czy powinienem założyć, że wszystkie klasy ram NS i UI używane we właściwościach wymagają jawnych deallocs?

Istnieje wiele informacji na temat SO i nie tylko na temat praktyk zwalniania zabezpieczającego ivar nieruchomości podczas korzystania z ręcznego śledzenia odniesień, ale stosunkowo niewiele na ten temat w przypadku korzystania z ARC.

emfurry
źródło

Odpowiedzi:

197

Krótka odpowiedź : nie, nie musisz zerować właściwości w deallocramach ARC.

Długa odpowiedź : nigdy nie powinieneś zerować właściwości dealloc, nawet przy ręcznym zarządzaniu pamięcią.

W MRR powinieneś zwolnić bluszcze . Nilling out oznacza wywoływanie seterów, które mogą wywoływać kod, którego nie powinny dotykać dealloc(np. Jeśli twoja klasa lub podklasa przesłania setter). Podobnie może wywołać powiadomienia KVO. Zamiast tego uwolnienie ivar pozwala uniknąć tych niepożądanych zachowań.

W ARC system automatycznie zwalnia wszelkie ivary za Ciebie, więc jeśli to wszystko, co robisz, nie musisz nawet wdrażać dealloc. Jednakże, jeśli masz jakiekolwiek bloki nie będące obiektami, które wymagają specjalnej obsługi (np. Przydzielone bufory, których potrzebujesz free()), nadal musisz sobie z nimi radzić dealloc.

Ponadto, jeśli ustawiłeś siebie jako delegata jakichkolwiek obiektów, powinieneś usunąć tę relację w dealloc(tu chodzi o wywoływanie [obj setDelegate:nil]). Uwaga dotycząca robienia tego na klasach, które nie są kompilowane z ARC, jest ukłonem w stronę słabych właściwości. Jeśli klasa wyraźnie oznaczy swoją delegatewłaściwość weak, nie musisz tego robić, ponieważ natura słabych właściwości oznacza, że ​​zostanie ona za Ciebie zakończona. Jeśli jednak właściwość jest zaznaczona assign, należy ją wyzerować, w deallocprzeciwnym razie klasa pozostanie z wiszącym wskaźnikiem i prawdopodobnie ulegnie awarii, jeśli spróbuje wysłać wiadomość do swojego delegata. Należy pamiętać, że dotyczy to tylko niezachowanych relacji, takich jak pełnomocnicy.

Lily Ballard
źródło
2
To ma sens! Pozwól, że zapytam cię jednak: typowym scenariuszem jest to, że mam MyController : UIViewControllerklasę, która tworzy i jest właścicielem UIView, a także ustawia delegata widoku na siebie. To jedyny zachowany właściciel tego poglądu. Kiedy kontroler zostanie zwolniony, widok powinien również zostać zwolniony. Czy w takim razie ma znaczenie, czy wskaźnik delegata zwisa?
emfurry
4
@emfurry: Prawdopodobnie tak nie jest, ponieważ zanim kontroler widoku umiera, sam widok nie powinien znajdować się w hierarchii widoków i nie powinien nic robić, ale najlepiej nie robić założeń. Co się stanie, jeśli widok asynchronicznie zaplanował pracę do wykonania później, a sam widok przeżyje swój kontroler widoku przez krótki czas (np. Ze względu na pracę asynchroniczną, która tymczasowo zachowuje widok)? Dla bezpieczeństwa najlepiej po prostu wyeliminować delegata. W rzeczywistości, jeśli dany widok to a UIWebView, dokumenty wyraźnie stwierdzają, że musisz wyzerować delegata.
Lily Ballard
3
@zeiteisen: No. unsafe_unretainedjest dokładnie odpowiednikiem assignwłaściwości i jest normalnym zachowaniem dla relacji delegatów w ramach MRR i należy je uzupełnić.
Lily Ballard
4
Nie zgadzam się z oświadczeniem o niestosowaniu seterów w dealloc z MRC. Apple tego nie poleca, ale robią to również w swoim kodzie. W rzeczywistości możesz stworzyć nowe problemy, nie używając ustawiacza. Jest na ten temat kilka ważnych dyskusji. Ważne jest, aby poprawnie napisać setera (musi zachowywać się poprawnie, jeśli przekażesz mu wartość zerową) i czasami obserwować kolejność zwalniania.
Sulthan
7
@Sulthan: To, czy używać seterów w dealloc, czy nie, to ogromna puszka robaków, ale moje stanowisko w zasadzie sprowadza się do: chcesz wywoływać jak najmniej kodu w dealloc. Setery mają tendencję do włączania efektów ubocznych, albo przez nadpisywanie w podklasach, albo przez KVO lub inne mechanizmy. Szczególnie należy unikać skutków ubocznych dealloc, takich jak zarazy. Jeśli możesz usunąć wywołanie metody z dealloc, powinieneś to zrobić. Jest to uproszczone do: nie wywołuj seterów w dealloc.
Lily Ballard
2

Żeby udzielić przeciwnej odpowiedzi ...

Krótka odpowiedź : nie, nie musisz eliminować właściwości deallocautosyntetyzowanych w ramach ARC. I nie musisz używać setera dla tych w init.

Długa odpowiedź : Powinieneś wyeliminować zsyntetyzowane na zamówienie właściwości dealloc, nawet pod ARC. I powinieneś użyć setera dla tych w init.

Chodzi o to, że twoje zsyntetyzowane na zamówienie właściwości powinny być bezpieczne i symetryczne pod względem zerowania.

Możliwy ustawiacz timera:

-(void)setTimer:(NSTimer *)timer
{
    if (timer == _timer)
        return;

    [timer retain];
    [_timer invalidate];
    [_timer release];
    _timer = timer;
    [_timer fire];
}

Możliwy ustawiacz dla scrollview, tableview, webview, textfield, ...:

-(void)setScrollView:(UIScrollView *)scrollView
{
    if (scrollView == _scrollView)
        return;

    [scrollView retain];
    [_scrollView setDelegate:nil];
    [_scrollView release];
    _scrollView = scrollView;
    [_scrollView setDelegate:self];
}

Możliwy ustawiacz dla właściwości KVO:

-(void)setButton:(UIButton *)button
{
    if (button == _button)
        return;

    [button retain];
    [_button removeObserver:self forKeyPath:@"tintColor"];
    [_button release];
    _button = button;
    [_button addObserver:self forKeyPath:@"tintColor" options:(NSKeyValueObservingOptions)0 context:NULL];
}

Wtedy nie trzeba powielić dowolny kod dealloc, didReceiveMemoryWarning, viewDidUnload, ... a nieruchomość może być bezpiecznie podawane do wiadomości publicznej. Jeśli martwiłeś się, że nie ma żadnych właściwości w dealloc, być może nadszedł czas, aby ponownie sprawdzić swoje setery.

Cœur
źródło