Search code examples
ioscore-dataconcurrencygrand-central-dispatchnsmanagedobjectcontext

Bad access when trying to clear and save different contexts


I need to perform periodic updates of the data I persist with Core Data. I get such data from asynchronous calls to REST services. To firstly retrieve all the data, I create a full core data stack in a private queue and then I do this:

- (void)updateDataFromServices
{
   [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
   self.dataUpdatePrivateContext = [MyCoreDataStackHelper getPrivateContext];

   if (self.dataUpdatePrivateContext != nil) {
       [self.dataUpdatePrivateContext performBlockAndWait: ^{
           // Asynchronous operations
           [self callService1];
           [self callService2];
           [self callService3];
       }];
   }
}

In the callback of each service that is called, I check that the rest of services have already finished too, and if all have finished I call a method (manageInfoUpdate) to handle updates between the data I have in my main context (in main thread) and the data I now have in the private context (in the private queue):

- (void)manageInfoUpdate
{
   const char* UpdateInfoQueue = "com.comp.myapp.updateinfo";
   dispatch_queue_t queue = dispatch_queue_create(UpdateInfoQueue, NULL);
   dispatch_async(queue,^{

    // Handle updates from private context:
    // Here I compare objects in the main context with the objects
    // in the private context and I delete objects from both
    // by calling:

    [mainContext deleteObject:object];
    [self.dataUpdatePrivateContext deleteObject:object];
    // This seems to work...

    // Save and clear private context
    [self saveContext:self.dataUpdatePrivateContext];
    [self clearContext:self.dataUpdatePrivateContext];

    dispatch_async(dispatch_get_main_queue(), ^{
        // Re-fetch from main context to get
        // the updated data

        // Save main context
        [self saveContext:mainContext];

        // Notify end of updates
    });
});
}

I try to perform the manageInfoUpdate operations in another async thread. I'm getting EXEC_BAD_ACCESS exceptions when trying to clear / save the contexts... Could somebody help me to find why?

Thanks in advance


Solution

  • You won't get an outright "error" for using Core Data in a multi-threaded environment incorrectly. The app just will crash, sometimes.

    To confirm you are using it correctly, turn on the debug flag com.apple.CoreData.ConcurrencyDebug 1 in your runtime arguments. Then it will crash EVERY time you touch a MOC or MO from the wrong queue.

    As it stands you code is not correct at all with regard to threading. A MO that is created on one queue can only be accessed from that queue. Likewise, a MOC that is configured for the main queue must be accessed on the main queue and a MOC configured as a private queue must be accessed on ITS private queue.

    Your "UpdateInfoQueue" is violating the threading rules completely.

    Turn on the debug flag, correct the errors it shows you and your save issue will be corrected.