【问题标题】:Core Data Save Error on iOS 10: Unresolved error Error Domain=NSCocoaErrorDomain Code=133020iOS 10 上的核心数据保存错误:未解决的错误 Error Domain=NSCocoaErrorDomain Code=133020
【发布时间】:2017-01-10 23:34:19
【问题描述】:

我一直在测试 iOS 10 中的新核心数据堆栈。我的测试应用将 JSON 数据解析为核心数据,我试图在用户可以访问 UI 时实现这一点。

我正在使用默认核心数据堆栈并使用后台上下文。

在 AppDelegate.m 中:

- (NSPersistentContainer *)persistentContainer {
    // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
    @synchronized (self) {
        if (_persistentContainer == nil) {
            _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"CoreDataTestingMDC"];
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                    abort();
                }
            }];
        }
    }
    _persistentContainer.viewContext.automaticallyMergesChangesFromParent = YES;
    return _persistentContainer;
}

我有一个简单的主从 UI,它在主视图控制器中显示核心数据实体,在详细视图中显示详细属性。如果用户不滚动主视图,一切正常。如果用户滚动,我通常会在保存时收到此错误:

Unresolved error Error Domain=NSCocoaErrorDomain Code=133020 "(null)" UserInfo={conflictList=(
"NSMergeConflict (0x600000667c00) for NSManagedObject (0x610000096490) with objectID '0xd000000000440000 <x-coredata://CFF27A51-8F9E-4898-A4EA-CD85C0AFF300/ContentItem/p17>' 
with oldVersion = 44 and newVersion = 45...

它继续列出具有完全相同属性的冲突项目。

同样在我的 AppDelegate 中,我添加了一个简单的便捷方法来生成背景上下文:

- (NSManagedObjectContext *)createBackgroundContext {
    return [self.persistentContainer newBackgroundContext];
}

这被传回 AppDelegate 进行保存操作:

- (void)saveContext:(NSManagedObjectContext *) theContext {
    NSError *error = nil;
    if ([theContext hasChanges] && ![theContext save:&error]) {
        NSLog(@"Unresolved error %@, %@", error, error.userInfo);
        abort();
    }
}

UI 按预期在 viewContext 上运行。我一直非常小心地使用所有 JSON 解析器编写的背景上下文。不知道为什么会崩溃。

更新:

似乎每当应用程序在初始运行后运行时就会发生错误。我可以在干净的模拟器上或删除应用程序后对其进行测试。它可以很好地将数据解析为核心数据,并且还会在用户与应用程序交互时更新。在第二次构建和运行时,应用程序将因上述错误而崩溃。

【问题讨论】:

    标签: ios core-data concurrency


    【解决方案1】:

    在我看来,您正在同时进行多个写入。要解决这个问题,您需要以单一同步方式写入核心数据。

    在您的核心数据管理器中创建一个 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,那将破坏此设置。同样,您应该将您创建的任何其他上下文(使用newBackgroundContext 或使用performBackgroundTask)视为只读,因为它们也将在写入队列之外。

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

    【讨论】:

    • 好的,如果我不使用newBackgroundContext,你是否建议我在performBackgroundTask 中使用viewContext?我可能会解析几十到几百个项目。不想锁定 UI...
    • performBackgroundTask 为您提供了使用的背景上下文。您应该永远不要使用 viewContext 进行编写。
    • 谢谢乔恩。我最终重写了解析器以完全使用performBackgroundTask。现在工作得很好。 :)
    【解决方案2】:

    也面临同样的问题。但我使用 ManagedObjectContext 的 MergePolicy 解决了它。默认情况下,合并策略是 NSMERGEPOLICYERROR。通过将其更改为 NSMergeByPropertyObjectTrumpMergePolicy 为我修复了 NSManagedObject 冲突问题。

    检查哪些合并策略适合您的要求。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-11
      • 2010-11-20
      相关资源
      最近更新 更多