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

Posted

技术标签:

【中文标题】将 NSManagedObject 与另一个 NSManagedContext(多线程)中的“相同”NSManagedObject 合并【英文标题】:Merge NSManagedObject with the "same" NSManagedObject in another NSManagedContext (Multi Threaded) 【发布时间】:2011-08-18 19:58:16 【问题描述】:

我有两个处于 1:n 关系的实体。在这个问题中,我将使用 companyemployes。在我的应用程序中,您可以创建一个新的公司并自动找到他们的员工

我创建了许多线程,它们都有自己的NSManagedObjectContext。在这些线程中,我创建了一名新员工。我可以创建新的employees(在一个线程内),然后使用mergeChangesFromContextDidSaveNotification: 合并两个上下文。

问题是我不知道在哪里创建新的公司。如果我在线程中创建它并添加新的 employe,我会为每个 employe 获得一家公司。

如果我在创建 employes(在他们自己的线程中)之前创建了一个 company,那么 company 是错误的NSManagedObjectContext。而且我不可能将 company 对象从主 NSManagedObjectContext 移动到线程中的对象。 我找到了一种将公司复制到线程的NSManagedObjectContext 的方法,但是在合并上下文时,我有一个 company 和一个 employe、一个和两个、一个和三个以及以此类推。

您可能会说我可以创建所有 员工,然后在创建他们之后,将他们添加到一家新公司。但我不希望这样,因为我已将实体绑定到 NSArrayController 以便在两个 TableView 中显示它们。我想在创建新员工后立即更新 tableview。

我还希望能够更新公司并寻找新的员工。至少在这一点上,我需要一个已经存在的 company 来刷新它。所以我必须以某种方式获取并更改它,而不是同时从两个线程更改它......

这个问题让我抓狂。现在坐了两天。有人可以帮助我吗?

编辑:当使用mergeChangesFromContextDidSaveNotification:时,是否可以以某种方式说它始终是同一家公司?

Edit2:这是我在 cmets 中谈到的测试应用程序:http://www.file-upload.net/download-3674327/CoreDataMultiThreading.zip.html

非常感谢!

(请原谅我的发音)

【问题讨论】:

【参考方案1】:

我遇到了几乎完全相同的问题,我认为这可能是 CoreData 架构中存在的最大问题之一 :(

线程不能很好地处理核心数据。我通常以一个核心数据“编写器”线程结束,而所有其他线程只会从持久存储中读取(在编写器线程进行更新时更新自己)。 (实际上,我使用 NSOperationQueue 和 NSOperation 子类来为我处理线程,但它基本上是相同的——只要在任何时候只有一个线程在写我就可以了)

您的问题有两种解决方案。

您可以先创建公司并将其保存到持久存储中,然后再触发任何线程。然后,所有让员工的线程都可以在各自的 NSManagedObjectContexts 中检索同一家公司。为了确保所有线程在它们的 NSManagedObjectContexts 中都有 same 公司,请将公司的 NSManagedObjectID 传递给每个线程并使用 objectWithID: 从本地 NSManagedContext 中检索它。 (在多线程中使用 CoreData 的规则之一是永远不要传递托管对象,始终传递 ID 并为每个线程获取一个新副本)

另一种解决方案是按顺序执行所有可能发生冲突的核心数据写入操作(我最喜欢的解决方案)——这将确保第一个操作使公司和其他操作只使用已经存在的操作.

希望有帮助!


EDIT 在线程之间移动对象示例

此方法将创建一个公司并为您提供托管对象 ID

// Create the company and save it. 
- (NSManagedObjectID *)createCompanyOnThreadOne 
     NSManagedObject *company = [NSEntityDescription insertNewObjectForEntityForName:@"Company" inManagedObjectContext:context];
     [company setName:@"My Company"];
     [[company managedObjectContext] save:nil];
     return [company objectID];

// 然后,对于每个要添加员工的后台线程,传入对象 ID:

// Create the company
ManagedObjectID *companyID = [self createCompanyOnThreadOne];

// Create three employees
[self performSelectorInBackground:@selector(createEmployee:) withObject:companyID];
[self performSelectorInBackground:@selector(createEmployee:) withObject:companyID];
[self performSelectorInBackground:@selector(createEmployee:) withObject:companyID];

// 在后台运行的方法看起来像

- (void)createEmployee:(NSManagedObjectID *)companyID 
    NSManagedObjectContext *context = ... get a new context for this thread here ... 

    // Get the company from the persistent store
    NSManagedObject *company = [context objectWithID:companyID];

    // Make your employee and save it
    NSManagedObject *employee = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:context];
    [employee setCompany:company];

    // Save it
    [context save:nil];

但是,我不建议同时编写这么多线程 - 这会变得混乱 - 如果两个线程同时将员工添加到公司会发生什么 - 我敢打赌一个线程会保存 OK 并且第二个线程会告诉你有合并冲突?我想你会发现的。

【讨论】:

感谢您的回答!第一个解决方案似乎最适合我的应用程序。但现在我很难将公司从一种情况转移到另一种情况。我试过 [threadContext insertObject:[mainContext objectWithID:[currentCompany objectID]]];然后(当然)它告诉我:“一个 NSManagedObject 可能只在一个 NSManagedObjectContext 中(或被一个 NSManagedObjectContext 观察到)。”所以我尝试使用我在这里找到的方法复制公司:pastebin.com/efkji4sy。但是话又说回来,我为一名员工开设了一家公司……嗯:( 嗨,我添加了一个简单的示例 - 我是从记忆中输入的,因此可能存在拼写错误。重要的是我在线程之间传递 objectID,而不是 company 对象本身。 感谢更新。但它不会起作用。 NSManagedObject *company = [context objectWithID:companyID];将导致我之前描述的相同错误:“一个 NSManagedObject 可能只在一个 NSManagedObjectContext 中(或被一个 NSManagedObjectContext 观察到)。”而且它们仍然处于不同的上下文中,所以 [employee setCompany:company];将导致“非法尝试在不同上下文中的对象之间建立关系‘公司’” 为什么调用 objectWithID: 会导致该错误 - 您没有在上下文之间传递托管对象,只是它的 ID!您能否编辑您的问题并添加导致错误的代码,以便我可以看到更多发生的情况? 那么您使用了错误的company(或错误的context) - 在我的示例中,您用于添加员工的公司对象是从创建员工对象 - 它们都是我的 createEmployee: 方法中的局部变量,不与任何其他方法共享。

以上是关于将 NSManagedObject 与另一个 NSManagedContext(多线程)中的“相同”NSManagedObject 合并的主要内容,如果未能解决你的问题,请参考以下文章

将 NSManagedObject 数组与另一个“对象”类型 Swift 2 的数组进行比较

如何在单个托管对象上下文中初始化新的 NSManagedObject 并设置与另一个 NSManagedObject 的关系?

如何在 CoreData 的代码中设置 NSManagedObject 与另一个 NSManagedObject 或它们的堆栈的关系?

NSManagedObject 类关系中的 NSPredicate 键

如何在核心数据中保存与另一个对象相关的对象?

更新到 NSManagedObject 会导致 NSFetchedResultsController 删除