多线程违规给我们留下的一切都是荣耀(失败)

时间:2019-03-01 21:22:25

标签: ios multithreading core-data

现在我两天以来一直在head头,这归咎于开发人员和我自己。因此,我去尝试简化示例并尝试理解该标志:

  1. 我从向导(主要细节应用程序)创建了一个核心数据项目
  2. 将标志添加到参数:-com.apple.CoreData.ConcurrencyDebug 1
  3. 在viewDidLoad方法中添加了简单代码:

    NSFetchRequest *fr = [NSFetchRequest fetchRequestWithEntityName:@"Event"];
    _events = [self.pc.viewContext executeFetchRequest:fr error:nil];
    Event *event = _events.firstObject;
    
    NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
    NSManagedObjectID *objID = [event objectID];
    [opQueue addOperationWithBlock:^{
        NSManagedObjectContext *context = self.pc.newBackgroundContext;
        Event *bgEvent = [context objectWithID:objID]; // Thread 5: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    }];
    
  4. 运行该应用程序并发生此崩溃(请在最后一行之前检查注释1行)

因此,我正在尝试将主MOC对象移动到后台MOC并崩溃。

在不同线程的MOC之间传递对象时,我会丢失某些东西吗?

注意:pc只是由向导创建并存储在此控制器的strong属性中的persistentContainer。是的,它具有存储和正确的URL,这很好,如果我删除了这段代码,该应用程序将不会崩溃。在向Event实体添加了一些行之后,我添加了此代码,以便获取对象。

1 个答案:

答案 0 :(得分:1)

您不能在任意队列上的NSManagedObjectContext上进行操作。您需要使用上下文的队列。这意味着将您对objectWithID:的呼叫包装到context.perform()(异步)或context.performAndWait()(同步)中。

通常来说,您应该使用上下文的队列而不是创建自己的NSOperationQueue,但是如果出于某种原因需要自己的队列,那就很好了。您只需要将上下文的队列用于上下文的操作即可。

有关详细信息,请参见Core Data Programming Guide: Concurrency


根据您的评论,您已经写了以下内容:

NSManagedObjectID *objID = [event objectID];
[self.pc.viewContext performBlockAndWait:^{ 
   NSManagedObjectContext *context = self.pc.newBackgroundContext;
   Event *bgEvent = [context objectWithID:objID]; 
}];

那是不正确的,也是造成问题的原因。您不能在context的队列中使用viewContext。我相信您的意思是:

NSManagedObjectID *objID = [event objectID];
NSManagedObjectContext *context = self.pc.newBackgroundContext
[context performBlock:^{
    Event *bgEvent = [context objectWithID:objID]; 
    ....
}];

您需要在自己的context中使用perform block,而不是其他上下文的perform块。

在这里使用performBlockAndWait是没有意义的,因为那样会阻塞主队列。相反,这将在后台队列上异步执行此阻止,我相信这是您要尝试执行的操作。