iOS 10上的核心数据保存错误:未解决的错误错误域= NSCocoaErrorDomain代码= 133020

时间:2017-01-10 23:34:19

标签: ios core-data concurrency

我一直在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解析器。不知道为什么会崩溃。

更新

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

2 个答案:

答案 0 :(得分:5)

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

在核心数据管理器中创建一个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)视为只读,因为它们也将位于写入队列之外。

起初我认为NSPersistentContainer的{​​{1}}有一个内部队列,并且支持初始测试。经过更多测试后,我发现它也可能导致合并冲突。

答案 1 :(得分:1)

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

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