ViewController respondsToSelector: wiadomość wysłana do cofniętej instancji (CRASH)

95

OK, oto umowa, nienawidzę zadawać pytań dotyczących mojego debugowania i awarii. Ponieważ zwykle sam sobie z nimi radzę, ale po prostu nie mogę sobie z tym poradzić , nawet po przejrzeniu wielu pytań .

OK, więc tutaj jest problem, moja aplikacja losowo włącza się i wyłącza z tym śladem stosu:

*** -[ViewController respondsToSelector:]: message sent to deallocated instance 0x1e5d2ef0

Gdzie ViewControllermoże się różnić, czasami miejsce, w którym mój kod ulega awarii, NIEViewController ma żadnego związku z tym konkretnym i nie jest jego właścicielem ani nie nazywa go.

Ponadto, aby uzyskać śledzenie konsoli, włączyłem Zombies, w przeciwnym razie nie uzyskałbym żadnego wydruku konsoli, dostałbym tylko:, objc_msgSendco wiem, oznacza, że ​​wysyłam wiadomość do czegoś, co zostało wydane. Ale nie mogę znaleźć tego, gdzie to jest ... Naprawdę utknąłem! Zwykle zawsze debuguję swoje awarie, więc naprawdę utknąłem na tym.

Ponownie, powoduje to awarie w różnych miejscach w różnym czasie, z włączaniem i wyłączaniem. Miejsce awarii nie ma prawie żadnego związku z ViewController. Uważam to za bardzo zagmatwane.

Potrzebujesz mojego kodu? Mam dużo plików, a ponieważ program się zawiesza w różnych miejscach, dystrybucja mojego kodu będzie bałaganem!

Próbowałem dodać symboliczne punkty przerwania bez powodzenia, a Zombies nie jest dostępny w aplikacji Instruments na iOS. Nie mogę uruchomić mojej aplikacji na symulatorze, ponieważ ma ona nieobsługiwane struktury architektury.

Dziękuję wszystkim...

MCKapur
źródło
czy spojrzałeś na to pytanie: stackoverflow.com/questions/1585688/…
self
Zakładając, że sposób, w jaki przechodzisz do swoich poglądów, jest spójny, może możesz pokazać nam przykład lub dwa. Jeśli wykonujesz standardowe wywołania push / presentViewController, powinno być dobrze, ale widzę, że wiele osób tutaj robi takie rzeczy, jak przydzielanie / inicjowanie kontrolera widoku, ale nie wykonuje push / present, ale raczej dodaje kontroler widok jako widok podrzędny. Tylko losowy przykład. Ale nie możemy tego zdiagnozować bez kodu. Mamy nadzieję, że kilka fragmentów pomoże nam dowiedzieć się, co się dzieje, więc zobaczmy.
Rob
A co z włączaniem symbolicznych punktów przerwania? Spróbuj dodać te: wiki.zemingo.com/index.php?title=Symbolic_Breakpoints
Stavash
@RobertRyan Używam presentModalViewController, nie dodam go jako
subview
W moim przypadku mój podrzędny kontroler widoku zawierał webView, a podrzędny VC był delegatem scrollView webView. Musiałem ręcznie usunąć odwołanie delegata podczas dealloc / viewWillDisappear lub otrzymałem tę awarię. Mam nadzieję, że to komuś pomoże.
Dermot

Odpowiedzi:

169

Użyj Instrumentów, aby wyśledzić cofnięte błędy instancji. Profiluj swoją aplikację ( Cmd ⌘+ I) i wybierz szablon Zombies . Po uruchomieniu aplikacji spróbuj ją zawiesić. Powinieneś dostać coś takiego:

wprowadź opis obrazu tutaj

Kliknij strzałkę obok adresu w wyskakującym okienku, aby wyświetlić obiekt, który został wywołany po cofnięciu przydziału.

wprowadź opis obrazu tutaj

Powinieneś teraz zobaczyć każde wywołanie, które się zmieniło, zachowuje licznik tego obiektu. Może to być spowodowane bezpośrednim wysyłaniem wiadomości o zachowaniu / zwolnieniu, a także opróżnianiem puli autowyrejestrowań lub wstawieniem do NSArrays.

