UIManagedDocument与NSFetchedResultsController和背景语境(

2019-07-30 06:33发布

我试图让下面的工作。

我有一个是在一个表视图中显示数据从API取出的表图。 为了这个目的,我使用NSFetchedResultsController:

self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                                                        managedObjectContext:self.database.managedObjectContext
                                                                      sectionNameKeyPath:nil
                                                                               cacheName:nil];

我在这样的背景环境中创建我的实体:

    NSManagedObjectContext *backgroundContext;
    backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    backgroundContext.parentContext = document.managedObjectContext; 

    [backgroundContext performBlock:^{
        [MyAPI createEntitiesInContext:backgroundContext];

        NSError *error = nil;
        [backgroundContext save:&error];
        if (error) NSLog(@"error: %@",error.localizedDescription);

        [document.managedObjectContext performBlock:^{
            [document updateChangeCount:UIDocumentChangeDone];
            [document.managedObjectContext save:nil];
        }];

现在,每当我得到新的数据(和插入/更新实体像刚才如上图所示),我NSFetchedResultsController完全不是那么回事,因为它应该。 特别是,我一直在更新一个实体(不创建一个新的),但我的表视图显示了两个实体。 一旦我重新启动应用程序它正确显示出来。

如果我执行self.database.managedObjectContext实体([MyAPI createEntities])的创作,一切工作正常。

任何想法,我做错了什么? 翻翻这里现有的线程,以便让我觉得,我做的正确方法。 同样,如果我不这样做的核心数据在后台背景下保存(但document.managedObjectContext),那么它工作正常...

Answer 1:

我今天在苹果开发论坛读到了类似的问题。 也许,这是同样的问题,是你的, https://devforums.apple.com/message/666492#666492 ,在这种情况下也许有一个bug(或者至少别人有同样的问题一起讨论吧!)。

假如它不是,它听起来就像你想要做的应该是完全有可能使用嵌套的上下文是什么,因此,假设没有错误UIManagedDocument

我唯一保留的是,我一直试图让一批装载有工作UIManagedDocument而且好像它不与嵌套的上下文环境中运行( https://stackoverflow.com/q/11274412/1347502 )。 我想的主要好处之一NSFetchedResultsController是它的改善,通过批量加载性能的能力。 因此,如果这不能做UIManagedDocument也许NSFetchedResultsController不准备与使用UIManagedDocument ,但我还没有得到这一问题的底部呢。

该保留之外,大部分我读过或约嵌套的背景和工作背景看的指令似乎与孩子上下文内完成。 你所描述什么是父母,子女,孙子女的配置。 在WWDC 2012的视频“会话214 - 核心数据的最佳做法”(+ 16:00分钟),苹果公司建议增加了父上下文的另一个同行上下文这种情况下,如

backgroundContext.parentContext = document.managedObjectContext.parentContext;

这项工作是在这种情况下异步执行,然后通过调用推到父,以节省后台背景。 父然后将异步保存,任何对等环境中,在这种情况下, document.managedObjectContext ,将通过访问变化的获取,合并,或刷新。 这也是在描述UIManagedDocument文档:

  • 如果合适的话,你可以从后台线程直接加载数据到父上下文。 您可以使用parentContext获取父上下文。 数据装入父上下文意味着你不扰乱孩子上下文的操作。 你可以简单地通过执行读取检索在后台加载的数据。

[编辑:重新阅读这一点,可能只是建议杰弗里的建议,即不产生在所有任何新的情况和只使用父上下文。]

话虽这么说,文件还表明,通常你不叫救儿童的上下文,但使用UIManagedDocument的保存方法。 这可能是当你调用保存或可能是问题的一部分的时机。 调用保存父上下文更强烈劝阻,由杰弗里提及。 我已经在堆栈溢出读另一个答案建议只使用updateChangeCount触发UIManagedDocument扑救。 但我没有看过苹果的任何东西,所以也许在这种情况下调用UIManagedDocument saveToURL:forSaveOperation:completionHandler:方法是适当获得同步并保存一切。

我想下一个明显的问题是如何通知NSFetchedResultsController的改变已经发生。 我会被诱惑简化设置上面所讨论的,然后订阅各种NSManagedObjectContextObjectsDidChangeNotification或保存在不同的上下文的通知,看的时候,如果有的话,被称为UIMangedDocument保存,自动保存,或者在后台更改保存到父(假定在这种情况下可允许的)。 我假设NSFetchedResultsController被连接到这些通知,以保持同步与底层数据。

或者也许你需要手动执行提取,合并,或在主背景刷新到什么变化渡过难关,然后以某种方式通知NSFetchedResultsController ,它需要刷新?

个人而言,我不知道是否UIManagedDocument准备用于一般消费,今年出现在WWDC没有提到它,以及如何建立一个更复杂的解决方案,而不是一个漫长的讨论,提出:“会话227 -使用iCloud的核心数据”



Answer 2:

在我的方法,我从服务器获取数据,我首先创建实体之后,我调用这两个方法来保存对文档的更改:

[self.managedObjectContext performBlock:^{
     // create my entities


     [self.document updateChangeCount:UIDocumentChangeDone];
     [self.document savePresentedItemChangesWithCompletionHandler:^(NSError *errorOrNil) {
            ...
      }];
}];


Answer 3:

因为你正在更新在不同的上下文中的结果,我想你会需要调用[self.fetchedResultsController performFetch:&error]在您的视图控制器-viewWillAppear:方法。


更新后

OK,你不应该调用[backgroundContext save:&error][document.managedObjectContext save:nil] 。 请参阅: UIManagedDocument类参考

通常,您应该使用标准UIDocument的方式来保存文档。 如果直接救孩子而言,你只提交,并转移到父上下文不文档存储。 如果你直接保存父上下文,你回避其他重要的操作,该文件执行。

我不得不使用-insertedObjectsobtainPermanentIDsForObjects:error:坚持在一个环境中创建新的对象。

接下来,我不认为你需要创建一个新的背景下在后台运行。 document.managedObjectContext.parentContext应该是在运行更新可用的背景环境。

最后,我不叫[document updateChangeCount:UIDocumentChangeDone]非常频繁。 这是由文件自动照顾。 你仍然可以做任何你想要的时间,但它不应该是必要的。

这里是我会叫你-createEntitiesInContext方法。

[document.managedObjectContext.parentContext performBlock:^{
    [MyAPI createEntitiesInContext:document.managedObjectContext.parentContext];

    NSSet *objects = [document.managedObjectContext.parentContext insertedObjects];
    if (objects.count > 0) {
        NSError *error = nil;
        [document.managedObjectContext.parentContext obtainPermanentIDsForObjects:objects error:&error]
        if (error) NSLog(@"error: %@",error.localizedDescription);
    }
}];


文章来源: UIManagedDocument with NSFetchedResultsController and background context