Z S
Z S

Reputation: 7493

Getting NSGenericException with reason: '*** Collection <NSConcreteHashTable: 0x282c34140> was mutated while being enumerated.'

I get this NSGenericException with reason Collection <NSConcreteHashTable: 0x282c34140> was mutated while being enumerated even though I'm not actually use fast-enumeration in my code.

This crash gets triggered when the NSFetchedResultsController delegate gets called as a property in my Core Data object gets updated, which ends up calling this method:

- (void)mutateCoordinatesOfClashingAnnotations:(NSArray *)annotations {

    NSDictionary *coordinateValuesToAnnotations = [self groupAnnotationsByLocationValue:annotations]; // returns a dictionary with key: coordinate (saved as NSValue) -> NSMutableArray of MKAnnotation objects

    for (int i=0; i<coordinateValuesToAnnotations.allKeys.count; i++) {
        NSValue *coordinateValue = coordinateValuesToAnnotations.allKeys[i];
        NSMutableArray *outletsAtLocation = coordinateValuesToAnnotations[coordinateValue];

        [self repositionAnnotations:outletsAtLocation];
    }
}

- (void)repositionAnnotations:(NSMutableArray *)annotations {

    for (int i = 0; i < annotations.count; i++) {

        double heading = radiansBetweenAnnotations * i;
        CLLocationCoordinate2D newCoordinate = [self calculateNewCoordinate];

        id <MKAnnotation> annotation = annotations[i];
        annotation.coordinate = newCoordinate; // this is where it crashes
    }
}

Basically, I'm trying to change the 'coordinate' value for a MKAnnotation object (which is actually a CoreData NSManagedObject as well), which ends up crashing. Strangely, I get the 'collection was mutated while being enumerated' error, even though there isn't any 'fast enumeration' going on in the code above. I mean, I'm changing one property of an object inside a mutable array, which itself is part of a dictionary.

Unfortunately, since the 'annotation' is a NSManagedObject, I can't just 'copy' it from the array and then change the copied value and replace the object inside the array.

The crash report looks like this:

*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <NSConcreteHashTable: 0x282c34140> was mutated while being enumerated.'

Last Exception Backtrace:
0   CoreFoundation                       0x00000001c058696c __exceptionPreprocess + 224
1   libobjc.A.dylib                      0x00000001c029f028 objc_exception_throw + 56
2   CoreFoundation                       0x00000001c05862d4 __NSFastEnumerationMutationHandler + 120
3   libobjc.A.dylib                      0x00000001c02b21a4 objc_enumerationMutation + 28
4   Foundation                           0x00000001c08706a8 -[NSConcreteHashTable countByEnumeratingWithState:objects:count:] + 72
5   MapKit                               0x00000001ce114b64 -[MKAnnotationManager observeValueForKeyPath:ofObject:change:context:] + 804
6   Foundation                           0x00000001c093bf14 NSKeyValueNotifyObserver + 288
7   Foundation                           0x00000001c093e01c NSKeyValueDidChange + 332
8   Foundation                           0x00000001c093b740 NSKeyValueDidChangeWithPerThreadPendingNotifications + 148
9   CoreData                             0x00000001c5006e38 _PF_ManagedObject_DidChangeValueForKey + 76
10  Foundation                           0x00000001c093784c NSKVOForwardInvocation + 336
11  CoreFoundation                       0x00000001c058adc0 ___forwarding___ + 676
12  CoreFoundation                       0x00000001c058d3a0 _CF_forwarding_prep_0 + 92
13  CJournal                             0x00000001049fc930 -[MapViewShared repositionAnnotations:] (MapViewShared.m:656)
14  CJournal                             0x00000001049fc580 -[MapViewShared mutateCoordinatesOfClashingAnnotations:] (MapViewShared.m:616)
15  CJournal                             0x0000000104a1fa98 -[MapViewController controllerDidChangeContent:] (MapViewController.m:1104)
16  CoreData                             0x00000001c50572a4 __82-[NSFetchedResultsController(PrivateMethods) _core_managedObjectContextDidChange:]_block_invoke + 5868
17  CoreData                             0x00000001c5015aac developerSubmittedBlockToNSManagedObjectContextPerform + 160
18  CoreData                             0x00000001c4ee01c4 -[NSManagedObjectContext performBlockAndWait:] + 208
19  CoreData                             0x00000001c4ee981c -[NSFetchedResultsController(PrivateMethods) _core_managedObjectContextDidChange:] + 120
20  Foundation                           0x00000001c095106c __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_2 + 24
21  CoreFoundation                       0x00000001c04de99c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 24
22  CoreFoundation                       0x00000001c04de9ec ___CFXRegistrationPost1_block_invoke + 64
23  CoreFoundation                       0x00000001c04ddce4 _CFXRegistrationPost1 + 392
24  CoreFoundation                       0x00000001c04dd97c ___CFXNotificationPost_block_invoke + 104
25  CoreFoundation                       0x00000001c0456910 -[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1420
26  CoreFoundation                       0x00000001c04dd2ac _CFXNotificationPost + 1264
27  Foundation                           0x00000001c083fbfc -[NSNotificationCenter postNotificationName:object:userInfo:] + 60
28  CoreData                             0x00000001c4eed264 -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 512
29  CoreData                             0x00000001c4ee95fc -[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:deletions:updates:refreshes:deferrals:wasMerge:] + 1244
30  CoreData                             0x00000001c4ee886c -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 980
31  CoreData                             0x00000001c501b48c _performRunLoopAction + 376
32  CoreFoundation                       0x00000001c0501524 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
33  CoreFoundation                       0x00000001c04fc1c4 __CFRunLoopDoObservers + 416
34  CoreFoundation                       0x00000001c04fc774 __CFRunLoopRun + 1288
35  CoreFoundation                       0x00000001c04fbf40 CFRunLoopRunSpecific + 476
36  GraphicsServices                     0x00000001ca779534 GSEventRunModal + 104
37  UIKitCore                            0x00000001c4674a60 UIApplicationMain + 1936
38  CJournal                             0x00000001048d8c44 main (main.m:20)
39  libdyld.dylib                        0x00000001c037ae18 start + 0

Can someone help me find the problem here? What 'enumeration' am I using that I can't see? Or is there something else going on?

Upvotes: 3

Views: 1738

Answers (1)

malhal
malhal

Reputation: 30781

It looks like when you change the coordinate the MKAnnotationManager observes the change and then counts its hash table by enumerating, and perhaps you are already enumerating the annotations.

In any case, you shouldn't add a model object to the view, that breaks MVC. It is the controllers job to take the model and format it for the view, via the annotation object. One approach is to create MKPointAnnotations from your fetchedObjects (or create a MKPointAnnotation subclass and init it with your model object storing it in a weak property) and add those to the map. In the FRC's didChangeObject you can update the annotation from the model object. The map's annotation view is doing KVO on the annotation's coordinate so it'll update its position on the map automatically. Create a category on your model object (see docs) to store the lazy loaded annotation (using objc_setAssociatedObject).

Upvotes: 1

Related Questions