web-dev-qa-db-fra.com

Pause sur EXC_BAD_ACCESS dans XCode?

Je suis nouveau dans le développement iPhone et le XCode en général et je ne sais pas comment commencer à dépanner un signal EXC_BAD_ACCESS. Comment puis-je obtenir que XCode se brise à la ligne exacte qui cause l'erreur?


Il semble que je n'arrive pas à faire arrêter XCode sur la ligne à l'origine du problème, mais je vois les lignes suivantes dans ma console de débogage:

Dim 25 oct 15:12:14 jasonsmacbook TestProject [1289]: CGContextSetStrokeColorWithColor: contexte invalide

Dim 25 oct 15:12:14 jasonsmacbook TestProject [1289]: CGContextSetLineWidth: contexte invalide

Dim 25 oct 15:12:14 jasonsmacbook TestProject [1289]: CGContextAddPath: contexte invalide

Dim 25 oct 15:12:14 jasonsmacbook TestProject [1289]: CGContextDrawPath: contexte invalide

2009-10-25 15: 12: 14.680 LanderTest [1289: 207] *** - [CFArray objectAtIndex:]: message envoyé à instance désallouée 0x3c4e610

Maintenant, j'essaie de dessiner dans le contexte que je récupère de UIGraphicsGetCurrentContext() et passe à l'objet avec lequel je veux dessiner.


Après des essais et des erreurs de débogage, j’ai trouvé qu’une NSMutableArray pour laquelle j’avais une propriété dans ma classe était un zombie. Je suis entré dans la fonction init pour la classe et voici le code que j'utilisais:

if ((self = [super init])) {
        NSMutableArray *array = [NSMutableArray array];
        self.terrainBlocks = array;
        [array release];
    }
    return self;    
}

J'ai supprimé la ligne [array release] et elle ne me donne plus le signal EXC_BAD_ACCESS, mais je ne comprends plus trop pourquoi cela fonctionne. Je pensais que lorsque j'utilisais la propriété, elle la conservait automatiquement pour moi, et que je devrais donc la libérer à partir de init afin d'éviter toute fuite. Je ne comprends vraiment pas comment cela fonctionne et tous les guides et les questions Stackoverflow que j'ai lus ne font que me confondre davantage avec la façon de définir les propriétés dans ma méthode init. Il ne semble pas y avoir de consensus sur la meilleure solution.

45
jasonh

Pour toute erreur EXC_BAD_ACCESS, vous essayez généralement d'envoyer un message à un objet validé. Le MEILLEUR moyen de les détecter est d'utiliser NSZombieEnabled .

Cela fonctionne en ne lâchant jamais réellement un objet, mais en l'enveloppant comme un "zombie" et en y mettant un drapeau indiquant qu'il aurait normalement été libéré. De cette façon, si vous essayez d'y accéder à nouveau, il sait toujours ce que c'était avant de commettre l'erreur, et avec ce peu d'informations, vous pouvez généralement revenir en arrière pour voir quel était le problème.

Cela est particulièrement utile dans les threads d'arrière-plan lorsque le débogueur recherche parfois des informations utiles.

TRES IMPORTANT DE NOTER Cependant, vous devez vous assurer à 100% que cela ne figure que dans votre code de débogage et non dans votre code de distribution. Parce que rien n'est jamais publié, votre application va fuir et fuite et fuite. Pour me rappeler de le faire, je mets ce journal dans mon appdélégant:

if(getenv("NSZombieEnabled") || getenv("NSAutoreleaseFreedObjectCheckEnabled"))
  NSLog(@"NSZombieEnabled/NSAutoreleaseFreedObjectCheckEnabled enabled!");

Si vous avez besoin d’aide pour trouver la ligne exacte, effectuez un Build-and-Debug (CMD-Y) au lieu d’un Build-and-Run (CMD-R). Lorsque l'application se bloque, le débogueur vous indiquera exactement quelle ligne et en combinaison avec NSZombieEnabled, vous devriez pouvoir savoir exactement pourquoi.

84
coneybeare

A propos de votre tableau. La ligne 

NSMutableArray *array = [NSMutableArray array];

