0

我正在尝试使以下工作。

我有一个表格视图,它在表格视图中显示从 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]),一切正常。

知道我做错了什么吗?浏览 SO 上的现有线程让我认为我的做法是正确的。同样,如果我不在后台上下文中保存核心数据(但在 document.managedObjectContext 上),那么它可以正常工作......

4

3 回答 3

1

我今天在 Apple 开发论坛上读到了类似的问题。也许这与您的问题相同,https://devforums.apple.com/message/666492#666492,在这种情况下,可能存在错误(或者至少有其他有相同问题的人可以讨论!)。

假设它不是,听起来你想要做的事情应该完全可以使用嵌套上下文,因此假设UIManagedDocument.

我唯一的保留是我一直在尝试使用批处理加载UIManagedDocument,但它似乎不适用于嵌套上下文(https://stackoverflow.com/q/11274412/1347502)。我认为它的主要好处之一NSFetchedResultsController是它能够通过批量加载来提高性能。因此,如果这不能完成,UIManagedDocument也许NSFetchedResultsController还没有准备好使用,UIManagedDocument但我还没有深入了解这个问题。

撇开这一保留不谈,我阅读或查看的有关嵌套上下文和后台工作的大部分说明似乎都是使用对等子上下文完成的。您所描述的是父、子、孙配置。在 WWDC 2012 视频“Session 214 - Core Data Best Practices”(+ 16:00 分钟)中,Apple 建议在此场景的父上下文中添加另一个对等上下文,例如

backgroundContext.parentContext = document.managedObjectContext.parentContext;

该工作在此上下文中异步执行,然后通过调用将其推送到父级以保存在后台上下文中。然后将异步保存父级,并且任何对等上下文(在本例中为document.managedObjectContext)将通过获取、合并或刷新来访问更改。这也在UIManagedDocument文档中进行了描述:

  • 如果合适,您可以将数据从后台线程直接加载到父上下文。您可以使用 parentContext 获取父上下文。将数据加载到父上下文意味着您不会干扰子上下文的操作。您只需执行 fetch 即可检索在后台加载的数据。

[编辑:重新阅读它可能只是推荐 Jeffery 的建议,即根本不创建任何新上下文,而只是使用父上下文。]

话虽如此,文档还建议您通常不要在子上下文中调用 save 而是使用UIManagedDocument的 save 方法。这可能是您调用 save 的时候,也可能是问题的一部分。正如 Jeffery 所提到的,更强烈地不鼓励在父上下文上调用 save。我读过的关于堆栈溢出的另一个答案建议仅updateChangeCount用于触发UIManagedDocument保存。但我还没有从 Apple 读到任何东西,所以在这种情况下,调用该UIManagedDocument saveToURL:forSaveOperation:completionHandler:方法可能适合让所有内容同步并保存。

我想下一个明显的问题是如何通知 NSFetchedResultsController 发生了变化。我很想像上面讨论的那样简化设置,然后NSManagedObjectContextObjectsDidChangeNotification在不同的上下文中订阅各种或保存通知,并查看在UIMangedDocument保存、自动保存或将背景更改保存到父级时调用哪些通知(如果有的话)(假设在这种情况下是允许的)。我假设NSFetchedResultsController连接到这些通知是为了与基础数据保持同步。

或者,您可能需要在主上下文中手动执行获取、合并或刷新以获取更改,然后以某种方式通知NSFetchedResultsController它需要刷新?

就我个人而言,我想知道是否UIManagedDocument已准备好用于一般消费,今年的 WWDC 上没有提及它,而是提出了关于如何构建更复杂的解决方案的冗长讨论:“Session 227 - Using iCloud with Core Data”

于 2012-07-06T14:12:18.113 回答
1

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

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


     [self.document updateChangeCount:UIDocumentChangeDone];
     [self.document savePresentedItemChangesWithCompletionHandler:^(NSError *errorOrNil) {
            ...
      }];
}];
于 2013-11-26T10:22:53.160 回答
0

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


更新后

好的,你不应该打电话给[backgroundContext save:&error]or [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);
    }
}];
于 2012-07-05T20:37:28.197 回答