Kolumna RefCt pokazuje retainCount po wywołaniu akcji, a Responsible Caller pokazuje nazwę klasy i metodę, w której została wykonana. Po dwukrotnym kliknięciu dowolnego zatrzymania / zwolnienia instrumenty pokażą wiersz kodu, w którym zostało to wykonane (jeśli to nie działa, możesz sprawdzić połączenie, zaznaczając je i wybierając jego odpowiednik w okienku Rozszerzone szczegóły ):

wprowadź opis obrazu tutaj

Umożliwi to zbadanie całego cyklu życia obiektu retainCount i prawdopodobnie od razu znajdziesz swój problem. Wszystko co musisz zrobić, to znaleźć brakuje zachować dla najnowszego wydania .

Johnnywho
źródło
3
W szczególności problem może nie być najnowszy release. Problemem jest to, każdy niezrównoważony release. Mogę też być po prostu niepowodzeniem w retainczymś, do czego masz wskaźnik i odnosisz się później.
Ken Thomases
1
Nie mam też szablonu instrumentów Zombie, może to być spowodowane tym, że używam Xcode Beta 4.5, w międzyczasie przełączę się na 4.4
MCKapur
2
Och, Zombie są dostępne tylko w symulatorze iOS. NIE MOGĘ uruchomić w symulatorze iOS, niektóre z moich używanych frameworków i bibliotek nie obsługują architektury
MCKapur
Tylko mała uwaga. Pochodzi z nowości w xcode 5. „Szablon instrumentu Zombies został ulepszony w Xcode 5 i obsługuje teraz używanie na urządzeniach. Korzystanie z Zombies na urządzeniach wymaga iOS 7.” Ta notatka przyniosła mi 2 godziny mojego cennego czasu ...
nickfox,
2
Co to znaczy, że nasza aplikacja przestaje ulegać awarii i przestaje wyświetlać komunikat o błędzie „wiadomość wysłana do cofniętej instancji”, gdy podłączymy do niej ten instrument? (To tak, jakby „choroba” zniknęła po wykonaniu „testu diagnostycznego”)
Praxiteles,
59

miał podobny problem. W moim przypadku viewController musiał pobrać zdarzenia navigationController, więc rejestrował się jako delegat kontrolera nawigacji:

 self.navigationController.delegate = self;

Awaria występuje, gdy ten kontroler został zwolniony, ale nadal był delegatem dla kontrolera widoku. Dodanie tego kodu w dealloc nie przyniosło żadnego efektu:

-(void) dealloc
{
    if (self.navigationController.delegate == self)
    {
        self.navigationController.delegate = nil;
    }

ponieważ w momencie wywołania dealloc kontroler widoku został już usunięty z hierarchii widoków, więc self.navigationController ma wartość zero, więc porównanie jest gwarantowane niepowodzeniem! :-(

Rozwiązaniem było dodanie tego kodu w celu wykrycia VC opuszczającego hierarchię widoku tuż przed tym, jak faktycznie to zrobi. Używa metody wprowadzonej w iOS 5 do określenia, kiedy widok jest wyskakujący, a nie wypychany

-(void) viewWillDisappear:(BOOL) animated
{  
   [super viewWillDisappear:animated];
   if ([self isMovingFromParentViewController])
   {
      if (self.navigationController.delegate == self)
      {
           self.navigationController.delegate = nil;
      }
   }
}

Nigdy więcej awarii!

oprogramowanie ewoluowało
źródło
Ja też dziękuję - potrzeba tylko 4 godzin wyszukiwania, aby znaleźć ten post.
daidai,
Dzięki za wysłanie wiadomości, miałem ten sam problem ^^
Tyron
Jak ludzie znajdują rozwiązania tak irytujących problemów? Czapki z głów !!
ViruMax
4

Dla każdego, kto nie może tego rozwiązać, oto kilka innych technik:

https://stackoverflow.com/a/12264647/539149

https://stackoverflow.com/a/5698635/539149

https://stackoverflow.com/a/9359792/539149

https://stackoverflow.com/a/15270549/539149

https://stackoverflow.com/a/12098735/539149

Możesz uruchomić Instruments w Xcode 5, klikając wyskakujące okienko projektu-> Edytuj schemat ... Profil -> Instrument i wybierz Alokacje lub Wycieki, a następnie profiluj swoją aplikację, następnie zatrzymaj instrumenty, kliknij przycisk informacji w Alokacjach i „Włącz wykrywanie NSZombie” .

Jednak w przypadku wiadomości pochodzących bezpośrednio z wątku com.apple.main-thread prawdopodobnie niczego to nie ujawni.

Waliłem w to głowę przez ponad dwie godziny, a odpowiedź okazała się nadwyżką, którą odkryłem, komentując kopię mojego projektu brutalną siłą, dopóki nie znalazłem winowajcy:

[viewController release];
viewController = NULL;

Problem polega na tym, że release nie ustawia zmiennej na NULL.

Oznacza to, że ustawienie go na NULL wywołuje zwalnianie ponownie, zmniejszając refcount i natychmiast zwalniając pamięć, aż do późniejszego zakończenia zmiennych, które odwołują się do viewController.

Więc włącz ARC lub upewnij się, że projekt konsekwentnie używa wydania lub NULL, ale nie obu. Wolę używać NULL, ponieważ wtedy nie ma szans na odniesienie się do zombie, ale utrudnia to znalezienie miejsca, w którym obiekty są uwalniane.

Zack Morris
źródło
4

Wczoraj spotkałem się z tym samym problemem w iOS. Utworzyłem IAP w podglądzie „Informacje” aplikacji i dodałem Obserwator transakcji w widoku „Informacje” viewDidLoad. Kiedy kupuję po raz pierwszy, nie ma problemu, ale po powrocie do głównego okna i ponownym wejściu o podglądzie do zakupu, pojawił się problem „wiadomość wysłana do cofniętej instancji” i aplikacja uległa awarii.

- (void)viewDidLoad
{
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];                                           object:nil];
}

