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

95

Ok, voici l'affaire, je déteste poser des questions sur mon débogage et mes plantages. Parce que je les gère généralement moi-même, mais je ne peux tout simplement pas contourner cela, même après avoir déjà vu plusieurs questions .

Ok, voici le problème, je trouve mon application allumée et désactivée au hasard avec cette trace de pile:

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

ViewControllerpeut varier, parfois l'endroit où mon code se bloque, n'a AUCUNE pertinence pour ce particulier ViewControlleret ne le possède pas ou 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, je n'obtiendrais que:, objc_msgSendce que je sais signifie que je envoie un message qui est publié. Mais je ne trouve pas où c'est ... je suis vraiment coincé! Habituellement, je débogue toujours mes plantages, donc je suis vraiment coincé là-dessus.

Encore une fois, cela se bloque à différents endroits à des moments différents, par intermittence. Et l'endroit où il se bloque n'a presque aucun rapport avec le ViewController. Et je trouve cela très déroutant.

Avez-vous besoin de mon code? J'ai beaucoup de fichiers et comme il plante à différents endroits, distribuer mon code sera un gâchis!

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 elle a des cadres d'architecture non compatibles.

Merci tout le monde...

MCKapur
la source
avez-vous regardé cette question: stackoverflow.com/questions/1585688/…
auto
En supposant que la façon dont vous passez à vos points de vue est cohérente, vous pouvez peut-être nous montrer un ou deux exemples. Si vous faites des appels push / presentViewController standard, tout devrait bien se passer, mais je vois beaucoup de gens ici faire des choses comme allouer / lancer un contrôleur de vue, mais ne pas faire un push / present, mais plutôt simplement ajouter le contrôleur voir comme sous-vue. Juste un exemple aléatoire. Mais nous ne pouvons pas diagnostiquer cela sans un code. Espérons que quelques extraits nous aideront à comprendre ce qui se passe, alors voyons voir.
Rob
Pourquoi ne pas activer les points d'arrêt symboliques? Essayez d'ajouter ceux-ci: wiki.zemingo.com/index.php?title=Symbolic_Breakpoints
Stavash
@RobertRyan J'utilise presentModalViewController, je ne l'ajoute pas en tant que sous-vue
MCKapur
Dans mon cas, mon contrôleur de vue enfant contenait un webView et le VC enfant était le délégué du scrollView de la webView. J'avais besoin de supprimer manuellement la référence de délégué pendant dealloc / viewWillDisappear ou j'ai eu ce crash. J'espère que ça aide quelqu'un.
Dermot

Réponses:

169

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

entrez la description de l'image ici

Cliquez sur la flèche à côté de l'adresse dans le popover pour afficher l'objet qui a été appelé après avoir été désalloué.

entrez la description de l'image ici

Vous devriez voir maintenant chaque appel qui a changé le nombre de rétention de cet objet. Cela peut être dû à l'envoi direct de messages de conservation / libération ainsi qu'à la vidange des pools de libération automatique ou à l'insertion dans des NSArrays.

