ViewController respondesToSelector: mensagem enviada para instância desalocada (CRASH)

95

Ok, aqui está o negócio, eu odeio fazer perguntas sobre minha depuração e travamentos. Porque normalmente eu mesmo cuido delas, mas simplesmente não consigo resolver isso, mesmo depois de já ter visto várias perguntas .

Ok, então aqui está o problema, acho meu aplicativo ligado e desligado aleatoriamente travando com este rastreamento de pilha:

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

Onde ViewControllerpode variar, às vezes o lugar onde meu código falha, NÃO tem relevância para aquele particular ViewControllere não possui ou chama isso.

Além disso, para obter o rastreamento do console, habilitei o Zombies, caso contrário, não obteria nenhuma impressão do console, apenas obteria:, o objc_msgSendque eu sei que significa que estou enviando uma mensagem de algo que foi lançado. Mas não consigo descobrir onde é ... estou mesmo preso! Normalmente eu sempre depuro minhas falhas, então estou realmente preso nisso.

Novamente, isso trava em lugares diferentes em momentos diferentes, ligado e desligado. E o local em que ele cai quase não tem relevância para o ViewController. E acho isso muito confuso.

Você precisa de algum do meu código? Eu tenho muitos arquivos e como ele está travando em lugares diferentes, distribuir meu código será uma bagunça!

Tentei adicionar breakpoints simbólicos sem sorte, e Zombies não está disponível no aplicativo Instruments para iOS. Não consigo executar meu aplicativo no simulador, pois ele possui estruturas de arquitetura incompatíveis.

Obrigado a todos ...

MCKapur
fonte
você olhou para esta pergunta: stackoverflow.com/questions/1585688/…
self
Supondo que a maneira como você faz a transição para suas visualizações seja consistente, talvez você possa nos mostrar um ou dois exemplos. Se você está fazendo chamadas push / presentViewController padrão, você deve ficar bem, mas vejo muitas pessoas aqui fazendo coisas como alocar / inicializar um controlador de visualização, mas não fazer um push / present, mas apenas adicionar o controlador visualizar como uma subvisão. Apenas um exemplo aleatório. Mas não podemos diagnosticar isso sem algum código. Esperamos que alguns trechos nos ajudem a descobrir o que está acontecendo, então vamos ver.
Rob
Que tal habilitar pontos de interrupção simbólicos? Tente adicionar estes: wiki.zemingo.com/index.php?title=Symbolic_Breakpoints
Stavash
@RobertRyan Eu uso presentModalViewController, não o adiciono como uma
subvisualização
No meu caso, meu controlador de visualização filho continha um webView, e o VC filho era o delegado para o scrollView do webView. Eu precisava remover manualmente a referência de delegado durante o dealloc / viewWillDisappear ou tive essa falha. Espero que ajude alguém.
Dermot de

Respostas:

169

Use instrumentos para rastrear erros de instância desalocada. Crie o perfil de seu aplicativo ( Cmd ⌘+ I) e escolha o modelo Zumbis . Depois que seu aplicativo estiver em execução, tente travá-lo. Você deve conseguir algo assim:

insira a descrição da imagem aqui

Clique na seta ao lado do endereço no popover para mostrar o objeto que foi chamado depois de ser desalocado.

insira a descrição da imagem aqui

Você deve ver agora cada chamada que mudou, reter a contagem deste objeto. Isso pode ocorrer porque o envio de mensagens reter / liberar diretamente, bem como drenar pools de liberação automática ou inserir em NSArrays.

A coluna RefCt mostra reterCount depois que a ação foi chamada e o responsável pela chamada responsável mostra o nome da classe e o método em que foi executada. Quando você clica duas vezes em qualquer retenção / liberação, os instrumentos mostram a linha de código onde isso foi executado (se isso não estiver funcionando, você pode examinar a chamada selecionando-a e escolhendo sua contraparte no painel Detalhes estendidos ):

insira a descrição da imagem aqui

Isso permitirá que você examine todo o ciclo de vida do objeto retentCount e provavelmente você encontrará seu problema imediatamente. Tudo o que você precisa fazer é encontrar a retenção ausente para a versão mais recente .

Johnnywho
fonte
3
O problema pode não ser o mais recente release, especificamente. O problema é qualquer desequilíbrio release. Também posso ser simplesmente um fracasso em retainalgo que você está observando e referenciando mais tarde.
Ken Thomases
1
Além disso, não tenho um modelo de instrumentos Zombie, isso pode ser porque estou usando o Xcode Beta 4.5, vou mudar para 4.4 por enquanto
MCKapur
2
Oh, zumbis são fornecidos apenas no simulador iOS. NÃO POSSO executar no simulador iOS, alguns dos meus frameworks e bibliotecas usadas não suportam a arquitetura
MCKapur
Apenas uma pequena nota. Este é o que há de novo no xcode 5. "O modelo de instrumento Zombies foi aprimorado no Xcode 5 e agora suporta o uso em dispositivos. O uso de Zombies em dispositivos requer iOS 7." Esta nota trouxe você por mim e 2 horas do meu precioso tempo ...
nickfox
2
O que significa se nosso aplicativo para de travar e para de dar um erro "mensagem enviada para instância desalocada" quando conectamos este instrumento a ele? (É como se a "doença" desaparecesse quando o paciente fosse submetido a um "teste diagnóstico".)
Praxiteles
59

