KVO i ARC, jak usunąćObserver

87

Jak usunąć obserwatora z obiektu pod ARC ? Czy po prostu dodajemy obserwatora i zapominamy o jego usunięciu? Jeśli nie zarządzamy już pamięcią ręcznie, gdzie rezygnujemy z obserwacji?

Na przykład na kontrolerze widoku:

[self.view addObserver:self
            forKeyPath:@"self.frame"
               options:NSKeyValueObservingOptionNew 
               context:nil];

Wcześniej wywoływałbym metodę removeObserver:kontrolera widoku dealloc.

drunknbass
źródło
4
Zauważ, że KVO .frame to bardzo zły pomysł. Jak napisali indziej inżynierowie Apple w StackOverflow, właściwość ramki UIKit nie jest zgodna z KVO. Kiedy to działa, to tylko przypadek.
steipete
2
Czy nie powinno być @"frame"raczej keyPath niż @"self.frame"?
Besi

Odpowiedzi:

126

Nadal możesz wdrożyć w -deallocramach ARC, co wydaje się być odpowiednim miejscem do usunięcia obserwacji kluczowych wartości. Po prostu nie dzwonisz już [super dealloc]z tej metody.

Jeśli -releasewcześniej dominowałeś, robiłeś coś w niewłaściwy sposób.

Brad Larson
źródło
1
Jesteś tego pewien? Cytuję z clang.llvm.org/docs/… , rozdział 7.1.2. dealloc: "Uzasadnienie: mimo że ARC niszczy zmienne instancji automatycznie, nadal istnieją uzasadnione powody, aby napisać metodę dealloc, takie jak zwolnienie zasobów, których nie można zachować. Brak wywołania [super dealloc] w takiej metodzie jest prawie zawsze błędem."
Elise van Looij
@ElisevanLooij Tak, to prawda. Jeśli wywodzisz się z tej klasy, wydaje się oczywiste, że musisz zadzwonić [super dealloc]. Kto jeszcze powinien to zrobić za Ciebie.
Björn Landmesser,
@ElisevanLooij Ups, cóż, powinienem był wcześniej sprawdzić. Nie jest dozwolone wywołanie [super dealloc]metody dealloc. Nie mam pojęcia, jak by to działało podczas tworzenia podklasy wspomnianej klasy. Może po prostu wskazane jest użycie finalizezamiast tego (tam, gdzie dzwonisz [super finalize])
Björn Landmesser
17
@ElisevanLooij - Kwestia, którą próbowali przedstawić, dotyczy przypadku ręcznego zarządzania pamięcią. Ponieważ nie wywoływanie [super dealloc]last w tej metodzie jest prawie zawsze błędem przy ręcznym zarządzaniu pamięcią, kompilator obsługuje to teraz za Ciebie, dlatego nie możesz już wywoływać -deallocbezpośrednio. Jedyne, co umieszczasz w -deallocmetodzie w ARC, to wszelkie zasoby niebędące obiektami, które musisz zwolnić, lub zadania czyszczenia, takie jak usuwanie obserwatorów. Sformułowanie, którego używają, jest trochę mętne, ale właśnie to miało na myśli.
Brad Larson
7
@ BjörnMilcke - Jak komentuję odpowiedź Elise, -finalizejest używany do tego w ramach czyszczenia pamięci , gdzie -deallocnigdy nie jest wywoływany, ale umieszczenie tego kodu w -deallocARC jest całkowicie dopuszczalne . [super dealloc]jest wywoływana automatycznie, dlatego wywoływanie jej w ramach ARC jest błędem.
Brad Larson
1

Robię to z tym kodem

- (void)dealloc
{
@try{
    [self.uAvatarImage removeObserver:self forKeyPath:@"image" context:nil];
} @catch(id anException) {
    //do nothing, obviously it wasn't attached because an exception was thrown
}
}    
user3461902
źródło
2
Jaki jest sens obsługi wyjątków dealloc? Za późno, żeby cokolwiek z tym zrobić.
Abizern
Jaki jest sens usuwania obserwatorów ze zmiennej instancji w dealloc? To uAvatarImage zostanie wkrótce cofnięte wraz z wszystkimi obserwatorami, które zasubskrybował do jego kluczowych ścieżek.
shoumikhin
1
@shoumikhin Używam ARC i musiałem usunąć obserwatora w metodzie dealloc. Mam to samo pytanie, które masz. Jednak gdy uruchomiłem wiele instancji klasy, w końcu otrzymałem błąd exc_bad_address. To rozwiązało problem. Również odpowiedź z tego miejsca stackoverflow.com/questions/32490808/… pomogła mi odkryć problem.
mac10688
-2

W innym miejscu na temat przepełnienia stosu Chris Hanson radzi użyć do tego celu metody finalize i zaimplementować oddzielną metodę unieważnienia, aby właściciele mogli powiedzieć obiektom, że są gotowe. W przeszłości stwierdziłem, że rozwiązania Hansona są dobrze przemyślane, więc będę się tym zajmował.

Elise van Looij
źródło
13
Zauważ, że odnosił się on do zbierania śmieci, a nie ARC (jego odpowiedź została napisana w 2008 roku). W ramach czyszczenia pamięci -deallocnigdy nie jest wywoływana. W ARC tak jest. Całkowicie akceptowalne jest usunięcie obserwatorów KVO w -dealloc, jak wskazuje Chris Lattner (który wie, o czym mówi) na forach programistów Apple: devforums.apple.com/message/475850
Brad Larson
3
Dzięki Brad, za wykonanie całej tej pracy. Nie, aby sfinalizować, tak, aby cofnąć przydział, ale bez [super dealloc]. Naprawdę proste, kiedy już to wiesz. Hej, @drunknbass, przyjmij odpowiedź tego człowieka!
Elise van Looij