在后台队列中保存临时托管对象上下文

Posted

技术标签:

【中文标题】在后台队列中保存临时托管对象上下文【英文标题】:Saving a temporary managed object context on a background queue 【发布时间】:2013-10-29 01:01:58 【问题描述】:

根据Concurrency with Core Data Guide,您不应将 NSManagedObjectContext 保存在后台线程中,因为应用程序可能会在保存完成之前退出,因为线程已分离。

如果我理解正确,这意味着这样的事情是不正确的

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^
    NSManagedObjectContext* tempContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [tempContext setParentContext:[[MyDataManager sharedInstance] mainContext];
    [tempContext performBlockAndWait:^
         //Do some processing
        NSError* error;
        [tempContext save:&error];
    ];
);

我的第一直觉是在主队列完成后将上下文保存在主队列中,但 managedObjectContexts 应该是线程安全的。以下是可以解决问题还是有更好的解决方案?

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^
    NSManagedObjectContext* tempContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [tempContext setParentContext:[[MyDataManager sharedInstance] mainContext];
    [tempContext performBlockAndWait:^
        //Do some processing
    ];
    dispatch_async(dispatch_get_main_queue(), ^
        [tempContext performBlockAndWait:^
            NSError* error;
            [tempContext save:&error];
        ];
    );
);

【问题讨论】:

【参考方案1】:

第一:

在您的第一个示例中:[context save:...] 应该在上下文的 performBlockAndWait: 块中完成。 此外,如果您使用的是后台上下文,您可以简单地调用它的 performBlock: 方法,因为它已经使用 GCD 进行调度,所以它看起来像:

NSManagedObjectContext* tempContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[tempContext setParentContext:[[MyDataManager sharedInstance] mainContext];
[tempContext performBlock:^
    //Do some processing
    NSError* error;
    [tempContext save:&error];
];

其次:

“在后台线程中保存容易出错”(据我所知并理解)意思是: 您可以在后台保存,但不保证您调用的保存会在应用程序退出时运行完成(数据库仍然有效,或者可以回滚到部分保存前的状态)。 ==> 如果您使用后台保存,请不要假设在 2 次应用程序执行之间保存操作已完成。

第三点(只是为了强调): 不要使用没有performBlock:performBlockAndWait: 的私有队列上下文 ==> 您的第二个示例将导致意外行为

【讨论】:

好的,在 performBlock 中调用保存是有意义的,但是有没有办法确保在应用程序退出之前后台保存完全完成? 正如我在第二点中提到的那样,不。这就是主线程保存和后台保存的区别。如果您有必须保存的关键信息,请在主线程上进行。 谢谢,但我最初的问题是我该怎么做呢?你说我的第二个例子会导致意想不到的行为。是因为我没有使用 performBlock 还是因为它是在主线程上执行的私有队列上下文?请参阅我编辑的问题。在主线程上保存的正确方法是什么? 您的第二个示例是错误的,因为未在私有队列上下文 performBlock... 方法中完成保存。用户可能随时终止您的应用程序,恕不另行通知(在后台)。这意味着所有未完成的保存都不会完成(主线程上的事件)。你应该保存在主线程的原因是为了给用户一个一致的体验(用户执行了一个永久效果的动作,这应该反映在下一次应用程序运行==>保存在主线程上) 好的,所以我的第二个示例现在是正确的,这样保存在主线程上执行并且将始终完成?

以上是关于在后台队列中保存临时托管对象上下文的主要内容,如果未能解决你的问题,请参考以下文章

托管对象上下文的临时对象

核心数据和托管对象上下文

托管对象上下文未保存到持久存储

托管对象上下文不合并来自后台上下文的更改

如何删除子托管对象上下文中的临时对象?

无法在具有现有ObjectWithID 的子上下文中检索临时对象:错误: