3

This is really odd...

I run my app, and while it is opening and the views are constructing I get:

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

The code trace goes through the following:

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

I've run the game lots and lots and lots of times and I've never seen this, then suddenly it popped up. The weird thing is that I'm not creating any other threads (that I know of) until after this code all gets called. It'll be easier for me to debug this if someone can give me some explanation of what might be getting modified while it's being accessed in a UIView. Does it have something to do with adding something to the view while it's already adding something, maybe? Any ideas?

Eli
  • 4,874
  • 6
  • 41
  • 50
  • Can you elaborate on what is different in the apps behavior when you get the error? Crash… no view… etc... – coneybeare Nov 10 '09 at 23:54
  • Can you post some code? The simple act of calling -addSubview: cannot cause this problem, and without code we can only guess blindly. – Lily Ballard Nov 10 '09 at 23:57

5 Answers5

17

Is it possible that your view has a sublayer where you assigned the delegate to be the view? This would ordinarily cause the view to recurse infinitely (well, until it hit the stack limit) when calling _makeSubtreePerformSelector:withObject:withObject:copySublayers:, but I suppose it's possible that whatever it's trying to do right here involves mutation.

The reason this is so is because UIView assumes that if a CALayer's delegate is a UIView, then that CALayer belongs to the UIView and the UIView is part of the hierarchy. However if you create your own CALayer and assign the delegate to be the UIView, the UIView will end up calling itself as part of the recursion.

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • 1
    Definitely a good response, although if what you say was the case, then this would happen every single time. As it is, it only happened once out of hundreds or thousands of runs. Hm. – Eli Nov 10 '09 at 22:56
  • 2
    Kevin's explanation of the UIView hierarchy re delegates helped me solve a tangential problem: I had added a CALayer as a view layer's sublayer and assigned the controller as delegate. Although upon viewDidUnload/dealloc I nullified/released the view before the sublayer, I was crashing on `_makeSubtreePerform...copySublayers`. The solution was to remove the sublayer *before* nullifying anything. Presumably this prevented the controller as delegate from calling the released sublayer. – Wienke May 12 '12 at 14:10
4

It is not a concurrency exception. Enumeration mutation exceptions happen when something (possibly inside a loop in the same thread) changes the array/set/dictionary that the loop is iterating.

Since addSubview: is on the stack, my guess is that something is trying to remove one of the UIView's subviews during construction.

Are you overriding any methods that may be getting run? Like addSubview or possibly layoutSubviews? If any of these is removing subviews, then this would cause the problem.

Matt Gallagher
  • 14,858
  • 2
  • 41
  • 43
  • 2
    Brilliant, that was the problem, thank you! I thought it was a concurrency problem because typically in order to mutate while accessing you need multiple threads, but you're right that this is not always the case. – Eli Nov 11 '09 at 16:26
  • Totally accurate. I had the same problem. In my case the added view's viewController was actually messing up with the superview's layout. Thanks a lot. You save me hours of suffering this issue. – Pacu Jan 19 '11 at 03:27
0

From the documentation:

Enumeration is “safe”—the enumerator has a mutation guard so that if you attempt to modify the collection during enumeration, an exception is raised.

Basically, this means that you are not allowed to add/remove objects to a collection (say an array) while you are enumerating it using the fast enumeration introduced in Objective-C 2.0. because doing so will render the mutation guard invalid.

In your case, the collection is related to a view hierarchy. If you want to add and/or remove views to this particular collection, do not use fast enumeration.

Massimo Cafaro
  • 25,429
  • 15
  • 79
  • 93
  • I know the first part, and the fast enumeration is happening on Apple's side, not mine. – Eli Nov 10 '09 at 22:55
0

I've seen this happen within CALayers when you try to clear out a property during the CALayer's -dealloc method, only changing that property inadvertently alters a visible property of the layer. You could check and see if you are somehow doing something that changes the properties of one of your views during the -dealloc of that same view.

Brad Larson
  • 170,088
  • 45
  • 397
  • 571
0

I hit upon the same bug. I was mutating the view hierarchy on a non-main thread. Make sure all modifications to the view are performed on the main thread.

Sunil Gowda
  • 2,476
  • 2
  • 17
  • 13