【发布时间】:2013-06-03 21:13:27
【问题描述】:
我有 2 个托管对象上下文:(1) 创建为 NSMainQueueConcurrencyType,供 UI/主线程使用;(2) 创建为 NSPrivateQueueConcurrencyType,供网络使用。这两个上下文都进入持久存储(即,我没有使用父/子上下文)。
对于视图控制器,我使用 UITableViewController 和 NSFetchedResultsController,它使用第一个 UI 托管对象上下文。
我正在通过观察NSManagedObjectContextDidSaveNotification 将第二个托管对象上下文中的更改合并到第一个上下文中。
应用程序正常工作,直到它处理导致插入新对象的网络响应并在第二个上下文中删除现有对象。保存第二个上下文时,NSManagedObjectContextDidSaveNotification 会触发并将更改合并到第一个上下文中。调用NSFetchedResultsController 的委托方法并向表中添加了新行,但表示已删除对象的行*没有删除。
如果我尝试在表视图上执行其他操作,例如重新加载表或更新其他对象,我会在控制台日志中得到以下断言:
*** 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 正在完成所有工作。我的委托方法是样板:
- (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
}
【问题讨论】:
标签: ios objective-c core-data