teve um problema semelhante. No meu caso, um viewController precisava obter eventos de navigationController, então ele estava se registrando como delegado do controlador de navegação:

 self.navigationController.delegate = self;

O travamento ocorre quando aquele controlador foi desalocado, mas ainda era o delegado para o controlador de visualização. Adicionar este código no dealloc não teve efeito:

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

porque no ponto em que o dealloc é chamado, o controlador de exibição já foi removido da hierarquia de exibição, então self.navigationController é nulo, portanto a comparação falhará! :-(

A solução foi adicionar esse código para detectar o VC deixando a hierarquia de visualizações antes de realmente fazê-lo. Ele usa um método introduzido no iOS 5 para determinar quando a visualização está sendo exibida e não empurrada

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

Não há mais travamentos!

software evoluiu
fonte
Eu também obrigado - apenas 4 horas de pesquisa foram necessárias para encontrar este post.
daidai de
Obrigado por postar, tive o mesmo problema ^^
Tyron
Como vocês encontram soluções para questões tão irritantes? Tirem o chapéu !!
ViruMax
4

Para quem não consegue resolver, aqui estão algumas outras técnicas:

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

Você pode executar Instrumentos no Xcode 5 clicando no pop-up do projeto-> Editar Esquema ... Perfil -> Instrumento e escolher Alocações ou Vazamentos, então criar o perfil de seu aplicativo, então parar Instrumentos, clicar no botão de informação em Alocações e "Ativar Detecção NSZombie" .

No entanto, para as mensagens que vêm diretamente do com.apple.main-thread, isso provavelmente não revelará nada.

Bati minha cabeça nisso por mais de duas horas e a resposta acabou sendo um lançamento excessivo, que descobri comentando uma cópia do meu projeto por força bruta até encontrar o culpado:

[viewController release];
viewController = NULL;

O problema é que o release não define a variável como NULL.

Isso significa que defini-lo como NULL chama o release novamente, diminuindo o refcount e liberando a memória imediatamente até mais tarde, quando as variáveis ​​que referenciam o viewController terminarem com ele.

Portanto, habilite o ARC ou certifique-se de que seu projeto use consistentemente release ou NULL, mas não ambos. Minha preferência é usar NULL porque então não há chance de fazer referência a um zumbi, mas torna mais difícil encontrar onde os objetos são liberados.

Zack Morris
fonte
4

Eu tinha encontrado o mesmo problema no iOS ontem. Eu fiz IAP na subvisão "Sobre" do aplicativo e adicionei o Transaction Observer no viewDidLoad "Sobre". Quando comprei pela primeira vez, sem problemas, mas depois que voltei à janela principal e entrei sobre a subvisualização para comprar novamente, o problema "mensagem enviada para instância desalocada" aconteceu e o aplicativo travou.

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

Depois de remover o Transaction Observer no dealloc, o problema está resolvido.

- (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
fonte
Ele consertou minha falha de tempo de execução ... Eu estava obtendo um zombieobjeto para compras no aplicativo. Depois de muitas horas de escavação, encontrei este ... UM GRANDE OBRIGADO Cara.
Mahendra de
4

Eu tive um problema muito semelhante e descobri que era devido ao conjunto de delegados do controlador de navegação.

O que segue resolveu meu problema,

- (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
fonte
Obrigado!! era o mesmo problema aqui.
pegpeg
2

Tive o mesmo problema no OS X.

Para resolver este - (void)deallocmétodo não basta como o @SoftwareEvolved já disse. Mas infelizmente- (void)viewWillDisappear está disponível apenas na versão 10.10 e posterior.

Eu introduzi o método personalizado em minha subclasse NSViewController, onde definir todas as referências perigosas para zumbis como nulo. No meu caso, eram NSTableViewpropriedades ( delegatee dataSource).

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

Isso é tudo. Cada vez que estou prestes a remover a visualização da visualização, preciso chamar este método.

Astoria
fonte
2

Eu tive o mesmo problema. Foi difícil descobrir qual delegado causou problema, porque ele não indica nenhuma linha ou instrução de código. Então, tentei de alguma forma. Talvez seja útil para você.

  1. Abra o arquivo xib e, com o proprietário do arquivo, selecione "mostrar o inspetor de conexões" no menu do lado direito. Os delegados são listados, defina-os como zero que são suspeitos.
  2. (O mesmo que no meu caso) Objetos de propriedade como Textfield podem criar problemas, então defina seus delegados como nulo.
-(void) viewWillDisappear:(BOOL) animated{

[super viewWillDisappear:animated];

if ([self isMovingFromParentViewController]){

self.countryTextField.delegate = nil;

self.stateTextField.delegate = nil;

}

}
nadim
fonte