NSFetchedResultsController 委托异常

Posted

技术标签:

【中文标题】NSFetchedResultsController 委托异常【英文标题】:NSFetchedResultsController delegate exception 【发布时间】:2012-07-31 17:18:15 【问题描述】:

我有一个应用程序,它使用表格视图来显示我的核心数据中的项目列表。我正在使用远程 api,并且在拉下表格视图后我正在更新我的内容 - 这会触发对 API 的调用。

数据被检索、解析并插入/更新到我的核心数据中。

有时在保存我的核心数据上下文后遇到错误...请注意,我没有为此使用多个线程,就像我说的那样,它似乎并不总是发生。

我真的快疯了。看来这个人也有类似的问题,但我仍然无法用他的解决方案解决我的问题: CoreData error driving me crazy... CoreData: Serious application error. An exception caught from delegate of NSFetchedResultsController

这是完整的错误:

2012-07-31 14:14:47.332 MyApp[2893:11303] 
*** Assertion failure in -[_UITableViewUpdateSupport _setupAnimationsForNewlyInsertedCells], 
/SourceCache/UIKit_Sim/UIKit-1914.84/UITableViewSupport.m:1133
2012-07-31 14:14:47.332 MyApp[2893:11303] CoreData: error: Serious application error.  
An exception was caught from the delegate of NSFetchedResultsController during a call to -
controllerDidChangeContent:.  
Attempt to create two animations for cell with userInfo (null)

更新

我的提取请求有一个谓词。为了似乎删除以前从 API 下载的对象,而新的 JSON 结果中缺少这些对象。我正在设置一个 hideFromUser 标志,它保存在我的核心数据中。

如果此标志为 YES,则它不会出现在表格视图中。但如果没问题,那就可以了。如果有任何更改,我也会更新有关该托管对象的信息。是否有可能我有一个以前设置为隐藏的对象......现在设置为显示,并且它还有一些新数据,这是否可能触发“单元格应该更新”和“单元格应该插入”?

我想得越多,它似乎就越不相关。

这是我更新数据的方式:

1)我将相应类型的所有相关对象设置为“隐藏表单用户”(NSPredicate 确保它们不会显示在表格视图中)。

2) 我从 JSON 数据中得到一个 NSArray。

3) 循环每个项目,我的 createABookOfClass:withJSON: 方法查询一本书的核心数据(使用 json 字典中的 ID),如果找不到,它会创建一个新的。注意:此时“隐藏用户标志”已恢复。

4) 全部完成后,我保存。

[[DPLocalStore getInstance] hideFlagItemsOfType:NSStringFromClass([MyFavouriteBook class])];

NSArray * itemsJSON = [data mutableObjectFromJSONData];

[itemsJSON enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop)      
    [[DPLocalStore getInstance] createABookOfClass:[MyFavouriteBook class]
                                          withJSON:obj];
];

NSError *error = nil;
BOOL didsave = [[DPLocalStore getInstance] save:&error];

也许正在发生的事情是包含对象 A 的单元格已更新,它是更新:隐藏标志已更改。因此,我遇到了 NSFetchedResultsController 的代表想要更新该单元格并删除它的情况......因为谓词现在不对应于这个对象......这听起来很可能......

【问题讨论】:

你能贴出插入/更新数据的代码吗? 这真的只是简单地解析一个 json 数组并创建一堆 nsmanagedobjects,在循环之后我保存上下文。这里没有发生什么特别的事情。也许如果我让你访问我的 BitBucket 存储库?也许这两个动画适用于一个单元格......我只是有一个想法,请检查我更新的问题。谢谢。 Het Martin,我应该像你说的那样发布更多代码,检查我的答案。干杯。 【参考方案1】:

我想我可能已经找到了错误,因为我更改了它,所以我没有遇到问题,所以我假设它没问题。

在我的controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: 实现中,我在NSFetchedResultsChangeMove 的switch 语句中有这个:

case NSFetchedResultsChangeMove:
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                     withRowAnimation:UITableViewRowAnimationFade];

    // Reloading the section inserts a new row and ensures that titles are updated appropriately.
    [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section]
             withRowAnimation:UITableViewRowAnimationFade];
    break;

由于它在一个较旧的项目中使用(我不是其中的一部分),我认为整个 controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: 实现是好的,因为这实际上更像是一个模板,而不是你可以做任何定制的东西。

因此,直到我在 Apple 开发者网站上再次阅读它时,我才注意到它。我注意到,如果是NSFetchedResultsChangeMove,您必须删除一个单元格,并将该单元格插入到新路径中。然而我在没有真正意识到的情况下实现了 - 删除单元格并重新加载该部分!

实际上我应该并且现在拥有以下内容:

case NSFetchedResultsChangeMove:
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                     withRowAnimation:UITableViewRowAnimationFade];
    [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                     withRowAnimation:UITableViewRowAnimationFade];
    break;

就像我说的那样,从那以后我就再也没有遇到过问题。很想从任何人那里知道是什么内部工作对此负责 - 例外是“尝试为单元创建两个动画”。

感谢您的关注。

【讨论】:

我猜我们复制和使用了一些教程或源代码。因为这是样板文件......苹果也有(正确的)实现在这里developer.apple.com/library/ios/#documentation/CoreData/… 这段代码还是错误的。您应该围绕您的 tableview 操作调用 beginUpdate 和 endUpdate 以进行插入/更新/删除。他们嵌套这些 beginUpdate/endUpdates 来计时同步表视图动画。这就是您收到重复动画错误的原因。 @Derek 即使分别在-controllerWillChangeContent:-controllerDidChangeContent: 中调用beginUpdatesendUpdates,我也遇到了同样的问题。并且异常仍然出现。提供的解决方案是正确的。 那是因为将它们放在 -controllerWillChangeContent: 和 -controllerDidChangeContent: 中是放置它们的错误位置。您将它们放在 -controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: 中,以便它按正确的顺序放置。 will 和 did 实现仅在批量更改时调用一次,因此无法正确嵌套。您必须将它们放在 didChangeObject 实现中。

以上是关于NSFetchedResultsController 委托异常的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 应用程序中调用 performFetch 后,是不是需要手动更新表视图?