多线程核心数据 - NSManagedObject 无效

Posted

技术标签:

【中文标题】多线程核心数据 - NSManagedObject 无效【英文标题】:Multithreaded Core Data - NSManagedObject invalidated 【发布时间】:2011-07-26 15:08:20 【问题描述】:

正如标题所暗示的,我正在使用一个核心数据应用程序,该应用程序在不同的后台线程中填充对象(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];

Merge 方法如下所示:

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。我该怎么做,我可以以某种方式将对象“更新”到另一个 Context 还是合并无法正常工作?

我需要在 Main ObjectContext 中定义一些特定的东西吗? 我尝试了不同的合并策略,但没有任何运气。

希望你能帮助我,谢谢!

【问题讨论】:

developer.apple.com/library/content/documentation/Cocoa/… 【参考方案1】:

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

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

然后您继续将数组发送到主线程,将NSManagedObjectinstances 传递到线程边界,并且在上下文之间是一个很大的禁忌。

你需要做的是:

    使用NSManagedObjectIDs 的NSManagedObject 创建一个数组。 通过线程边界发送对象 ID 数组。 从新线程上的托管对象 ID 及其上下文中重新创建一个带有 NSManagedObjects 的数组。

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

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

一个正常的用例:

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

    // Handle error.

完整的源代码,包括用于解析来自 apple.com 的新闻 RSS 并将其存储在 Core Data 中的示例应用程序,可在此处获得:https://github.com/jayway/CWCoreData。

【讨论】:

-1 虽然您的代码可能有用,但它并不能真正解决这个特定问题。 嘿!谢谢,我自己想出了这个,并完全按照您在此处描述的方式进行了操作。 :) 我有同样的问题,请你解释一下如何获取 NSManagedObjectIDs 数组 re reset invalidation - '托管对象上下文中的所有对象可能同时失效。 (例如,作为调用重置的结果,或者如果存储从持久存储协调器中删除。)当这种情况发生时,NSFetchedResultsController 不会使所有对象无效,也不会发送对象删除的单独通知。 developer.apple.com/library/ios/#DOCUMENTATION/CoreData/…【参考方案2】:

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

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

相反,您需要注册 tableview 控制器(在前端线程上)以从后台线程的上下文中接收 NSManagedObjectContextDidSaveNotification。这样,当后台上下文保存时,前端/主上下文将使用新数据更新自身,这将触发 tableview 的更新。

【讨论】:

以上是关于多线程核心数据 - NSManagedObject 无效的主要内容,如果未能解决你的问题,请参考以下文章

如何在不复制目标 NSManagedObject 的情况下将目标 NSManagedObject 添加到另一个具有反向多对多核心数据关系的对象?

核心数据 executeFetchRequest 返回 NSManagedObject

NSManagedObject - 跨线程设置自定义实例变量

将 NSManagedObject 与另一个 NSManagedContext(多线程)中的“相同”NSManagedObject 合并

在 Xcode 6 中从核心数据数据模型创建 NSManagedObject 子类

如何在核心数据 NSManagedObject 关系的 NSSet 中添加或删除项目时触发通知?