UIManagedDocument NSFetchedResultsController和背景上下文

我正在尝试以下工作。

我有一个表格视图,显示从表格视图中的API获取的数据。 为此,我正在使用NSFetchedResultsController:

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

我在像这样的后台上下文中创build我的实体:

  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并不像它应该那样工作。 特别是,我总是更新一个实体(而不是创build一个新实体),但我的表视图显示两个实体。 一旦我重新启动应用程序,它显示正确。

如果我在self.database.managedObjectContext中创build实体([MyAPI createEntities]),一切正常。

任何想法我做错了什么? 通过这里的现有线索来看看,让我觉得我正在做正确的方式。 再次,如果我不做核心数据保存在背景上下文(但在document.managedObjectContext),那么它工作正常…

我今天在苹果开发论坛上看到类似的问题。 也许这和你的问题一样, https://devforums.apple.com/message/666492#666492 ,在这种情况下可能有一个错误(或者至less有其他人有同样的问题来讨论它!)。

假设它不是,这听起来像你想要做的应该是完全可能的嵌套上下文,因此假设没有与UIManagedDocument错误。

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

除了这个保留之外,大部分我已经阅读或者关于嵌套上下文和后台工作的指令似乎都是用同级子上下文来完成的。 你所描述的是一个父母,孩子,孙子的configuration。 在WWDC 2012video“Session 214 – 核心数据最佳实践”(+16:00分钟)中,Applebuild议在此场景的父上下文中添加另一个对等上下文,例如

 backgroundContext.parentContext = document.managedObjectContext.parentContext; 

这个工作是在这个上下文中asynchronous执行的,然后通过一个调用来保存在背景上下文中。 然后父节点将被asynchronous保存,任何对等上下文(在本例中为document.managedObjectContext )都将通过读取,合并或刷新来访问这些更改。 这也在UIManagedDocument文档中描述:

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

[编辑:重新阅读这可能只是build议Jeffery的build议,即不创build任何新的上下文,只是使用父上下文。]

这就是说,文档还build议,通常你不要调用保存子上下文,但使用UIManagedDocument的保存方法。 这可能是您确实要求保存或可能是问题的一部分的场合。 如Jeffery所述,更强烈地阻止调用保存在父上下文中。 我读过堆栈溢出的另一个答案build议只使用updateChangeCount触发UIManagedDocument保存。 但是我没有从Apple那里读过任何东西,所以在这种情况下,可以调用UIManagedDocument saveToURL:forSaveOperation:completionHandler:方法来获取所有内容并保存。

我想下一个明显的问题是如何通知NSFetchedResultsController发生了变化。 我会试图简化如上所述的设置,然后订阅各种NSManagedObjectContextObjectsDidChangeNotification或保存在不同的上下文通知,看看哪些(如果有的话),当UIMangedDocument保存,自动保存或当背景更改保存到父假设在这种情况下是允许的)。 我假设NSFetchedResultsController连线到这些通知,以保持与底层数据同步。

另外也许你需要在主要上下文中手动执行一个提取,合并或刷新来获取更改,然后以某种方式通知NSFetchedResultsController它需要刷新?

就我个人而言,我想知道UIManagedDocument是否已经准备好进行一般消费了,今年在WWDC上没有提到它,而是提出了一个关于如何构build一个更为复杂的解决scheme的冗长讨论:“Session 227 – 使用带有核心数据的iCloud”

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

 [self.managedObjectContext performBlock:^{ // create my entities [self.document updateChangeCount:UIDocumentChangeDone]; [self.document savePresentedItemChangesWithCompletionHandler:^(NSError *errorOrNil) { ... }]; }]; 

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


更新后

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

您通常应该使用标准的UIDocument方法来保存文档。 如果直接保存子上下文,则只能将更改提交到父上下文,而不提交到文档存储。 如果直接保存父上下文,则会回避文档执行的其他重要操作。

我不得不使用-insertedObjectsobtainPermanentIDsForObjects:error:持久化在上下文中创build的新对象。

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

最后,我不经常调用[document updateChangeCount:UIDocumentChangeDone] 。 这由文件自动处理。 你仍然可以随时做,但不是必须的。

以下是我将如何调用Your -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); } }];