J'ai une application SDK pour iPhone comportant plusieurs vues qui apparaissent et disparaissent lorsque l'utilisateur crée du contenu. Après avoir utilisé l'application sur un appareil pendant un certain temps, j'obtiens le blocage suivant:
Program received signal: “EXC_BAD_ACCESS”.
(gdb) backtrace
#0 0x33369ebc in objc_msgSend ()
#1 0x320e5248 in -[UIScrollView(UIScrollViewInternal) _scrollViewAnimationEnded] ()
#2 0x338b4a14 in -[NSObject performSelector:withObject:] ()
#3 0x320e5098 in -[UIAnimator stopAnimation:] ()
#4 0x320e4b7c in -[UIAnimator(Static) _advance:] ()
#5 0x320e4a34 in LCDHeartbeatCallback ()
#6 0x34350e60 in HeartbeatVBLCallback ()
#7 0x332e91c0 in IOMobileFramebufferNotifyFunc ()
#8 0x316532f8 in ?? ()
#9 0x33866b50 in __CFMachPortPerform ()
#10 0x338ae52a in CFRunLoopRunSpecific ()
#11 0x338adc1e in CFRunLoopRunInMode ()
#12 0x3434e1c8 in GSEventRunModal ()
#13 0x32002c30 in -[UIApplication _run] ()
#14 0x32001230 in UIApplicationMain ()
#15 0x00002ff8 in main (argc=1, argv=0x2ffff550) at /Developer/svn/MyCompany/iPhone/MyApplication/Other Sources/main.m:14
Comme vous pouvez le voir sur la trace, la seule mention de mon code dans cet appel est l'appel à main.
J'ai exécuté Build and Analyze à partir de Xcode et je l'ai également configuré pour exécuter l'analyseur clang sur mon projet à partir du terminal. Ces deux problèmes ne détectent aucun problème dans le code. J'utilise une version très récente du SDK iOS (je n'ai pas encore téléchargé la version 4.1, mais celle que j'utilise est celle qui était dans la version juste avant la version 4.1).
En outre, j'ai exécuté l'application dans Instruments avec le simulateur et l'application ne présente aucune fuite de mémoire.
Je suis sur le point d'essayer d'utiliser la variable NSZombieEnabled
et de voir si cela trouve quelque chose, mais le problème est que je dois utiliser l'application pendant 30 à 40 minutes environ avant qu'elle ne plante, et je soupçonne que NSZombieEnabled
ne m'aide peut-être même pas à trouver le problème.
Il semble que les incidents que j'ai vus surviennent lorsqu'une vue modale appelle un délégué dans le contrôleur de vue parent. Le contrôleur de vue parent effectue ensuite certains traitements avant de fermer le contrôleur de vue modal. Il y a quelques références dans le blocage à des animations et des vues de défilement, mais je ne suis pas sûr de ce que je pourrais faire pour leur causer des problèmes. Quelqu'un a-t-il des suggestions quant aux choses à rechercher?
EDIT: J'ai mis le drapeau NSZombieEnabled
dans l'application, et sur l'appareil, ce message s'affiche avec le message suivant dans la console:
2010-09-11 17:10:33.970 MyApplication[9321:207] ***
-[MyViewController respondsToSelector:]: message
sent to deallocated instance 0x7489480
Autant que je sache, je règle les délégués utilisés dans l'application sur nil dans les deallocs de toutes mes classes, de sorte que je ne sais pas trop où chercher.
J'ai essayé d'utiliser la commande malloc_history
pid address sur ceci, mais il a dit qu'il ne pouvait pas trouver le processus, j'ai essayé 9321, 9321: 207 et 207. De plus, si j'essaie d'utiliser la variable MallocStackLogging
, le programme ne s'exécutera pas. Sur le périphérique, je reçois un tas de malloc:
incapables de créer des messages de répertoire de journal de pile dans la console et un programme plante.
Oh, et d'ailleurs, je ne peux pas utiliser la vérification des zombies dans Instruments, car cela ne semble pas fonctionner avec un appareil et je ne peux pas obtenir le même crash dans le simulateur.
La variable UIScrollView
sur la pile # 1 veut probablement informer son délégué de la fin de l'animation, mais le délégué est parti à ce stade. Régler NSZombieEnabled
le confirmerait probablement.
Les délégués ne sont pas conservés. Il s'agit donc d'une erreur courante dans Cocoa et Cocoa Touch. Recherchez des délégués sur UIScrollView
ou UITableView
dans votre code et essayez de trouver celui qui pourrait être publié avant l'heure.
Je viens de résoudre ce problème moi-même.
J'ai eu un problème où:
Le problème était que les messages des délégués scrollview étaient déclenchés sur un objet nouvellement désalloué, et que les journaux d'incidents étaient un peu déroutants car ils indiquaient des références d'objet non sensuelles.
Le correctif consistait à définir le délégué scrollview sur nil en tant que première ligne de la méthode dealloc de mon contrôleur de vue.
J'espère que ceci aide quelqu'un d'autre!
Pour compléter, j’ajoute cette trace de pile (iOS 6) à ceux qui risquent de rencontrer le même problème, mais avec une implémentation légèrement différente et les étapes exactes pour reproduire le problème.
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x71f05631
Crashed Thread: 0
Thread 0 name: Dispatch queue: com.Apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x3919b5d0 objc_msgSend + 1
1 UIKit 0x33421830 -[UIScrollView(UIScrollViewInternal) _delegateScrollViewAnimationEnded] + 48
2 UIKit 0x334217ba -[UIScrollView(UIScrollViewInternal) _scrollViewAnimationEnded:finished:] + 130
3 UIKit 0x334216a4 -[UIAnimator stopAnimation:] + 460
Cela se produit sur iOS 6 et a commencé à se produire lorsque j'ai implémenté la méthode UIScrollViewDelegate:
" -(void)scrollViewDidEndDecelerating:(UITableView *)tableView"
and made a call to:
"[tableView scrollToRowAtIndexPath: indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];".
Le problème est survenu lorsque l'animation a démarré et que j'ai appuyé sur le bouton "Précédent" et que mon contrôleur de vue a été supprimé avant la fin de l'animation.
Lors de la reproduction, vous devez être sûr d'appuyer sur le bouton "Retour" après le début de l'animation, mais avant sa fin. Il m'a fallu quelques essais. J'ai essayé de recréer le problème en supprimant par programmation le contrôleur de vue, mais je n'ai pas pu le reproduire. Je devais utiliser le bouton "Retour". J'appelais simplement [myTableView release] dans le dealloc. La solution était la suivante pour définir ces deux propriétés sur nil:
self.myTableView.delegate = nil;
self.myTableView = nil;
Au début, délégués devrait être de faible/assign type. Mais, dans ce cas, il existe un obstacle très commun Animé par des animations de défilement. Si vous utilisez des modifications de décalage de contenu animées pour Votre ScrollViews vous devez absolument définir son délégué à nil à dealloc méthode.
Sinon, vous obtiendrez ce qui suit
[YourViewController respondsToSelector:]: message sent to deallocated instance
L'exemple très commun:
1. _tableView is ivar of YourViewController
2. _tableView.delegate = self;
3. - (void)scrollViewDidScroll:(UIScrollView *)scrollView is implemented at YourViewController
4. at some point you call [_tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionBottom animated:YES];
or [_tableView setContentOffset:CGPoint animated:YES]
and try to close YourViewController
La _tableView est conservée par CoreAnimation, mais YourViewController est désalloué!
J'imagine que le délégué de scrollview est défini sur un objet désalloué. Essayez de définir tous les délégués des objets enfants sur nil dans vos méthodes dealloc.
Cela peut arriver si vous avez inséré un contrôleur de rafraîchissement dans une vue sous forme de sous-vue (mon conseil, ne faites jamais cela) ...
Tout ce qui précède n'a pas résolu mon problème, j'ai donc creusé à nouveau mon code. J'ai reconnu que le blocage apparaissait lorsque j'utilisais le clavier et UICollectionView animation (ouais, c'est une discussion en ligne) et que je rejetais le contrôleur actuel.
L'application se bloque parce que j'essaie de faire défiler dans le bloc d'achèvement de l'animation :)
Il suffit de le couper et tout fonctionne bien maintenant!
Bonne codage et débogage :)
Après avoir affronté le même problème, j'ai défini:
self.collectionView.delegate = nil;
dans - (void)viewDidLoad
(avant de définir ViewController en tant que délégué collectionView) et -(void)viewWillDisappear:(BOOL)animated
Tout fonctionne bien maintenant.
Merci pour l'aide.
J'ai vu un tel comportement lorsque j'appelle scrollToRowAtIndexPath sans indexPath