使用 MagicalRecord 在后台保存的设计策略

Posted

技术标签:

【中文标题】使用 MagicalRecord 在后台保存的设计策略【英文标题】:Design strategy to save in the background with MagicalRecord 【发布时间】:2012-11-03 17:40:16 【问题描述】:

最近我开始了一个只需要一个商店的新应用程序(没有基于文档的应用程序)。有一段时间我很高兴我终于可以摆脱 NSManagedObjectContext... 直到我想在后台保存:-(

现在我对自己的代码感到困惑。例如:

- (void)awakeFromInsert

    [super awakeFromInsert];
    [self resetCard];
    self.creationDate = TODAY;
    self.dictionary = [Dictionary activeDictionary];
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center postNotificationName:NOTE_NEWCARD object:self];    

[Dictionary activeDictionary] 是一个 NSManagedObject 静态函数,返回一个指向在主线程中创建的 NSManagedObject 的指针。这将在后台保存期间导致交叉/上下文错误。因为我的程序总是从同一个存储读取,我想我可以避免这样写:

[Dictionary activeDictionaryWithContext:...]

我认为使用 MagicalRecord,只要我始终使用相同的后端工作,就可以避免传递上下文指针。我应该使用哪个函数来获取该上下文?

[NSManagedObjectContext MR_defaultContext]
[NSManagedObjectContext MR_context]
[NSManagedObjectContext MR_contextForCurrentThread]

在示例中,对象在通知中发送自己,这几乎是为了引起更多冲突。

在通知的情况下,我是否应该始终只发送 objectID?

在我看来,我的对象只有在主上下文中运行时才应该发出副作用操作/通知。但是,其中一些辅助操作会更改我的对象图,从而创建其他实体的新实例。

如果我使用 [MagicalRecord MR_saveAll] 保存,我可以安全地忽略我提到的两个有问题的函数调用吗?

我是否应该假设新的后台保存上下文的对象将是我的主线程中的对象的精确副本,而不调用那些额外的函数?

现在我遇到了问题,因为我从没想过 awakeFromInsert 会为同一个商店的同一个对象运行多次。我在想这样的事情:

- (void)awakeFromInsert

    [super awakeFromInsert];
    if ([self managedObjectContext] == [NSManagedObjectContext MR_defaultContext]) 
        [self resetCard];
        self.creationDate = TODAY;
        self.dictionary = [Dictionary activeDictionary];
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center postNotificationName:NOTE_NEWCARD object:self];    
    

这应该使我的 awakeFromInsert 代码只运行一次,而不是在后台保存上下文中。我担心这样做会丢失信息

【问题讨论】:

【参考方案1】:

虽然您当然可以通过这种方式发送您的对象,但我建议反对。请记住,即使在 CoreData 中使用新的父子上下文,NSManagedObjects 也是不是线程安全的。如果您创建或导入对象,则需要先保存它们,然后才能在其他上下文中使用它们。

MagicalRecord 提供了一个比较简单的后台保存API:

[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext)
    MyEntity *newEntity = [MyEntity MR_createInContext:localContext];

    //perform other entity operations here
];

此块为您完成所有工作,无需担心正确设置 NSManagedObjectContext。

你不应该在通知中传递 NSManagedObjects 的另一个原因是你不知道通知将在哪个线程上接收。这可能会导致崩溃,因为同样,NSManagedObjects 不是线程安全的。

您提供的通知方法的另一种替代方法是将观察者添加到 NSManagedObjectContextDidSaveNotification,并将您的更改合并到该通知上。这只会在您的对象被保存后触发,并且通过父子关系或持久存储(旧方式)跨上下文是安全的。

【讨论】:

谢谢,我使用的是 MagicalRecord 2.0.7,虽然我只使用一个上下文,但普通的 MR_save 会带来一些麻烦,因为它可以使用它创建的第二个上下文。最糟糕的是,通知来自第三个上下文?!?!? 我知道 NSManagedObjects 不是线程安全的,我的问题是我应该如何处理它以及在 MR_save 期间会发生什么。我看到正在创建第二个上下文,因此我的 awakeFromInsert 被同一商店的对象调用了两次。如果我的 awakeFromInsert 代码仅在当前上下文是主线程时运行,我担心会丢失信息。现在更新问题... awakeFromInsert 被调用了两次,因为您在不同的上下文中创建了两个实例。我认为您想要的是创建对象,然后保存,然后在第二个上下文中加载对象。 这篇帖子两天后,我发现如果不在当前上下文中运行,我必须编写排除 awakeFromInsert 的条件。我很确定我只使用一个单一的上下文。 MR_save 确实创建了一个新对象并重新创建了我所有对象的实例,因此我的所有 awakeFromInsert 都执行了两次。使用标准 :save:&error 不会触发第二次调用。 Magic 是否处理了上下文合并?还是我需要设置 AppDelegate 来处理这个?

以上是关于使用 MagicalRecord 在后台保存的设计策略的主要内容,如果未能解决你的问题,请参考以下文章

如何确保在使用 MagicalRecord 3.0 保存后台线程后通知 NSFetchedResultsController

正确设置 MagicalRecord 以在后台线程上保存 rootSavingContext

MagicalRecord:后台导入数据,访问刚导入的数据

在后台线程中使用 MagicalRecord 和 NSXMLParser

MagicalRecord UIApplicationDidEnterBackground

MagicalRecord 插入太慢