web-dev-qa-db-fra.com

ViewController respondsToSelector: message envoyé à l'instance désallouée (CRASH)

Ok, voici le contrat, je déteste poser des questions sur mon débogage et les plantages. Parce que je les gère habituellement moi-même, mais que je ne peux pas y arriver, même après avoir déjà consulté plusieurs questions .

Ok donc voici le problème, je trouve mon application aléatoire et s'écraser avec cette trace de pile:

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

ViewController peut varier, parfois le lieu où mon code se bloque a [~ # ~] non [~ # ~] en relation avec ce particulier ViewController et ne le possède pas et ne l'appelle pas.

De plus, pour obtenir cette trace de la console, j'ai activé Zombies, sinon je n'aurais aucune impression de console, mais seulement: objc_msgSend, qui, je le sais, signifie que je diffuse quelque chose qui est publié. Mais je ne trouve pas où c'est… Je suis vraiment coincé! Habituellement, je toujours débogue mes crashs, donc je suis vraiment bloqué à ce sujet.

Encore une fois, cela se bloque à différents endroits et à différents moments, de temps en temps. Et l'endroit où il se bloque n'a presque aucune pertinence pour le ViewController. Et je trouve cela très déroutant.

Avez-vous besoin de mon code? J'ai beaucoup de fichiers et, comme il tombe en panne à différents endroits, la distribution de mon code sera un désastre!

J'ai essayé d'ajouter des points d'arrêt symboliques sans succès, et Zombies n'est pas disponible sur l'application Instruments pour iOS. Je ne peux pas exécuter mon application sur le simulateur car celui-ci est doté de structures d'architecture non compatibles.

Merci tout le monde...

95
MCKapur

Utilisez Instruments pour localiser les erreurs d'instance désallouées. Profil de votre application (Cmd ⌘+I) et choisissez Zombies template. Une fois votre application en cours d'exécution, essayez de la bloquer. Vous devriez obtenir quelque chose comme ça:

enter image description here

Cliquez sur la flèche à côté de l'adresse dans la fenêtre pop-up pour afficher l'objet appelé après sa désallocation.

enter image description here

Vous devriez voir maintenant chaque appel qui a changé conserve le nombre de cet objet. Cela peut être dû au fait que l’envoi direct de messages de rétention/édition ainsi que le vidage de pools de libération automatique ou l’insertion dans NSArrays.

La colonne RefCt indique la valeur de retentionCount après l’appel de l’action et appelant responsable indique le nom de la classe et la méthode dans laquelle elle a été effectuée. Lorsque vous double-cliquez sur une retenue/validation, les instruments vous montreront la ligne de code où cela a été exécuté (si cela ne fonctionne pas, vous pouvez examiner un appel en le sélectionnant et en choisissant son équivalent dans Détails détaillés vitre):

enter image description here

Cela vous permettra d’examiner tout le cycle de vie des objets RetenirCompte et probablement que vous trouverez tout de suite votre problème. Tout ce que vous avez à faire est de trouver manquant conserver pour les derniers version.

169
Johnnywho

eu un problème similaire. Dans mon cas, un viewController devait obtenir les événements navigationController. Il était donc enregistré en tant que délégué du contrôleur de navigation:

 self.navigationController.delegate = self;

Le crash se produit lorsque ce contrôleur a été désalloué mais était toujours le délégué du contrôleur de vue. L'ajout de ce code dans dealloc n'a eu aucun effet:

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

car au moment de l'appel de dealloc, le contrôleur de vue a déjà été supprimé de la hiérarchie des vues. Par conséquent, self.navigationController est nul, la comparaison est donc vouée à l'échec! :-(

La solution a été d’ajouter ce code pour détecter le VC), laissant la hiérarchie juste avant de le faire. Il utilise une méthode introduite dans iOS 5 pour déterminer le moment où la vue est affichée et pas poussé

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

Pas plus de crash!

59
software evolved

Pour ceux qui ne peuvent pas le résoudre, voici d'autres techniques:

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

Vous pouvez exécuter Instruments dans Xcode 5 en cliquant sur la fenêtre de projet -> Modifier le schéma ... Profil -> Instrument et choisissez Allocations ou Fuites, profilez ensuite votre application, puis arrêtez Instruments, cliquez sur le bouton d’information dans Allocations et "Activer la détection NSZombie". .

Cependant, pour les messages provenant directement du com.Apple.main-thread, cela ne révélera probablement rien.

Je me suis cogné la tête dessus pendant plus de deux heures et la réponse s'est avérée être une libération excessive, que j'ai découverte en commentant une copie de mon projet par force brute jusqu'à ce que je trouve le coupable:

[viewController release];
viewController = NULL;

Le problème est que cette version ne définit pas la variable sur NULL.

Cela signifie que, lorsque NULL est défini, les appels sont relâchés, décrémentant le nombre de références et libérant la mémoire immédiatement jusqu'à ce que les variables faisant référence à viewController en aient terminé.

Activez donc ARC ou assurez-vous que votre projet utilise systématiquement release ou NULL, mais pas les deux. Ma préférence est d'utiliser NULL car il n'y a aucune chance de faire référence à un zombie, mais cela rend plus difficile la recherche des endroits où les objets sont libérés.

4
Zack Morris

Hier, j'avais rencontré le même problème avec iOS. J'ai créé IAP dans la sous-vue "À propos" de l'application et ajouté Transaction Observer à "À propos" de viewDidLoad. Lorsque j'achète pour la première fois, pas de problème, mais après être revenu à la fenêtre principale et entrer dans la sous-vue pour acheter à nouveau, le problème "message envoyé à l'instance désallouée" s'est produit et l'application est tombée en panne.

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

Après avoir supprimé Transaction Observer dans dealloc, le problème est résolu.

- (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];
}
4
Ouyang Yong

J'avais un problème très similaire et j'ai compris que c'était dû aux délégués du contrôleur de navigation.

Le ci-dessous a résolu mon problème,

- (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;
    }
}
4
thatzprem

J'ai eu le même problème. Il était difficile de trouver le problème de cause de délégué, car il n'indique aucune instruction de ligne ou de code.

  1. Ouvrir le fichier xib et à partir du propriétaire du fichier, sélectionnez le menu de droite "Afficher l'inspecteur de connexions". Les délégués sont répertoriés, définissez-les sur zéro, ce qui est suspecté.
  2. (Identique à mon cas) Propriété Un objet comme Textfield peut créer un problème, définissez donc ses délégués sur nil.
-(void) viewWillDisappear:(BOOL) animated{

[super viewWillDisappear:animated];

if ([self isMovingFromParentViewController]){

self.countryTextField.delegate = nil;

self.stateTextField.delegate = nil;

}

}
2
nadim

Avait le même problème dans OS X.

Pour résoudre ce problème, la méthode - (void)dealloc n'est pas suffisante, comme l'a déjà dit @SoftwareEvolved. Malheureusement, - (void)viewWillDisappear n'est disponible que sur les versions 10.10 et supérieures.

J'ai introduit la méthode personnalisée dans ma sous-classe NSViewController, où définir toutes les références zombie-dangerous à nil. Dans mon cas, il s'agissait de NSTableView propriétés (delegate et dataSource).

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

C'est tout. Chaque fois que je suis sur le point de supprimer la vue de la superview besoin d'appeler cette méthode.

2
Astoria