移动部分中的最后一行时UITableView endUpdates崩溃

Ale*_*lor 10 core-data objective-c nsfetchedresultscontroller ios

我有一个UITableViewController,它由NSFetchedResultsController支持.

我的NSFetchedResultsController根据布尔值将结果放入两个部分.

在后台线程中,更改数据源以便添加或删除行.我有我的后台线程的NSManagedObjectContext正确合并,这种情况适用于大多数情况.数据更改时行会更改,并在带动画的部分之间移动.

但有一种情况是我的应用程序崩溃了EXC_BAD_ACCESS.这种情况是一个部分中的最后一行移动到另一部分.(下面的堆栈跟踪).崩溃发生在objc_msgSend中,但我使用的正常调试提示没有返回任何有用的东西,我只是收到"值无法转换为整数" gdb.

NSFetchedResultsControllerDelegate方法按以下顺序调用:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller;
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type;
     // ^ The parameters specify a deletion of the 1st section
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath;
     // ^ The parameters specify a moved row from the 1st section to the 1st section 
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller;
Run Code Online (Sandbox Code Playgroud)

通过StackOverflow,许多遇到类似问题的人正在使用NSArray或NSMutableArray手动备份他们的UITableViewController,他们只是没有在正确的时间更新数据源.在这种情况下,NSFetchedResultsController正在处理返回的行数和节数,并且在[tableView endUpdates]调用时肯定会更新.

有没有人有任何调试技巧,指针或解决方案? 这篇博客文章提示解决创建新部分的类似问题.

如果我不能解决这个问题,我有两个选择:

  1. 在行上放弃动画并简单地调用 [tableView reloadData]
  2. 切换到使用NSArray支持的存储为UITableView,并希望最好的

更新

我修改了Apple示例Core Data应用程序"Locations"以直接使用NSFetchedResultsController并重现该问题. 我已经上传了这个项目的源代码.使用该项目重现该问题.

  1. 只需单击+按钮几次然后等待
  2. 每5秒将一个项目移动到另一个类别.
  3. 当第一类即将被清空时,它会崩溃.

崩溃有时是关于为该主题创建与其他StackOverflow问题相同的单元格的两个动画.更常见的是,碰撞如上下文所述.

参考

堆栈跟踪:

#0  0x31e0afbc in objc_msgSend ()
#1  0x32c11522 in -[_UITableViewUpdateSupport(Private) _computeRowUpdates] ()
#2  0x32c10510 in -[_UITableViewUpdateSupport initWithTableView:updateItems:oldRowData:newRowData:oldRowRange:newRowRange:context:] ()
#3  0x32c0f99e in -[UITableView(_UITableViewPrivate) _endCellAnimationsWithContext:] ()
#4  0x32c0e66c in -[UITableView endUpdatesWithContext:] ()
#5  0x000088d6 in -[DraftHistoryController controllerDidChangeContent:] (self=0x3f6f1e0, _cmd=0x377ca29c, controller=0x3f716e0) at /Users/ataylor/Documents/Documents/Programming/iPhone/Drafter/Drafter/Drafter/DraftHistoryController.m:323
#6  0x3775a892 in -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] ()
Run Code Online (Sandbox Code Playgroud)

我的NSFetchedResultsControllerDelegate方法与Apple示例代码中的方法相同:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    // The fetch controller is about to start sending change notifications, so prepare the table view for updates.
    [self.tableView beginUpdates];
}


- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

    UITableView *tableView = self.tableView;

    switch(type) {

        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

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

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        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;
    }
}


- (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)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    // The fetch controller has sent all current change notifications, so tell the table view to process all updates.
    [self.tableView endUpdates];     // <---- Crash occurs here
}
Run Code Online (Sandbox Code Playgroud)

相关的UITableViewControllerDelegate方法如下:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [[self.fetchedResultsController sections] count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}
Run Code Online (Sandbox Code Playgroud)

XJo*_*nes 6

我快速看了一下.您修改后的Locations项目没有崩溃,但它确实生成了核心数据异常.问题在于重新加载控制器中的部分:didChangeObject:.我改变了代码如下,iOS 4.3和iOS 5上的一切看起来都很好(包括章节标题).

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
    UITableView *tableView = self.tableView;

    switch(type) {

        // other cases here

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}
Run Code Online (Sandbox Code Playgroud)