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

流过昼夜 提交于 2019-12-13 18:55:37

问题


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...


回答1:


What does context.executeFetchRequest(fetchRequest) do? What does that code look like?

At some point you are on another queue other than the main queue.

The error you are getting indicates you are violating thread confinement. If you have a breakpoint on exceptions and errors in Xcode it will show you the exact line of code that is violating the rule and what queue it was executed on.

What line of code is Xcode stopping on?

What queue are you on when Xcode stops?

I would not recommend turning off that debug flag, it is there to help you and to let you find threading errors. Turning it off is just hiding the problem and will cause a problem in production, with user data. Far better to invest the time, learn what is going on and correct it properly.




回答2:


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.



来源:https://stackoverflow.com/questions/32774538/illegal-access-to-managed-object-context-in-main-thread-why

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!