多线程核心数据 - 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 中定义特定的内容吗? 我尝试过不同的合并策略,但没有任何运气。
希望你能帮助我,谢谢!
As the title suggests im working with a Core Data Application which gets filled with objects in different background threads (XML Parsing)
In my background thread I'm doing this
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];
The Merge method looks like this:
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];
I think the merge is successful but as i want to display it in an UITableView it always tells me that my objects are invalidated which is to be expected because of
[managedContext reset];
What i want to do is show the Items which are currently in the database, in the background parse the xml and if thats finished i want to update the UITableView with the new / updated objects. How would I do that, can i "update" the objects to the other Context somehow or is the merge not working correctly?
Do I need to define something specific in the Main ObjectContext?
I've tried different mergepolicies without any luck.
Hope you can help me, thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我相信您的问题是
returnSource
数组的内容。如果这是一堆 NSManagedObject 实例,那么它们将由后台线程上下文在后台线程上创建。您调用
-[NSManagedObjectContext reset]
将使它们无效,因为这是您明确告诉上下文要做的事情。但这不是大问题。然后,您继续将数组发送到主线程,在线程边界上传递 NSManagedObject 实例,并且在上下文之间传递这是一个很大的禁忌。
您需要做的是:
NSManagedObject
的NSManagedObjectID
创建一个数组。NSManagedObject
的数组。我已经制作了一些核心数据助手,遵循三个规则(第三次写东西时,使其通用)。
最重要的是,我隐藏了管理每个线程的不同托管对象上下文、处理通知以及所有垃圾的复杂性。相反,我引入了线程本地上下文的概念。基本上是延迟创建的
NSManagedObjectContext
实例,当当前线程退出时自动注册更新和清理。正常用例:
完整的源代码,包括用于解析来自 apple.com 的新闻 RSS 并将其存储在 Core Data 中的示例应用程序,可在此处获取:https://github.com/jayway/CWCoreData。
I believe your problem is the contents of the
returnSource
array. If that is a bunch ofNSManagedObject
instances then they will have been created on the background thread by the background thread context.You call to
-[NSManagedObjectContext reset]
will invalidate them, since that is what you explicitly tell the context to do. But that is not the big problem.You then go on to send the array to a the main thread, passing
NSManagedObject
instances over thread borders, and between contexts is a big no-no.What you need to do is:
NSManagedObjectID
s of theNSManagedObject
.NSManagedObject
s from the managed object IDs on the new thread with it's context.I have made some Core Data helpers, following the rule of three (the third time you write something, make it general).
Most importantly I have hidden the complexity of managing different managed object contexts for each thread, handling notifications, and all that junk. Instead I have introduced the concept of thread local contexts. Basically lazily created
NSManagedObjectContext
instances that automatically registers for updates and cleanup when the current thread exits.A normal use-case:
The full source code, including a sample app for parsing the news RSS from apple.com and store it in Core Data, is available here: https://github.com/jayway/CWCoreData.
在这种情况下,没有理由在后台上下文上调用
reset
,因为它无论如何都会随着后台线程而消失,并且在任何情况下都不会再使用它。当您希望上下文忘记之前的步骤时,您通常会使用带有撤消管理的reset
。这里的主要问题是您的后台线程被配置为从它创建的托管对象上下文接收通知。这是毫无意义的。
相反,您需要注册 tableview 控制器(在前台线程上)以从后台线程的上下文中接收
NSManagedObjectContextDidSaveNotification
。这样,当后台上下文保存时,前端/主上下文将使用新数据更新自身,这将触发表视图的更新。There is no reason in this case to call
reset
on the background context because it will disappear anyway with the background thread and you never use it again in any case. You usually usereset
with undo management when you want the context to forget previous steps.Your major problem here is that your background thread is configured to receive notifications from the managed object context it creates. That is rather pointless.
Instead, you need to register the tableview controller (on the front thread) to receive the
NSManagedObjectContextDidSaveNotification
from the context on background thread. That way, when the background context saves, the front/main context will update itself with the new data which will trigger an update of the tableview.