【问题标题】:Core Data merge two Managed Object ContextCore Data 合并两个 Managed Object Context
【发布时间】:2011-10-21 00:20:55
【问题描述】:

我的 Cocoa/Application 在主线程上有一个托管对象上下文。 当我需要更新我的数据时,我的程序将:

  1. 开始一个新线程
  2. 从服务器接收新数据
  3. 创建新的托管对象上下文
  4. 向主线程发送通知以合并两个上下文

这是在主线程接收通知的函数

- (void)loadManagedObjectFromNotification:(NSNotification *)saveNotification
{
    if ([NSThread isMainThread]) {
        [self.managedObjectContext mergeChangesFromContextDidSaveNotification:saveNotification];
    } else {
        [self performSelectorOnMainThread:@selector(loadManagedObjectFromNotification:) withObject:saveNotification waitUntilDone:YES];     
    }
}

我没有收到任何错误。 我的问题是合并结果,它实际上连接了两个上下文中的托管对象。

我的实体是一个非常简单的属性和关系列表。

也许合并需要一些说明,以便了解更新的托管对象何时不是新的,而是第一个的编辑版本。 我想在某处我需要指定一种方式来明确识别实体(例如,属性可以像 ID 一样)和合并策略(如果 2 个托管对象代表同一个对象,则使用 lastModificationDate 更多最近)。

我只需要了解如何正确合并 2 个上下文,以便为每个对象创建一个更新的副本。

更新 1

问题现在对我来说很清楚了。 2 上下文有一个很大的区别:ObjectID。 当主线程上的上下文使用持久存储协调器获取 ManagedObjects 时,第二个线程通过获取远程 URL 来创建这些对象。即使对象具有相同的内容,它们也会有 2 个不同的 objectID。

我的对象已经有一个唯一的标识符,我可以使用 setObjectId 来设置这个值。 (Apple 文档说这不是一个好主意)。

【问题讨论】:

  • 很明显对象 ID 是不同的,因为您是在创建对象而不是从存储中获取它们并更新它们。这就是为什么我在回答中说您不需要其他任何东西来合并对象。无论如何,处理这种情况的正确方法是使用托管对象 ID。由于托管对象 ID 是线程安全的,您可以将它们从主线程传递到另一个线程,然后使用 existingObjectWithID:error: 检索与特定 ID 关联的对象,更新它并保存上下文。现在合并将按您的预期运行。
  • 或者,如果您事先不知道必须在线程之间传递哪些托管对象 ID,那么在您的另一个线程中,您只需使用谓词来获取对象,以检索与检索到的对象相对应的对象从服务器,然后你更新它们并保存上下文。
  • 正确!问题是objectIds。现在,每次我执行 fetch 并且需要更新现有对象时,我都会从 store 中获取对象并更新它。
  • 很高兴知道您解决了问题。
  • 如果你把你的评论改写成答案,我会投票给你。

标签: objective-c cocoa core-data nsmanagedobject nsmanagedobjectcontext


【解决方案1】:

为了正确合并上下文,您需要执行以下操作。 首先,您不需要自己的通知。对上下文执行保存操作会自动将以下通知转发给已注册的观察者:

NSManagedObjectContextDidSaveNotification

因此,您需要做的就是:

1)在你的主线程中,可能在viewDidLoad方法中,注册这个通知:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(contextDidSave:)
                                             name:NSManagedObjectContextDidSaveNotification
                                            object:nil];

2) 在你的主线程中实现contextDidSave: 方法如下:

- (void)contextDidSave:(NSNotification *)notification
{

    SEL selector = @selector(mergeChangesFromContextDidSaveNotification:); 
    [managedObjectContext performSelectorOnMainThread:selector withObject:notification waitUntilDone:YES];

}

3) 在您的 dealloc 方法中添加以下内容:

[[NSNotificationCenter defaultCenter] removeObserver:self];

4) 使用类似以下方法在您的其他线程中创建一个新上下文:

- (NSManagedObjectContext*)createNewManagedObjectContext
{

    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init]; 
    [moc setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
    [moc setUndoManager:nil];
    return [moc autorelease];
}

5) 在收到新数据后,处理这种情况的正确方法是使用托管对象 ID。由于托管对象 ID 是线程安全的,您可以将它们从主线程传递到另一个线程,然后使用 existingObjectWithID:error: 检索与特定 ID 关联的对象,更新它并保存上下文。现在合并将按您的预期运行。或者,如果您事先不知道必须在线程之间传递哪些托管对象 ID,那么在您的另一个线程中,您只需使用谓词获取对象以检索与从服务器检索的对象相对应的对象,然后更新它们并保存上下文。

【讨论】:

  • 这和我的实现完全一样。我的问题是合并。当我调用 [self.managedObjectContext mergeChangesFromContextDidSaveNotification:saveNotification];它应该合并我的 ManagedObjects,但它只是进行串联。
  • 如果不查看所有相关源代码,很难判断您的实现出了什么问题。您确定要将数据保存在另一个线程的上下文中吗?需要保存(因为合并操作比较内存对象与存储对象),但您没有在问题中提及这一点。关于合并策略。当然,如果默认的不适合您的需要,您可以在每个上下文中设置一个。同样,如果没有额外的细节,很难说清楚。
  • 这是个好主意。问题是如果您检索它会产生 IO 的 ID。预取并不能解决这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-02
  • 2013-01-28
  • 2023-01-27
相关资源
最近更新 更多