Ios NSFetchedResultsController无法在子上下文中获取

Ios NSFetchedResultsController无法在子上下文中获取,ios,core-data,nsfetchedresultscontroller,uimanageddocument,Ios,Core Data,Nsfetchedresultscontroller,Uimanageddocument,我有一个带有一些数据的UIManagedDocument,我使用NSFetchedResultsController将这些数据显示在列表中。数据会定期在后台更新,并将更改放到UIManagedDocument.managedObjectContext(使用performBlock:)中 当我显示文档主上下文中的数据时,所有操作都按预期进行。但只要我在主上下文的子上下文(child.parentContext=document.managedObjectContext)中显示列表,我就看不到任何对

我有一个带有一些数据的
UIManagedDocument
,我使用
NSFetchedResultsController
将这些数据显示在列表中。数据会定期在后台更新,并将更改放到
UIManagedDocument.managedObjectContext
(使用performBlock:)中

当我显示文档主上下文中的数据时,所有操作都按预期进行。但只要我在主上下文的子上下文(
child.parentContext=document.managedObjectContext
)中显示列表,我就看不到任何对象,控制台上会显示以下错误:

foo[17895:15203] CoreData: error: (NSFetchedResultsController)
                 The fetched object at index 5 has an out of order section name 'E.
                 Objects must be sorted by section name'
只有在将新对象插入到“文档”联系人后,才会发生这种情况。当我等待自动保存足够长的时间时,列表显示良好。另外,只有在
NSFetchedResultsController
上设置了
sectionNameKeyPath
并且仅在子上下文中才存在问题

这是我设置“获取结果”控制器的方式,没有什么特别之处,因此我不知道我在这里会做错什么:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Contact"];
fetchRequest.sortDescriptors = [Contact userDefinedSortDescriptors];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"hidden == nil || hidden == NO"];

NSFetchedResultsController *fetchedResultsController = 
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                    managedObjectContext:_managedObjectContext
                                      sectionNameKeyPath:[Contact userDefinedSectionNameKeyPath]
                                               cacheName:@"ContactList"];
[联系userDefinedSortDescriptors]
[联系userDefinedSectionNameKeyPath]
在运行时解析。排序描述符包含sectionNameKeyPath作为第一个条目。两者都不能是
nil
或其他有趣的东西

编辑:澄清了一些模糊的部分。具体来说,我不会在文档管理对象上下文上调用-save:

编辑2:我将尝试解释主运行中心之间的关系

有三种托管对象上下文在起作用:

1) 通过加载文档创建的
UIManagedDocument.managedObjectContext

2) 有一个后台线程正在运行,它会不时更新对象。这是一个专用队列主运行中心,文档主运行中心作为父级:

context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
context.parentContext = repository.managedObjectContext;

[context performBlock:^{ /* updates */ }];
[context performBlock:^{ [context save:NULL]; }];
3) 当用户想要进行更改时,将创建一个新的MOC作为文档MOC的子级。这是一个主队列主运行中心。这是用于执行上述提取的上下文

后台更新通过NSOperationQueue完成,但对后台MOC的所有访问都被
-performBlock:
正确包围。所有其他访问都是从主线程完成的


编辑3:在处理
NSFetchRequest
上的一些设置时,我发现在设置
fetchRequest.includesPendingChanges=NO
时问题消失了。但这不是一个可行的解决方案,因为现在用户在UIManagedDocument将更改保存到后台之前不会再看到任何更新。首先,这个

数据在后台定期更新,更改保存到UIManagedDocument.managedObjectContext(使用performBlock:)中

UIManagedDocuments实现自动保存,我们应该直接调用保存方法的唯一时间是在初始创建时。所有其他“保存”应通过自动保存界面完成。基本上,那就是打电话

[document updateChangeCount:UIDocumentChangeDone];
如果使用撤消管理器,这将自动完成。因此,如果要直接输入document.managedContext,只需调用上述方法通知托管文档更改已完成,其他所有操作都应自动处理

第二,我不知道你说的是什么意思:

但一旦我在子上下文中显示列表

儿童语境是什么?为什么任意上下文会看到任何东西?它是UIManagedDocument主上下文的子上下文,还是父上下文,还是其他内容

文档很清晰(对于clear的某些定义),但不幸的是,它需要阅读UIDocument、UIManagedDocument以及所有NSManagedObjectContext内容的完整文档集

当我终于对正在发生的事情了如指掌时(讽刺的是,通过阅读iCloud的工作原理),我所有与UIManagedDocument相关的问题似乎都消失了

基本上,遵循以下简单规则:

  • 切勿直接在UIManagedDocument中嵌入的任何上下文上调用任何“save”方法

  • 当对象“脏”且需要保存时,请向撤消管理器注册更改,或直接调用[doc updateChangeCount:UIDocumentChangeDone]

  • 如果您想使用子上下文,请直接使用。在这种情况下,只需设置parentContext属性,并在子上下文上调用save:。其他一切都会自动发生

  • 编辑 另一个危险信号是您正在使用不同的上下文运行代码。如果是这种情况,您必须确保从正确的线程运行。NSManagedObjectContext对象不是线程安全的。因此,必须始终从创建它们的线程(如果是NSConfinementConcurrencyType)访问它们,如果是其他两种并发类型之一,则使用performBlock

    如果您使用多个上下文连续插入/获取,您将有一些问题需要解决。主要是,您必须确保您的更改被传播并计划保存,然后您的回迁执行您希望它们执行的操作

    插入完成后,请确保仅使用UIManagedDocument的子上下文调用save。切勿对托管文档调用save或savetoul。使用上述方法保存到它。这将确保正确传播更改

    获取内容时,您必须决定是否希望获取内容一直沿着上下文链进行。NSFetchRequest上有很多选项(如setShouldRefreshRefetchedObjects)。这些选项将决定fetch是只使用自己的上下文,还是转到后台存储以及其他许多内容。默认值通常是您想要的,但是在使用子上下文时,您有更多的责任

    此外,如果您有孩子,请继续