Search code examples
iosobjective-cmultithreadingcore-datamagicalrecord

Core Data determining where save and fetch operations conflict


tldr: during coredata fetch my app crashes with error: 'collection mutated while enumerated' and I want to figure out the coredata save that's mutating the data and therefore causing this crash.

I'm performing 100 fetch operation on Core Data using Magical Records. These fetch operations are performed in the background thread. While these fetches are running, somewhere in my app I perform a save. Now, apparently when this save happens my app crashes because it changes the data in the other ManagedObjectContext.

I can't find the location of the save because it's being run from a different thread, so the farthest the backtrace goes is "developer submitted block to developerSubmittedBlockToNSManagedObjectContextPerform

First of all, how could a change from another context on another thread cause me to crash while I'm performing a fetch with a separate context?

How can I figure out where this save is?

How can I fix this issue?

How do I debug the app for other instances of this situation that aren't that obvious?

This is how I'm performing 100 fetch requests:

- (instancetype) initWithUserSession:(BPDUserSession*)userSession{
    ...
    self.context = [NSManagedObjectContext MR_context]; // new context, root is parent, private type
    [self buildAndFetchFRCsInContext:self.context];
    ...
}

- (void) buildAndFetchFRCsInContext:(NSManagedObjectContext*)context {
self.contactsFRC = [self buildFetchResultsControllerForClass:[Contact class] sortedBy:@"id" withPredicate:nil inContext:context];
self.callsFRC = [self buildFetchResultsControllerForClass:[Call class] sortedBy:@"id" withPredicate:nil inContext:context];
self.newsItemsFRC = [self buildFetchResultsControllerForClass:[NewsItem class] sortedBy:@"id" withPredicate:nil inContext:context];

NSMutableArray *list = [[NSMutableArray alloc] initWithCapacity:100];
for (int i = 0; i < 100; i++) {
    list[i] = [self buildFetchResultsControllerForClass:[NewsItem class] sortedBy:@"id" withPredicate:nil inContext:context];
}

[context performBlock:^{
    __unused NSDate* start = [NSDate date];

    NSError* error;

    // Peform the fetches
    [self.contactsFRC performFetch:&error];
    [self.callsFRC performFetch:&error];
    [self.newsItemsFRC performFetch:&error];

    for (int i = 0; i < list.count; i++) {
        [list[i] performFetch:&error];
    }

    NSLog(@"Spent [%@s] performing fetchs for counts!", @(fabs([start timeIntervalSinceNow])));

    [self calculateAndBroadcastCounts];
}];
}

In general I'm having a lot of trouble debugging Core Data and there doesn't seem to be any great tool or resource I can utilize, does anyone have any resources for me to look through?


Solution

  • The issue was I was saving objects from a context that wasn't the context they were created on. so when I run fetch here it sometimes conflicts with the save call and we have a crash.