Po usunięciu obserwatora transakcji w dealloc problem został rozwiązany.

- (void)dealloc
{
    // Even though we are using ARC, we still need to manually stop observing any
    // NSNotificationCenter notifications.  Otherwise we could get "zombie" crashes when
    // NSNotificationCenter tries to notify us after our -dealloc finished.

    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
Ouyang Yong
źródło
Naprawiono moją awarię środowiska wykonawczego ... zombieOtrzymałem obiekt do zakupów w aplikacji. Po wielu godzinach kopania znalazłem ten… WIELKIE DZIĘKI Człowieku.
Mahendra,
4

Miałem bardzo podobny problem i doszedłem do wniosku, że jest to spowodowane ustawieniem delegatów kontrolera nawigacji.

Poniższe rozwiązało mój problem,

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    if (self.navigationController.delegate != self) {
        self.navigationController.delegate = self;
    }
}

-(void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];

    if (self.navigationController.delegate == self) {
        self.navigationController.delegate = nil;
    }
}
thatzprem
źródło
Dzięki!! tutaj był ten sam problem.
pegpeg
2

Miałem ten sam problem w OS X.

Aby rozwiązać tę niewystarczającą - (void)deallocmetodę, jak już powiedział @SoftwareEvolved. Ale niestety - (void)viewWillDisappearjest dostępny tylko w wersji 10.10 i nowszych.

Wprowadziłem niestandardową metodę w mojej podklasie NSViewController, w której ustawiłem wszystkie odniesienia niebezpieczne dla zombie na nil. W moim przypadku były to NSTableViewwłaściwości ( delegatei dataSource).

- (void)shutdown
{
  self.tableView.delegate = nil;
  self.tableView.dataSource = nil;
}

To wszystko. Za każdym razem, gdy mam zamiar usunąć widok z okna nadzoru, wywołaj tę metodę.

Astoria
źródło
2

Miałem ten sam problem. Trudno było znaleźć delegata, który powoduje problem, ponieważ nie wskazuje on żadnej instrukcji w wierszu ani kodzie, więc spróbuję jakiś sposób, może to okaże się pomocne.

  1. Otwórz plik xib i od właściciela pliku, wybierz „pokaż inspektora połączeń” po prawej stronie menu. Na liście są delegaci, którzy są podejrzani, ustaw ich na zero.
  2. (Tak jak w moim przypadku) Obiekt właściwości, taki jak Textfield, może tworzyć problem, więc ustaw jego delegatów na zero.
-(void) viewWillDisappear:(BOOL) animated{

[super viewWillDisappear:animated];

if ([self isMovingFromParentViewController]){

self.countryTextField.delegate = nil;

self.stateTextField.delegate = nil;

}

}
nadim
źródło