关于Objective C:核心数据:如何在两个NSManagedObjectContext之间合并插入/更新/删除,同时将合并作为不可撤消的步骤进行维护?

Core Data: How to merge inserts/updates/deletes between two NSManagedObjectContext's while maintaining the merge as an undoable step?

我有一个基于文档的Core Data应用程序(在Mac OS X 10.5及更高版本上运行),试图在主线程上使用两个NSManagedObjectContext。我想将在辅助上下文中所做的更改合并到我的主(主要)上下文中。另外,我希望从辅助上下文中合并的更改是不可撤消的,并导致文档被标记为"脏"。我猜我的问题类似于"撤消在主线程上执行的核心数据插入",但是,ATM,我没有使用其他线程。

我一直在观察NSManagedObjectContextDidSaveNotification(在调用-[self.secondaryContext save:]时从第二个上下文发送),如下所示:

1
2
3
4
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(mocDidSave:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:self.secondaryContext];

在观察者调用的-mocDidSave:方法中,我尝试在主上下文上使用-[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:]将合并从辅助上下文到主上下文的更改:

1
2
3
4
- (void)mocDidSave:(NSNotification *)notification
{
    [self.primaryContext mergeChangesFromContextDidSaveNotification:notification];
}

但是,虽然说插入的对象很容易出现在我的阵列控制器中,但是文档没有被标记为脏,并且新添加的管理对象的isInserted属性未设置为YES。同样,插入(插入到主上下文中)也是不可撤销的。

对所有插入的对象进行重新故障处理将至少将文档标记为脏,但是插入仍不可撤消:

1
2
3
4
5
6
7
8
- (void)mocDidSave:(NSNotification *)notification
{
    [self.primaryContext mergeChangesFromContextDidSaveNotification:notification];

    for (NSManagedObject *insertedObject in [[notification userInfo] objectForKey:NSInsertedObjectsKey]) {
        [self.primaryContext refreshObject:[self.primaryContext existingObjectWithID:[insertedObject objectID] error:NULL] mergeChanges:NO];
    }
}

W.r.t。 -mocDidSave:,通过自定义实现,我得到了更好的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
- (void)mocDidSave:(NSNotification *)notification
{
    NSDictionary *userInfo = [notification userInfo];

    NSSet *insertedObjects = [userInfo objectForKey:NSInsertedObjectsKey];
    if ([insertedObjects count]) {
        NSMutableArray *newObjects = [NSMutableArray array];
        NSManagedObject *newObject = nil;
        for (NSManagedObject *insertedObject in insertedObjects) {
            newObject = [self.primaryContext existingObjectWithID:[insertedObject objectID] error:NULL];
            if (newObject) {
                [self.primaryContext insertObject:newObject];
                [newObjects addObject:newObject];
            }
        }

        [self.primaryContext processPendingChanges];

        for (NSManagedObject *newObject in newObjects) {
            [self.primaryContext refreshObject:newObject mergeChanges:NO];
        }
    }

    NSSet *updatedObjects = [userInfo objectForKey:NSUpdatedObjectsKey];
    if ([updatedObjects count]) {
        NSManagedObject *staleObject = nil;
        for (NSManagedObject *updatedObject in updatedObjects) {
            staleObject = [self.primaryContext objectRegisteredForID:[updatedObject objectID]];
            if (staleObject) {
                [self.primaryContext refreshObject:staleObject mergeChanges:NO];
            }
        }
    }

    NSSet *deletedObjects = [userInfo objectForKey:NSDeletedObjectsKey];
    if ([deletedObjects count]) {
        NSManagedObject *staleObject = nil;
        for (NSManagedObject *deletedObject in deletedObjects) {
            staleObject = [self.primaryContext objectRegisteredForID:[deletedObject objectID]];
            if (staleObject) {
                [self.primaryContext deleteObject:staleObject];
            }
        }

        [self.primaryContext processPendingChanges];
    }
}

这将导致我的数组控制器被更新,文档被标记为脏文件以及插入


如果您不使用单独的线程,则实际上不需要分开上下文。在同一线程上使用两个上下文会增加复杂性,但不会增加任何好处。如果您不确定是否要使用线程,那么我强烈建议您仅使用一个上下文。过早的优化是万恶之源。

保存重置撤消管理器,因此您无法使用NSManagedObjectContextDidSaveNotification进行操作
执行任何可以撤消的操作。如您所见,您可以欺骗应用程序以使其认为文档脏了,但是您不能强迫撤消管理器记住过去的保存。

做到这一点的唯一方法是无限撤消,就是在后台保存文档的多个版本。 IIRC,您还可以序列化撤消管理器,以便可以将其写入文件并重新加载到回溯。