Search code examples
iosmultithreadingcore-dataconcurrencynsmanagedobjectcontext

Illegal access to managed object context in main thread, why?


I've enabled com.apple.CoreData.ConcurrencyDebug 1 to check Core Data concurrency errors. I have the following code snippet in a Swift class:

lazy var context: NSManagedObjectContext! = {
    var appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    return appDelegate.managedObjectContext!
}()

func getAllEntitiesToRootContext() -> [MyEntity]? {
    let fetchRequest = NSFetchRequest(entityName:"MyEntity")

    do {
        let fetchedResults = try context.executeFetchRequest(fetchRequest) as! [MyEntity]

        if fetchedResults.count > 0 {
            return fetchedResults
        } else {
            return nil
        }
    } catch let error as NSError {
        print("could not fetch \(error), \(error.userInfo)")
        return nil
    }
}

If I've correctly understood, the context I get from AppDelegate is associated to main thread, right?

But then, from another Objective-C class I have, I do:

self.myEntitiesArray = [mySwiftClass getAllEntitiesToRootContext];

and I get this error log:

CoreData: error: The current thread is not the recognized owner of this NSManagedObjectContext(0x1a25f8a0). Illegal access during executeFetchRequest:error:

I don't understand why... I'm supposed to have such context associated to main thread, and I'm calling getAllEntitiesToRootContext from main thread...

Please I need help. Thanks in advance

EDIT: These are the methods related to Core Data in AppDelegate:

- (NSManagedObjectContext *)managedObjectContext
{
   // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
   if (_managedObjectContext != nil) {
       return _managedObjectContext;
   }

   NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
   if (!coordinator) {
       return nil;
   }
   _managedObjectContext = [[NSManagedObjectContext alloc] init];
   [_managedObjectContext setPersistentStoreCoordinator:coordinator];
   return _managedObjectContext;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
   // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it.
   if (_persistentStoreCoordinator != nil) {
       return _persistentStoreCoordinator;
   }

   // Create the coordinator and store

   _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
   NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"MyApp.sqlite"];
   NSError *error = nil;
   NSString *failureReason = @"There was an error creating or loading the application's saved data.";
   if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
       // Report any error we got.
       NSMutableDictionary *dict = [NSMutableDictionary dictionary];
       dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
       dict[NSLocalizedFailureReasonErrorKey] = failureReason;
       dict[NSUnderlyingErrorKey] = error;
       error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
       // Replace this with code to handle the error appropriately.
       // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
       NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
       abort();
   }

   return _persistentStoreCoordinator;
}

- (NSManagedObjectModel *)managedObjectModel
{
   // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
   if (_managedObjectModel != nil) {
      return _managedObjectModel;
   }
   NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyApp" withExtension:@"momd"];
  _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
 return _managedObjectModel;
}

EDIT 2: I'm using Xcode 7 and testing in iOS 9 device.

EDIT 3: If I disable com.apple.CoreData.ConcurrencyDebug 1, I get objects from getAllEntitiesToRootContext()... I don't really understand anything, why is this happening?

EDIT 4: I've made some tests. If I do this from an Objective-C class:

- (void)getEntities
{
   AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
   NSManagedObjectContext *mainContext = appDelegate.managedObjectContext;

   NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"MyEntity"];
   NSArray *entities = [mainContext executeFetchRequest:fetchRequest error:nil];

   for (NSManagedObject *item in entities) {
       NSLog(@"Name: %@", ((MyEntity *)item).name);
   }
}

I sometimes get no error when calling executeFetchRequest and the name of the entities is shown in logs console. Other times I also get the Core Data error similar that the one I posted above and I also get when doing as I was doing:

- (NSArray *)getEntities
{
   MyEntityDao *myEntityDao = [[MyEntityDao alloc] init];
   self.myEntities = [[myEntityDao getAllEntitiesToRootContext] mutableCopy];

   return [[NSArray arrayWithArray:self.myEntities] copy];
}

where MyEntityDao is the Swift class that defines lazy var context and getAllEntitiesToRootContext(), I get the Core Data error I also posted above... why? Are not these two code snippets equivalent? Why I'm sometimes said that main thread is not the owner of the MOC I retrieve from AppDelegate?

I really need help with this...


Solution

  • It seems that before calling getAllEntitiesToRootContext(), in certain scenario I was retrieving the context defined in AppDelegate from a queue that wasn't the main thread, what was causing that context to be initialized in that other queue...

    I found this thanks to the comment of Leo and the answer of Marcus S. Zarra, thanks.