多线程核心数据- NSManagedObject无效



正如标题所示,我正在使用一个核心数据应用程序,该应用程序在不同的后台线程(XML解析)中填充对象

在我的后台线程中,我这样做

managedContext = [[NSManagedObjectContext alloc] init];
[managedContext setUndoManager:nil];
[managedContext setPersistentStoreCoordinator: [[DataManager sharedManager] persistentStoreCoordinator]];
 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 
 [nc addObserver:self
        selector:@selector(mergeChanges:)
            name:NSManagedObjectContextDidSaveNotification
          object:managedContext];

NSMutableArray *returnSource = [[self parseDocument:doc] retain];

 [managedContext save:&error];
 if (error) {
     NSLog(@"saving error in datafeed"); 
 }
 [managedContext reset];
[self performSelectorOnMainThread:@selector(parseCompleteWithSource:) withObject:returnSource waitUntilDone:YES];

合并方法如下:

NSManagedObjectContext *mainContext = [[DataManager sharedManager] managedObjectContext];
// Merge changes into the main context on the main thread
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
                              withObject:notification
                           waitUntilDone:YES];  
[[NSNotificationCenter defaultCenter] removeObserver:self];

我认为合并是成功的,但当我想在UITableView中显示它时,它总是告诉我,我的对象是无效的,这是预期的,因为

[managedContext reset];

我想做的是显示当前在数据库中的项目,在后台解析xml,如果完成,我想用新的/更新的对象更新UITableView。我该如何做到这一点,我可以"更新"对象到其他上下文或合并不能正常工作?

我需要在Main ObjectContext中定义一些特定的东西吗?我试过不同的合并策略,但都没有成功。

希望你能帮助我,谢谢!

我相信你的问题是returnSource数组的内容。如果这是一堆NSManagedObject实例,那么它们将由后台线程上下文在后台线程上创建。

调用-[NSManagedObjectContext reset]将使它们无效,因为这是您显式地告诉上下文要做的。但这不是大问题。

然后继续将数组发送到主线程,在线程边界和上下文之间传递NSManagedObject实例是一个大禁忌。

你需要做的是:

  1. NSManagedObjectNSManagedObjectID s创建一个数组
  2. 在线程边界上发送对象ID数组
  3. 在新线程的上下文中,根据管理对象id重新创建一个 NSManagedObjects数组。

我做了一些Core Data helper,遵循三个的规则(第三次写东西时,让它通用)

最重要的是,我隐藏了为每个线程管理不同托管对象上下文、处理通知和所有垃圾的复杂性。相反,我引入了线程局部上下文的概念。基本上惰性创建NSManagedObjectContext实例,当当前线程退出时自动注册更新和清理。

一个正常的用例:

NSManagedObjectCotext* context = [NSManagedObjectCotext threadLocalContext];
// Do your stuff!!
NSError* error = nil;
if (![context saveWithFailureOption:NSManagedObjectContextCWSaveFailureOptionThreadDefault 
                              error:&error]) 
{
    // Handle error.
}

完整的源代码,包括解析apple.com的新闻RSS并将其存储在CoreData中的示例应用程序,可在这里获得:https://github.com/jayway/CWCoreData.

在这种情况下没有理由在背景上下文中调用reset,因为它无论如何都会随着背景线程消失,并且在任何情况下您都不会再次使用它。当您希望上下文忘记前面的步骤时,通常使用reset和undo管理。

你的主要问题是你的后台线程被配置为从它创建的托管对象上下文接收通知。那是毫无意义的。

相反,你需要注册tableview控制器(在前端线程上)从后台线程的上下文接收NSManagedObjectContextDidSaveNotification。这样,当后台context保存时,front/main context会用新数据更新自己这会触发tableview的更新。

最新更新