在主线程中非法访问托管对象上下文,为啥?

Posted

技术标签:

【中文标题】在主线程中非法访问托管对象上下文,为啥?【英文标题】:Illegal access to managed object context in main thread, why?在主线程中非法访问托管对象上下文,为什么? 【发布时间】:2015-09-25 03:56:34 【问题描述】:

我已启用 com.apple.CoreData.ConcurrencyDebug 1 来检查 Core Data 并发错误。我在Swift 类中有以下代码 sn-p:

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
    

如果我理解正确的话,我从AppDelegate 获得的上下文是与主线程相关联的,对吧?

但是,从我的另一个 Objective-C 班级,我愿意:

self.myEntitiesArray = [mySwiftClass getAllEntitiesToRootContext];

我得到这个错误日志:

CoreData:错误:当前线程不是此 NSManagedObjectContext(0x1a25f8a0) 的公认所有者。 executeFetchRequest期间非法访问:错误:

我不明白为什么...我应该将这样的上下文关联到主线程,而我正在从主线程调用getAllEntitiesToRootContext...

我需要帮助。提前致谢

编辑:这些是AppDelegate中与Core Data相关的方法:

- (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;

编辑 2:我正在使用 Xcode 7 并在 ios 9 设备中进行测试。

编辑 3:如果我禁用 com.apple.CoreData.ConcurrencyDebug 1,我会从 getAllEntitiesToRootContext() 获取对象...我什么都不懂,为什么会这样?

编辑 4: 我做了一些测试。如果我从 Objective-C 类中执行此操作:

- (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);
   

我有时在调用executeFetchRequest 时不会出错,并且实体的名称会显示在日志控制台中。其他时候,我也会收到与我在上面发布的类似的 Core Data 错误,而且我在做我正在做的事情时也会遇到:

- (NSArray *)getEntities

   MyEntityDao *myEntityDao = [[MyEntityDao alloc] init];
   self.myEntities = [[myEntityDao getAllEntitiesToRootContext] mutableCopy];

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

其中MyEntityDao 是定义lazy var contextgetAllEntitiesToRootContext()Swift 类,我收到上面也发布的核心数据错误...为什么?这两个代码sn-ps不是等价的吗?为什么我有时会说主线程不是我从AppDelegate 检索到的 MOC 的所有者?

我真的需要这方面的帮助......

【问题讨论】:

你如何初始化appDelegate.managedObjectContext @Leo 我是从AppDelegate默认提供的方法中得到的 @Leo 我编辑了我的问题 @Leo 似乎有错误...在application:didFinishLaunchingWithOptions: 中调用[self managedObjectContext] 以确保它在主线程中初始化是个好主意吗? 【参考方案1】:

context.executeFetchRequest(fetchRequest) 是做什么的?这段代码是什么样的?

在某些时候,您在主队列之外的另一个队列中。

您收到的错误表明您违反了线程限制。如果您在 Xcode 中对异常和错误设置断点,它将向您显示违反规则的确切代码行以及执行它的队列。

Xcode 停止在哪一行代码?

Xcode 停止时你在哪个队列中?

我不建议关闭该调试标志,它可以帮助您并让您找到线程错误。关闭它只是隐藏问题,并且会在生产中导致用户数据出现问题。更好地投入时间,了解正在发生的事情并正确纠正它。

【讨论】:

非常感谢,我将测试一些东西并尝试看看发生了什么......我想我误解了一些 Core Data 基础知识,这就是我管理错误的原因在我的整个应用程序中,这是我第一次不直接管理 SQlite 文件,而且我的数据模型和应用程序逻辑比我找到的示例要复杂一些。我打算再写一篇文章来澄清一些想法。 我无法理解在这种情况下发生了什么......我的lazy var context 在主线程中被调用,它检索在AppDelegate 中定义的MOC,这样的MOC 是应该属于主线程吧?但是当我尝试在getAllEntitiesToRootContext 中获取调用executeFetchRequest 的数据时,我得到了我在问题中提到的核心数据错误......这可能是因为我试图获取的数据是从私人MOC 中保存的吗?有道理吗? 它并不总是显示违反规则的确切代码行。我需要做些什么才能让它显示出来吗?有时间请查看问题:***.com/questions/39685807/…。【参考方案2】:

似乎在调用getAllEntitiesToRootContext() 之前,在某些情况下,我正在从不是主线程的队列中检索AppDelegate 中定义的上下文,是什么导致该上下文在另一个队列中被初始化。 .

感谢 Leo 的评论和 Marcus S. Zarra 的回答,我找到了这个,谢谢。

【讨论】:

[[NSManagedObjectContext alloc] init] 自 iOS 9 起已弃用。切换到正确的初始化程序,这将极大地帮助解决此类问题。保持调试标志打开。很高兴你把它整理出来。

以上是关于在主线程中非法访问托管对象上下文,为啥?的主要内容,如果未能解决你的问题,请参考以下文章

在后台线程中更新托管对象上下文

Core Data 3 托管对象上下文

其他线程可以间接访问在自己的线程中运行的托管对象上下文吗?

(SWIFT 3) 创建父子托管对象上下文

托管对象上下文未保存到持久存储

核心数据:获取背景并在主线程上使用 objectWithID,性能优势?