views:

858

answers:

5

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?

+6  A: 

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.

Kevin Ballard
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
A: 

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.

unforgiven
I know the first part, and the fast enumeration is happening on Apple's side, not mine.
Eli
A: 

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
+1  A: 

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
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
A: 

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