核心数据父/子上下文保存失败

时间:2015-11-06 23:16:36

标签: multithreading core-data

我使用Parent / Child模型设置了后台线程。本质上,上下文保存失败。

这是我的设置。在AppDelegate中,我使用NSMainQueueConcurrencyType设置_managedObjectContext:

- (NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {

        _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];//[[NSManagedObjectContext alloc] init];
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _managedObjectContext;
}

在我的数据加载类中,我在这里设置父/子mocs以在后台线程上执行工作:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(queue, ^{

    NSManagedObjectContext *mainMOC = self.managedObjectContext;
    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];


    [moc setParentContext:mainMOC];
    [moc setUndoManager:nil];

当json数据完成后,我尝试使用以下宏执行保存操作:

#define SAVE_MOC    { NSError *error; \
if (![moc save:&error]) { NSLog(@"Sub MOC Error"); } \
[mainMOC performBlock:^{  NSError *e = nil;  if (![mainMOC save:&e]) {    

NSLog(@"Main MOC Error %@",error.localizedDescription);}}];}

当我完成数据加载时,我会跳回主线程,如下所示:

dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"<---- complete CS sub moc! ---->");
    //this fires ok

});

所以,从我的SAVE_MOC宏中我只得到一个简单的错误: 主MOC错误(null)

如果我能提供更多信息,请告诉我。我对多线程非常陌生,并试图更好地处理这种方法。

谢谢, 约什

1 个答案:

答案 0 :(得分:2)

  

在我的数据加载类中,我在这里设置了父/子mocs来执行   后台线程的工作:

dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
    NSManagedObjectContext *mainMOC = self.managedObjectContext;
    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];

你不应该这样做。这样做。

NSManagedObjectContext *mainMOC = self.managedObjectContext;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

确保您在performBlock中访问MOC。例如,

[moc performBlock:^{
    // Anything at all involving this MOC or any of its objects
}];
  

当json数据完成后,我尝试执行保存操作   使用以下宏:

考虑用这样的东西保存。保存完成后,将调用完成块。

- (void)saveMOC:(NSManagedObjectContext*)moc
     completion:(void(^)(NSError *error))completion {
    [moc performBlock:^{
        NSError *error = nil;
        if ([moc save:&error]) {
            if (moc.parentContext) {
                return [self saveMOC:moc.parentContext completion:completion];
            }
        }
        if (completion) {
            dispatch_async(dispatch_get_main_queue(), ^{
                completion(error);
            });
        }
    }];
}

[self saveMOC:moc completion:^(NSError *error) {
    // Completion handler is called from main-thread, after save has finished
    if (error) {
        // Handle error
    } else {
    }
}];

修改

  

如果moc.parentContext是主要并发类型,则此代码将崩溃。 -   蒙迪

我发布的代码没有内在原因会导致父级MOC NSMainQueueConcurrencyType崩溃。自父/子被添加到核心数据以来,它一直支持成为父上下文。

也许我错过了一个拼写错误,所以我直接从这个答案中复制/粘贴saveMOC:completion:,并编写了以下测试助手。

- (void)testWithChildConcurrencyType:(NSManagedObjectContextConcurrencyType)childConcurrencyType
               parentConcurrencyType:(NSManagedObjectContextConcurrencyType)parentConcurrencyType {
    NSAttributeDescription *attr = [[NSAttributeDescription alloc] init];
    attr.name = @"attribute";
    attr.attributeType = NSStringAttributeType;
    NSEntityDescription *entity = [[NSEntityDescription alloc] init];
    entity.name = @"Entity";
    entity.properties = @[attr];
    NSManagedObjectModel *model = [[NSManagedObjectModel alloc] init];
    model.entities = @[entity];

    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    [psc addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:NULL];
    NSManagedObjectContext *parent = [[NSManagedObjectContext alloc] initWithConcurrencyType:parentConcurrencyType];
    parent.persistentStoreCoordinator = psc;

    NSManagedObjectContext *child = [[NSManagedObjectContext alloc] initWithConcurrencyType:childConcurrencyType];
    child.parentContext = parent;

    NSManagedObject *obj = [NSEntityDescription insertNewObjectForEntityForName:@"Entity" inManagedObjectContext:child];
    [obj setValue:@"value" forKey:@"attribute"];

    XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"save from %@ to %@ finished", concurrencyTypeString(childConcurrencyType), concurrencyTypeString(parentConcurrencyType)]];
    [self saveMOC:child completion:^(NSError *error) {
        // Verify data saved all the way to the PSC
        NSManagedObjectContext *localMoc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        localMoc.persistentStoreCoordinator = psc;
        NSFetchRequest *fr = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
        XCTAssertEqualObjects(@"value", [[[localMoc executeFetchRequest:fr error:NULL] firstObject] valueForKey:@"attribute"]);
        [expectation fulfill];
    }];
    [self waitForExpectationsWithTimeout:10 handler:nil];
}

然后,我为每个可能的父/子关系写了一个测试。

- (void)testThatDoingRecursiveSaveFromPrivateToPrivateWorks {
    [self testWithChildConcurrencyType:NSPrivateQueueConcurrencyType
                 parentConcurrencyType:NSPrivateQueueConcurrencyType];
}
- (void)testThatDoingRecursiveSaveFromPrivateToMainWorks {
    [self testWithChildConcurrencyType:NSPrivateQueueConcurrencyType
                 parentConcurrencyType:NSMainQueueConcurrencyType];
}
- (void)testThatDoingRecursiveSaveFromMainToPrivateWorks {
    [self testWithChildConcurrencyType:NSMainQueueConcurrencyType
                 parentConcurrencyType:NSPrivateQueueConcurrencyType];
}
- (void)testThatDoingRecursiveSaveFromMainToMainWorks {
    [self testWithChildConcurrencyType:NSMainQueueConcurrencyType
                 parentConcurrencyType:NSMainQueueConcurrencyType];
}

那么,我错过了什么?

在我写这篇文章时,我想起了一个360iDev演示文稿,主持人表示你无法在performBlock上下文中调用NSMainQueueConcurrencyType。当时,我认为他只是错过了,意味着禁闭,但也许社区中存在一些混淆。

您无法在performBlock MOC上致电NSConfinementConcurrencyType,但performBlock完全支持NSMainQueueConcurrencyType