gpt4 book ai didi

ios - -[UITableView _endCellAnimationsWithContext :] with NSFetchedResultsController 中的断言失败

转载 作者:可可西里 更新时间:2023-11-01 05:05:59 26 4
gpt4 key购买 nike

我有 2 个托管对象上下文:(1) 创建为 NSMainQueueConcurrencyType,供 UI/主线程使用,(2) 创建为 NSPrivateQueueConcurrencyType,供 UI/主线程使用网络。这两个上下文都进入持久存储(即,我没有使用父/子上下文)。

对于 View Controller ,我使用一个 UITableViewController 和一个使用第一个 UI 管理对象上下文的 NSFetchedResultsController

我正在通过观察 NSManagedObjectContextDidSaveNotification 将第二个托管对象上下文中的更改合并到第一个上下文中。

应用程序工作正常,直到它处理网络响应,导致在第二个上下文中插入新对象删除现有对象。当第二个上下文被保存时,NSManagedObjectContextDidSaveNotification 触发并且更改被合并到第一个上下文中。 NSFetchedResultsController 的委托(delegate)方法被调用,一个新行被添加到表中,但是表示已删除对象的行*没有被删除。

如果我尝试对 TableView 执行其他操作,例如重新加载表或更新其他对象,我会在控制台日志中得到此断言:

*** Assertion failure in -[UITableView _endCellAnimationsWithContext:],
/SourceCache/UIKit/UIKit-2380.17/UITableView.m:1070

CoreData: error: Serious application error. An exception was caught
from the delegate of NSFetchedResultsController during a call to -
controllerDidChangeContent:. Invalid update: invalid number of rows in
section 0. The number of rows contained in an existing section after the
update (6) must be equal to the number of rows contained in that section
before the update (7), plus or minus the number of rows inserted or deleted
from that section (6 inserted, 6 deleted) and plus or minus the number of rows
moved into or out of that section (0 moved in, 0 moved out). with
userInfo (null)

通常,如果您在使用 UITableView 的批量更新方法时忘记更新您的模型对象,您会收到此错误,但在这种情况下,NSFetchedResultsController 正在做所有的工作。我的委托(delegate)方法是样板文件:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;

case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
NSLog(@" didChangeObject type=%d indexPath=%@ newIndexPath=%@", type, indexPath, newIndexPath);

UITableView *tableView = self.tableView;

switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;

case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;

case NSFetchedResultsChangeUpdate:
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
break;

case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}

我的 UITableViewDataSource tableView:cellForRowAtIndexPath 方法是:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}

[self configureCell:cell atIndexPath:indexPath];

return cell;
}

configureCell: 是:

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
Event *event = (Event *)[self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[event valueForKey:@"timeStamp"] description];

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Owner"];
NSArray *objects = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
cell.detailTextLabel.text = [objects.lastObject name]; // simplified
}

最佳答案

如果您在准备响应 tableView:cellForRowAtIndexPath: 的单元格时使用 NSFetchRequest

NSFetchedResultsController 会非常困惑。如果您不执行 NSFetchRequest,一切都很好。但是,如果您这样做,它会触发 NSFetchedResultsController 执行进一步的更改通知,这会对 UITableView 造成不良影响。

解决方法是在 NSFetchRequest 上设置 includesPendingChanges = NO

我已经打开了一个关于这个的雷达问题——问题 id 14048101——有一个详细的例子和示例应用程序。此错误在 iOS 5.1、6.0 和 6.1 上重现。

在我的示例应用程序中,我将日志记录添加到 Xcode 的 CoreData 模板以记录 NSFetchedResultsController 委托(delegate)方法的进入/离开。当我在网络上下文中插入和删除对象时,日志记录显示:

01:=>(之前)mergeChangesFromContextDidSaveNotification02:=>(输入)controllerWillChangeContent count=403:<=(离开)controllerWillChangeContent count=404: didChangeObject type=1 indexPath=(null) newIndexPath= 2 索引 [0, 0]05: => (输入) controllerDidChangeContent count=5

此时,一切都很好。 controllerDidChangeContent: 已被调用以处理 1 插入,它调用 [tableView endUpdates],它调用 tableView:cellForRowAtIndexPath:,它调用 configureCell:atIndexPath:.

06: =>(输入)在第 0 行配置单元格

此时,configureCell:atIndexPath: 创建了一个 NSFetchRequest 并调用了 [self.managedObjectContext executeFetchRequest:error:] —— 此处开始坏处。执行此获取请求会在插入处理完成之前触发上下文中剩余更改的处理(1 次删除和 3 次更新)(我们在第 05 行输入 controllerDidChangeContent: 直到第 16 行)。

07: => (输入) controllerWillChangeContent count=508:<=(离开)controllerWillChangeContent count=509: didChangeObject type=2 indexPath= 2 索引 [0, 4] newIndexPath=(null)10: didChangeObject type=4 indexPath= 2 索引 [0, 2] newIndexPath=(null)11: didChangeObject type=4 indexPath= 2 索引 [0, 1] newIndexPath=(null)12: didChangeObject type=4 indexPath= 2 索引 [0, 3] newIndexPath=(null)13: => (输入) controllerDidChangeContent count=4

此时,框架正在对controllerDidChangeContent: 进行重入调用。

14: <= (离开) controllerDidChangeContent count=415:<=(离开)在第 0 行配置单元格16:<=(离开)controllerDidChangeContent 计数=417:<=(在)mergeChangesFromContextDidSaveNotification

此时,您可以在 UI 中看到:(1) 添加了一个新单元格,(2) 更新了 3 个单元格,(3) 已删除的单元格仍然可见,这是错误的。

在 UI 中进一步操作后,我通常会收到 断言失败 或发送到无效对象异常的消息。

我的示例应用程序位于 https://github.com/peymano/CoreDataFetchedResultsController

关于ios - -[UITableView _endCellAnimationsWithContext :] with NSFetchedResultsController 中的断言失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16905935/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com