核心数据:如何合并两个 NSManagedObjectContext 之间的插入/更新/删除,同时将合并保持为可撤消的步骤?

发布于 2024-11-18 13:31:04 字数 3819 浏览 4 评论 0原文

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

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

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

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

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

但是,虽然插入的对象很容易出现在我的数组控制器中,但文档并未标记为脏,并且新添加的托管对象的 isInserted 属性未设置为 YES。此外,插入(到主要上下文中)是不可撤消的。

对任何插入的对象进行重新故障至少会将文档标记为脏,但插入仍然不可撤消:

- (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];
    }
}

Wrt -mocDidSave:,我使用自定义实现得到了稍微好一点的结果:

- (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];
    }
}

这导致我的数组控制器得到更新后,文档被标记为脏,并且插入和插入内容被标记为脏。删除操作是不可撤销的。但是,我仍然遇到无法撤消的更新问题。我是否应该手动循环所有更新的对象并使用 -[NSManagedObjectchangedValues] 重新应用主上下文中的更改?

当然,这种自定义实现会在主上下文上重复辅助上下文中的大量工作。是否有任何其他/更好的方法可以在两个上下文之间进行合并,同时将合并保持为可撤消的步骤?

I have a document-based Core Data application (running on Mac OS X 10.5 and above) where I'm trying to use two NSManagedObjectContext's on the main thread. I'd like to merge the changes made in the secondary context into my main (primary) context. In addition, I want the changes that were merged in from the secondary context to be undoable and to cause the document to be marked "dirty". I guess my question is similar to "Undoing Core Data insertions that are performed off the main thread" but, ATM, I'm not using different threads.

I've been observing the NSManagedObjectContextDidSaveNotification (which gets sent from the second context when calling -[self.secondaryContext save:]) like this:

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

In the -mocDidSave: method called by the observer I tried to use -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:] on the primary context to merge the changes from the secondary context into the primary context:

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

However, while the, say, inserted objects readily appear in my array controller, the document is not marked dirty and the isInserted property of the newly added managed objects is not set to YES. Also the insertion (into the primary context) is not undoable.

Re-faulting any inserted objects will at least mark the document dirty but the insertion is still not undoable:

- (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:, I had slightly better results with a custom implementation:

- (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];
    }
}

This causes my array controller to get updated, the document gets marked dirty, and the insertions & deletions are undoable. However, I'm still having problems with updates which aren't yet undoable. Should I instead manually loop over all updatedObjects and use -[NSManagedObject changedValues] to reapply the changes in the primary context?

This custom implementation would, of course, duplicate a lot of work from the secondary context on the main context. Is there any other/better way of getting a merge between two contexts while maintaining the merge as undoable step?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一城柳絮吹成雪 2024-11-25 13:31:04

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

保存会重置撤消管理器,因此您无法使用 NSManagedObjectContextDidSaveNotification 来
执行任何可以撤消的操作。正如您所发现的,您可以欺骗应用程序认为文档是脏的,但您不能强制撤消管理器记住上次保存的内容。

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

If you are not using separate threads, then you don't actually need to separate contexts. Using two context on the same thread adds complexity without gaining anything. If you don't know for certain you will employ threads, then I would highly recommend just using the one context. Premature optimization is the root of all evil.

Saves reset the Undo manager so you can't use NSManagedObjectContextDidSaveNotification to
perform any operation that can be undone. As you found you can trick the app into thinking the document is dirty but you can't force the Undo manager to remember past the last save.

The only way to do that, to get unlimited undo, is to save multiple versions of the doc behind the scenes. IIRC, you can also serialize the undo manager so that it can be written to file and reloaded to backtrack.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文