首页 文章

NSPersistentContainer并发用于保存到核心数据

提问于
浏览
5

我仍然对如何使用NSPersistentContainer performBackgroundTask 创建实体并保存它感到困惑 . 通过在 performBackgroundTask() { (moc) in } 块中调用方便方法 init(context moc: NSManagedObjectContext) 创建实例后,如果我检查 container.viewContext.hasChanges ,则返回false并说没有什么要保存,如果我在 moc 上调用save(为此块创建的后台MOC),我会收到如下错误:

致命错误:无法保存上下文:错误域= NSCocoaErrorDomain代码= 133020“无法合并更改 . ”的UserInfo = {conflictList =(
“NSMergeConflict(0x17466c500)for NSManagedObject(0x1702cd3c0)with objectID'0xd000000000100000 <x-coredata:// 3EE6E11B-1901-47B5-9931-3C95D6513974 / Currency / p4>'with oldVersion = 1 and newVersion = 2 and old cached row =
致命错误:无法保存上下文:错误Domain = NSCocoaErrorDomain Code = 133020“无法合并更改 . ”UserInfo = {conflictList =(
“NSMergeConflict(0x170664b80)for NSManagedObject(0x1742cb980)with objectID'0xd000000000100000 <x-coredata:// 3EE6E11B-1901-47B5-9931-3C95D6513974 / Currency / p4>'with oldVersion = 1 and newVersion = 2 and old cached row =
和新数据库row =
)}

所以我没有得到并发工作,如果有人能向我解释在iOS 10中对核心数据使用此功能的正确方法,我将非常感激

1 回答

  • 7

    TL:DR :您的问题是您正在使用 viewContext 和背景上下文进行编写 . 您应该只以一种同步方式写入核心数据 .

    Full explanation: 如果一个对象同时从两个不同的上下文中更改,则核心数据并不是一个很好的解决方案,因为您可以通过这种方式丢失数据 . 很多专业人员长期处理这个问题的方式是有一个操作队列来排队写入,所以一次只进行一次写入,并且主线程上只有读取的另一个上下文 . 这样你就不会遇到任何合并冲突 . (有关此设置的详细说明,请参阅https://vimeo.com/89370886) .

    使用 NSPersistentContainer 进行此设置非常简单 . 在核心数据管理器中创建一个NSOperationQueue

    _persistentContainerQueue = [[NSOperationQueue alloc] init];
    _persistentContainerQueue.maxConcurrentOperationCount = 1;
    

    并使用此队列写所有内容:

    - (void)enqueueCoreDataBlock:(void (^)(NSManagedObjectContext* context))block{
        void (^blockCopy)(NSManagedObjectContext*) = [block copy];
    
        [self.persistentContainerQueue addOperation:[NSBlockOperation blockOperationWithBlock:^{
            NSManagedObjectContext* context =  self.persistentContainer.newBackgroundContext;
            [context performBlockAndWait:^{
                blockCopy(context);
                [context save:NULL];  //Don't just pass NULL here. look at the error and log it to your analytics service
            }];
        }]];
    }
    

    当您调用 enqueueCoreDataBlock 时,该块将入队以确保没有合并冲突 . 但是如果你写入 viewContext 会破坏这种设置 . 同样,您应该将您创建的任何其他上下文(使用 newBackgroundContextperformBackgroundTask )视为只读,因为它们也将位于写入队列之外 .

    起初我以为 NSPersistentContainerperformBackgroundTask 有一个内部队列,初始测试支持它 . 经过更多测试后,我发现它也可能导致合并冲突 .

相关问题