ne vous donne pas réellement un objet retenu, mais plutôt un objet autorelease. Il est probablement conservé à la ligne suivante, mais vous ne devez pas le publier à la troisième ligne. Voir this

C'est la règle fondamentale:

Vous devenez propriétaire d'un objet si vous le créez à l'aide d'une méthode dont le nom commence par «alloc» ou «nouveau» ou contient «copie» (par exemple, alloc, newObject ou mutableCopy), ou si vous lui envoyez un message de rétention. Vous êtes responsable de la renonciation à la propriété des objets que vous possédez via release ou autorelease. À tout autre moment où vous recevez un objet, vous ne devez pas le libérer.

17
epatel

Dans Xcode 4, vous pouvez activer les zombies en cliquant sur la liste déroulante Schéma (en haut à gauche, à droite du bouton d'arrêt) -> Modifier le schéma -> onglet Diagnostics -> Activer les objets Zombie.

10
snez

Xcode/gdb se casse toujours sur EXC_BAD_ACCESS, il vous suffit de remonter la pile d’appels pour trouver le code qui l’a déclenchée. 

Notez que ces types d'erreur se produisent souvent avec les objets autoreleased, ce qui signifie que la cause ultime du problème ne se trouvera pas dans la pile d'appels ayant déclenché EXC_BAD_ACCESS. C'est alors que NSZombieEnabled et NSAutoreleaseFreedObjectCheckEnabled deviennent utiles.

7
Darren

Une nouvelle réponse à un ancien thread ... dans XCode 4, le moyen le plus efficace de diagnostiquer les exceptions EXC_BAD_ACCESS consiste à utiliser Instruments pour profiler votre application (dans XCode, cliquez sur Produit/Profil et choisissez Zombies). Cela vous aidera à identifier les messages envoyés aux objets désalloués.

5
Jonathan Moffatt

A partir des classes Stanford CS193P: si vous ajoutez un point d'arrêt (manuellement, en modifiant des points d'arrêt) pour le symboleobjc_exception_throw, vous obtiendrez une bien meilleure image de ce qui a mal tourné. bousiller la trace de la pile. Lorsque vous vous arrêtez dans objc_exception_throw, vous pouvez souvent regarder en arrière exactement quel accès/opération a causé votre problème.

2
Adam Eberbach

Une autre approche utile consiste à définir des points d'arrêt qui se déclencheront immédiatement après que l'exception se soit produite:

Ouvrez la fenêtre des points d'arrêt (Exécuter - Afficher - Points d'arrêt) et ajoutez deux points d'arrêt symboliques appelés «objc_exception_throw» et «[NSException raise]». 

De: http://blog.emmerinc.be/index.php/2009/03/19/break-on-exception-in-xcode/

2
Frank

Je voulais juste ajouter pour les autres qui viennent d'un site Web, à la recherche de solutions pour la même erreur, mais avec une erreur différente. Dans mon cas, j'ai eu la même erreur lorsque j'ai essayé d'instancier NSDictionary avec une faute de frappe dans le nom de la clé. J'avais oublié d'ajouter "@" devant ma clé:

NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys: myObj1, @"goodKey", myObj2, "badkey @ is missing in front", nil];
1
Centurion

J'espère que je n'ai pas manqué une réponse identique, mais j'ai découvert qu'il était possible pour certains projets de générer cette erreur en raison de l'exécution sur des simulateurs d'anciennes versions d'iOS pouvant être incompatibles avec une dépendance de projet ou un framework. Je poursuivais trop longtemps l'un de ces objectifs avant de réaliser que cela se produisait dans les versions du simulateur d'empilement, comme l'iPhone 4S, que l'application n'était même pas censée essayer de prendre en charge.

Cela aurait été bien d’avoir reçu un message d’erreur plus détaillé, mais j’imagine que c’est la responsabilité du cadre dans lequel il a été créé. comme je me suis retrouvé.

1
Thomas Allenbaugh

Avant d'activer les zombies, je vous recommande de vous débarrasser de tous les avertissements (si vous en avez). Des choses simples comme une fonction non vide sans return peuvent être à l'origine de cette erreur. Si vous n'avez pas d'avertissements, procédez comme indiqué dans les autres réponses.

0
user2387149