2009-11-10 4 views
3

C'est vraiment bizarre ...iPhone interface utilisateur addSubview provoquant exception concurrency

je lance mon application, et pendant son ouverture et les vues sont en train de construire je reçois:

Collection <CALayerArray: 0x124650> was mutated while being enumerated. 

La trace de code passe par ce qui suit:

main 
UIApplicationMain 
-[UIApplication _run] 
CFRunLoopRunInMode 
CFRunLoopRunSpecific 
_UIApplicationHandleEvent 
-[UIApplication sendEvent:] 
-[UIApplication handleEvent:withNewEvent:] 
-[UIApplication _runWithURL:sourceBundleID:] 
-[UIApplication _performInitilizationWithURL:sourceBundleID:] 
-[AppDelegate applicationDidFinishLaunching:] 
+[Controller initializeController] //This is my own function 
    [window addSubview: pauseMenuController.view] //This is the last point of my code it goes through 
-[UIView(Hierarchy) addSubview:] 
-[UIView(Internal) _addSubview:positioned:relativeTo:] 
-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:] 
-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:] 
-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:] 
_NSFastEnumerationMutationHandler 
objc_exception_throw 

J'ai couru les lots de jeu et beaucoup, beaucoup de fois et je ne l'ai jamais vu cela, puis tout à coup, il surgit. La chose étrange est que je ne crée pas d'autres threads (que je connais) jusqu'à après ce code est appelé. Ce sera plus facile pour moi de déboguer ceci si quelqu'un peut me donner une explication de ce qui pourrait être modifié pendant qu'il est accédé dans un UIView. Cela a-t-il quelque chose à voir avec l'ajout de quelque chose à la vue alors qu'il ajoute déjà quelque chose, peut-être? Des idées?

+0

Pouvez-vous élaborer sur ce qui est différent dans le comportement des applications lorsque vous obtenez l'erreur? Crash ... pas de vue ... etc ... – coneybeare

+0

Pouvez-vous poster du code? Le simple fait d'appeler -addSubview: ne peut pas causer ce problème, et sans code, nous ne pouvons que deviner aveuglément. –

Répondre

4

Ce n'est pas une exception de concurrence. Les exceptions de mutation d'énumération se produisent lorsque quelque chose (éventuellement à l'intérieur d'une boucle dans le même thread) change le tableau/ensemble/dictionnaire que la boucle est en train d'itérer.

Étant donné que addSubview: est sur la pile, je suppose que quelque chose essaie de supprimer l'une des sous-vues de UIView pendant la construction.

Êtes-vous en train de surcharger les méthodes qui peuvent être lancées? Comme addSubview ou éventuellement layoutSubviews? Si l'un d'entre eux supprime des sous-vues, cela entraînera le problème.

+2

Brillant, c'était le problème, merci! Je pensais que c'était un problème de concurrence, car généralement, afin de muter tout en accédant, vous avez besoin de plusieurs threads, mais vous avez raison que ce n'est pas toujours le cas. – Eli

+0

Totalement précis. J'ai eu le même problème. Dans mon cas, viewController de la vue ajoutée était en fait en train de bousiller la mise en page de la vue d'ensemble. Merci beaucoup. Vous me sauvez des heures de souffrir de ce problème. – Pacu

16

Est-il possible que votre vue possède une sous-couche dans laquelle vous avez affecté le délégué à la vue? Cela entraînerait normalement la vue à se récurser infiniment (bien, jusqu'à ce qu'il atteigne la limite de la pile) lors de l'appel _makeSubtreePerformSelector:withObject:withObject:copySublayers:, mais je suppose qu'il est possible que tout ce qu'il essaie de faire ici implique une mutation. La raison en est que UIView suppose que si le délégué d'un CALayer est un UIView, alors ce CALayer appartient à l'UIView et l'UIView fait partie de la hiérarchie. Toutefois, si vous créez votre propre CALayer et affectez le délégué à UIView, l'UIView finira par s'appeler lui-même dans le cadre de la récursivité.

+0

Certainement une bonne réponse, bien que si ce que vous dites était le cas, alors cela arriverait à chaque fois. Comme c'est le cas, cela ne s'est produit qu'une fois sur des centaines ou des milliers de courses. Hm. – Eli

+1

L'explication de Kevin de la hiérarchie UIView sur les délégués m'a aidé à résoudre un problème tangentiel: j'avais ajouté un CALayer comme sous-couche d'une couche de vue et assigné le contrôleur en tant que délégué. Bien que lors de viewDidUnload/dealloc j'ai annulé/libéré la vue avant le sous-calque, je plantais sur '_makeSubtreePerform ... copySublayers'. La solution consistait à supprimer la sous-couche * avant * d'annuler quoi que ce soit. Cela a vraisemblablement empêché le contrôleur en tant que délégué d'appeler la sous-couche libérée. – Wienke

0

De la documentation:

Enumeration est « sûr » -le recenseur a un garde de mutation de sorte que si vous essayez de modifier la collection lors de l'énumération, une exception est levée. Fondamentalement, cela signifie que vous n'êtes pas autorisé à ajouter/supprimer des objets à une collection (disons un tableau) pendant que vous l'énumérez en utilisant l'énumération rapide introduite dans Objective-C 2.0. car cela rendrait le gardien de mutation invalide.

Dans votre cas, la collection est liée à une hiérarchie de vues. Si vous souhaitez ajouter et/ou supprimer des vues à cette collection particulière, n'utilisez pas l'énumération rapide.

+0

Je connais la première partie, et l'énumération rapide se passe du côté d'Apple, pas le mien. – Eli

0

J'ai vu cela se produire dans CALayers lorsque vous essayez d'effacer une propriété pendant la méthode -dealloc de CALayer, seule la modification de cette propriété altère par inadvertance une propriété visible de la couche. Vous pouvez vérifier et voir si vous faites quelque chose qui change les propriétés de l'une de vos vues pendant le -dealloc de cette même vue.

0

J'ai rencontré le même bug. Je faisais muter la hiérarchie de vue sur un thread non principal. Assurez-vous que toutes les modifications apportées à la vue sont effectuées sur le thread principal.