La colonne RefCt affiche retientCount après que l'action a été appelée et l'appelant responsable affiche le nom de la classe et la méthode dans laquelle elle a été effectuée. Lorsque vous double-cliquez sur une conservation / libération, les instruments vous montreront la ligne de code où cela a été effectué (si cela ne fonctionne pas, vous pouvez examiner l'appel en le sélectionnant et en choisissant son homologue dans le volet Détails étendus ):

entrez la description de l'image ici

Cela vous permettra d'examiner tout le cycle de vie de l'objet retentionCount et vous trouverez probablement votre problème tout de suite. Tout ce que vous avez à faire est de trouver manquant de retenir pour le dernier communiqué .

Johnnywho
la source
3
Le problème n'est peut-être pas le plus récent release, en particulier. Le problème est tout déséquilibré release. Je peux aussi simplement être un échec à retainquelque chose sur lequel vous gardez un pointeur et auquel vous faites référence plus tard.
Ken Thomases
1
De plus, je n'ai pas de modèle d'instruments Zombie, c'est peut-être parce que j'utilise Xcode Beta 4.5, je vais passer à 4.4 pour le moment
MCKapur
2
Oh, les zombies ne sont fournis que dans le simulateur iOS. JE NE PEUX PAS exécuter dans le simulateur iOS, certains de mes frameworks et bibliothèques utilisés ne prennent pas en charge l'architecture
MCKapur
Juste un petit mot. Ceci est tiré des nouveautés de xcode 5. "Le modèle d'instrument Zombies a été amélioré dans Xcode 5 et prend désormais en charge l'utilisation sur les appareils. L'utilisation de Zombies sur les appareils nécessite iOS 7." Cette note vous a apporté par moi et 2 heures de mon temps précieux ...
nickfox
2
Qu'est-ce que cela signifie si notre application cesse de planter et cesse de donner une erreur "message envoyé à l'instance désallouée" lorsque nous y accrochons cet instrument? (C'est comme si la «maladie» disparaissait lorsque le patient recevait un «test de diagnostic».)
Praxiteles
59

eu un problème similaire. Dans mon cas, un viewController devait obtenir les événements navigationController, il s'enregistrait donc 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 où dealloc est appelé, le contrôleur de vue a déjà été supprimé de la hiérarchie de vue, donc self.navigationController est nul, donc la comparaison échouera à coup sûr! :-(

La solution consistait à ajouter ce code pour détecter le VC quittant la hiérarchie de vue juste avant qu'il ne le fasse réellement. Il utilise une méthode introduite dans iOS 5 pour déterminer quand la vue est affichée et non poussée

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

Plus de plantages!

logiciel évolué
la source
Moi aussi merci - seulement 4 heures de recherche nécessaires pour trouver ce message.
daidai
Merci d'avoir posté, j'ai eu le même problème ^^
Tyron
Comment les gens trouvent-ils des solutions à un problème aussi irritant? Chapeau !!
ViruMax
4

Pour tous ceux qui ne peuvent pas le résoudre, voici quelques 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 des instruments dans Xcode 5 en cliquant sur la fenêtre contextuelle du projet-> Modifier le schéma ... Profil -> Instrument et choisissez Allocations ou Fuites, puis profilez votre application, puis arrêtez les instruments, cliquez sur le bouton info dans Allocations et "Activer la détection NSZombie" .

Cependant, pour les messages provenant directement du fil de discussion com.apple.main, cela ne révélera probablement rien.

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

[viewController release];
viewController = NULL;

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

Cela signifie que le définir sur NULL appelle à nouveau la libération, décrémentant le refcount et libérant la mémoire immédiatement jusqu'à plus tard, lorsque les variables qui font référence à viewController en ont fini avec lui.

Activez donc ARC ou assurez-vous que votre projet utilise de manière cohérente release ou NULL, mais pas les deux. Ma préférence est d'utiliser NULL car il n'y a alors aucune chance de référencer un zombie mais cela rend plus difficile de trouver où les objets sont libérés.

Zack Morris
la source
4

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

- (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];
}
Ouyang Yong
la source
Il a corrigé mon crash d'exécution ... zombieJ'obtenais un objet pour les achats inApp. Après de nombreuses heures à creuser, j'ai trouvé celui-ci ... UN GRAND MERCI Homme.
Mahendra
4

J'ai eu un problème très similaire et j'ai compris que c'était dû au jeu de 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;
    }
}
thatzprem
la source
Merci!! était le même problème ici.
pegpeg
2

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

Pour résoudre cette - (void)deallocméthode pas assez comme @SoftwareEvolved l'a déjà dit. Mais malheureusement, - (void)viewWillDisappearn'est disponible que sur la version 10.10 et ultérieure.

J'ai introduit une méthode personnalisée dans ma sous-classe NSViewController où définir toutes les références zombie-dangereuses à nil. Dans mon cas, c'était des NSTableViewpropriétés ( delegateet 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 supervision, appelez cette méthode.

Astoria
la source
2

J'ai eu le même problème.Il était difficile de trouver quel délégué causait un problème, car il n'indique aucune ligne ou instruction de code.J'ai donc essayé un moyen, peut-être que cela vous sera utile.

  1. Ouvrez le fichier xib et à partir du propriétaire du fichier, sélectionnez "Afficher l'inspecteur de connexions" dans le menu de droite. Les délégués sont répertoriés, définissez-les sur zéro qui sont suspectés.
  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;

}

}
Nadim
la source