Ios – collection was mutated while being enumerated with coredata and multithreading

core-dataiosipadmultithreadingobjective c

I am trying to debug an ipad application I have just inherited from some other developers. At the moment I'm getting a really odd error that I'm not sure how to attack.

The app dies with this error:
* Terminating app due to uncaught exception 'NSGenericException', reason: '* Collection <__NSCFSet: 0xc5d2b40> was mutated while being enumerated.'

When I do po 0xc5d2b40, it tells me that its an array of a certain object type. However when I turn on breakpoints at all exceptions and go through the backtrace, there is no sign of an array of this object type being either enumerated or mutated.

I have seen a few references that suggest that this could be related to coredata and multithreading, but I'm wondering if anyone has any suggestions on how to approach this problem. I should also note: I ONLY get this error on the device, NOT in the simulator.

Thanks in advance!

Note: When the app hits the breakpoint from the objc_exception_throw, using the bt command I get the following results from the various background threads:

Most threads look like this:
tid = 0x2c03, 0x307e9cd4 libsystem_kernel.dylib__workq_kernreturn + 8
frame #0: 0x307e9cd4 libsystem_kernel.dylib
__workq_kernreturn + 8
frame #1: 0x32d35f3c libsystem_c.dylib_pthread_wqthread + 616
frame #2: 0x32d35cd0 libsystem_c.dylib
start_wqthread + 8

I have one thread which is running a method in the AFURLConnectionOperation (external library: AFNetworking).

There is a single thread with my code in the backtrace, which is what I referenced above. I'm a bit new to the debugger, am I potentially doing something wrong in checking my backtraces?

The backtrace on the thread where all my code is executing looks like this:

tid = 0x2003, 0x32d2fcac libsystem_c.dylib`OSSpinLockLock$VARIANT$wfe + 84
frame #0: 0x32d2fcac libsystem_c.dylib`OSSpinLockLock$VARIANT$wfe + 84
frame #1: 0x32eb7c88 libobjc.A.dylib`_objc_rootReleaseWasZero_slow + 28
frame #2: 0x32eb5210 libobjc.A.dylib`_objc_rootReleaseWasZero + 132
frame #3: 0x32eb515c libobjc.A.dylib`_objc_rootRelease + 12
frame #4: 0x31ba6b0a CoreData`-[NSSQLIntermediate _generateSQLForKeyPathExpression:allowToMany:inContext:] + 278
frame #5: 0x31ba687a CoreData`-[NSSQLIntermediate _generateSQLForExpression:allowToMany:inContext:] + 186
frame #6: 0x31babea4 CoreData`-[NSSQLSimpleWhereIntermediate _generateSQLType2InContext:] + 660
frame #7: 0x31ba6260 CoreData`-[NSSQLSimpleWhereIntermediate generateSQLStringInContext:] + 400
frame #8: 0x31bbbd06 CoreData`-[NSSQLCompoundWhereIntermediate _generateMulticlauseStringInContext:] + 174
frame #9: 0x31bbba84 CoreData`-[NSSQLCompoundWhereIntermediate generateSQLStringInContext:] + 272
frame #10: 0x31ba5d1a CoreData`-[NSSQLFetchIntermediate generateSQLStringInContext:] + 90
frame #11: 0x31ba24d0 CoreData`-[NSSQLGenerator newSQLStatementForFetchRequest:ignoreInheritance:countOnly:nestingLevel:] + 460
frame #12: 0x31ba2212 CoreData`-[NSSQLAdapter _newSelectStatementWithFetchRequest:ignoreInheritance:] + 414
frame #13: 0x31ba1e6c CoreData`-[NSSQLCore newRowsForFetchPlan:] + 144
frame #14: 0x31ba152e CoreData`-[NSSQLCore objectsForFetchRequest:inContext:] + 698
frame #15: 0x31ba0fc6 CoreData`-[NSSQLCore executeRequest:withContext:error:] + 282
frame #16: 0x31ba0464 CoreData`-[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 1432
frame #17: 0x31b9ec28 CoreData`-[NSManagedObjectContext executeFetchRequest:error:] + 604
frame #18: 0x00052956 digidrill`-[DigidrillIncrementalCache executeFetchRequest:allNewerThanOrFailure:] + 410 at DigidrillIncrementalCache.m:80
frame #19: 0x0003fbae digidrill`-[DigidrillIncrementalStore(Responders) immutableCacheListResponderBegin:intoArray:inContext:outErr:] + 434 at DigidrillIncrementalStore+Responders.m:253
frame #20: 0x00015ea4 digidrill`-[DigidrillIncrementalStore executeRequest:withContext:error:] + 3836 at DigidrillIncrementalStore.m:157
frame #21: 0x31ba0464 CoreData`-[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 1432
frame #22: 0x31b9ec28 CoreData`-[NSManagedObjectContext executeFetchRequest:error:] + 604
frame #23: 0x00018534 digidrill`+[CoreDataExtractor executeSyncFetchRequest:callback:onDispatchQueue:] + 704 at CoreDataExtractor.m:175
frame #24: 0x0001a9a2 digidrill`+[CoreDataExtractor ensureGammaDataExistsForTrack:maxOld:flags:outErr:] + 1234 at CoreDataExtractor.m:363
frame #25: 0x00058670 digidrill`-[XYGraph dataForTrackPlot:] + 632 at XYGraph.m:230
frame #26: 0x0006f7d6 digidrill`__23-[TrackPlot reloadData]_block_invoke_0 + 66 at TrackPlot.m:49
frame #27: 0x344b0c58 libdispatch.dylib`_dispatch_call_block_and_release + 12
frame #28: 0x344b2d0e libdispatch.dylib`_dispatch_queue_drain + 274
frame #29: 0x344b2b74 libdispatch.dylib`_dispatch_queue_invoke$VARIANT$mp + 40
frame #30: 0x344b37e6 libdispatch.dylib`_dispatch_worker_thread2 + 210
frame #31: 0x32d35dfa libsystem_c.dylib`_pthread_wqthread + 294
frame #32: 0x32d35cd0 libsystem_c.dylib`start_wqthread + 8

Best Answer

This guy has a pretty good explanation that solved it for me:

http://www.pixeldock.com/blog/collection-was-mutated-while-being-enumerated-during-coredata-operation-on-background-thread/

Basically the MO context can only be used by the thread on which it was created.

Related Topic