Objective c 移动节中的最后一行时UITableView endUpdates崩溃

Objective c 移动节中的最后一行时UITableView endUpdates崩溃,objective-c,ios,core-data,nsfetchedresultscontroller,Objective C,Ios,Core Data,Nsfetchedresultscontroller,我有一个UITableViewController,它由NSFetchedResultsController支持 我的NSFetchedResultsController基于布尔值将结果分为两部分 在后台线程中,数据源被更改,以便添加或删除行。 我的后台线程的NSManagedObjectContext合并正确,这种情况在大多数情况下都可以正常工作。数据更改时,行会发生变化,并通过动画在节之间移动 但是,有一种情况是,我的应用程序因EXC_坏访问而崩溃。 这种情况是将节中的最后一行移动到另一节。

我有一个UITableViewController,它由NSFetchedResultsController支持

我的NSFetchedResultsController基于布尔值将结果分为两部分

在后台线程中,数据源被更改,以便添加或删除行。 我的后台线程的NSManagedObjectContext合并正确,这种情况在大多数情况下都可以正常工作。数据更改时,行会发生变化,并通过动画在节之间移动

但是,有一种情况是,我的应用程序因EXC_坏访问而崩溃。 这种情况是将节中的最后一行移动到另一节。(下面是堆栈跟踪)。崩溃发生在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;
我的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
}
-(void)controllerWillChangeContent:(NSFetchedResultsController*)控制器{
//fetch控制器即将开始发送更改通知,因此请准备表视图以进行更新。
[self.tableView开始更新];
}
-(void)控制器:(NSFetchedResultsController*)控制器didChangeObject:(id)索引路径中的一个对象:(NSIndexPath*)变更类型的indexPath:(NSFetchedResultsChangeType)类型newIndexPath:(NSIndexPath*)newIndexPath{
UITableView*tableView=self.tableView;
开关(类型){
案例NSFetchedResultsChangesInsert:
[tableView InsertRowsatindExpath:[NSArray arrayWithObject:newIndexPath]带RowAnimation:UITableViewRowAnimationFade];
打破
案例NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]和RowAnimation:UITableViewRowAnimationFade];
打破
案例NSFetchedResultsChangeUpdate:
[self-configureCell:[tableView cellForRowAtIndexPath:indexPath]atIndexPath:indexPath];
打破
案例NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]和RowAnimation:UITableViewRowAnimationFade];
//重新加载节将插入新行,并确保标题得到适当更新。
[tableView重新加载节:[NSIndexSet IndexSetWithiIndex:NewIndexXPath.section]带RowAnimation:UITableViewRowAnimationFade];
打破
}
}
-(void)控制器:(NSFetchedResultsController*)控制器didChangeSection:(id)sectionInfo atIndex:(nsInteger)ChangeType的sectionIndex:(NSFetchedResultsChangeType)类型{
开关(类型){
案例NSFetchedResultsChangesInsert:
[self.tableView insertSections:[NSIndexSet IndexSetWithiIndex:sectionIndex]带RowAnimation:UITableViewRowAnimationFade];
打破
案例NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet IndexSetWithiIndex:sectionIndex]带RowAnimation:UITableViewRowAnimationFade];
打破
}
}
-(void)controllerDidChangeContent:(NSFetchedResultsController*)控制器{
//fetch控制器已发送所有当前更改通知,因此请告诉表视图处理所有更新。

[self.tableView endUpdates];//我对通过添加新节来解决问题的代码非常好奇,因此我决定将其放在当前NSFetchedResultsControllerDelegate方法的位置,它确实解决了我的问题。通过跟踪,我发现这段代码来自苹果的“位置”使用NSFetchedResultsController时的示例崩溃:

    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;
但以下代码可以完美地工作:

    case NSFetchedResultsChangeMove:
        if (newIndexPath != nil) {
            [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [self.tableView insertRowsAtIndexPaths: [NSArray arrayWithObject:newIndexPath] withRowAnimation: UITableViewRowAnimationTop];
        }
        else {
            [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[indexPath section]] withRowAnimation:UITableViewRowAnimationFade];
        }
        break;

我没有一个章节标题会改变的案例,我不会继续调试来解决苹果的代码声称要解决的关于章节标题的问题。

我快速查看了一下。你的修改位置项目没有在我身上崩溃,但它确实生成了核心数据异常。问题在于在controller: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;
    }
}

我删除了一个部分中的最后一行,导致超时崩溃

请确保已实现控制器:didChangeSection:atIndex:forChangeType:以处理节删除

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

    switch(type) {
        case NSFetchedResultsChangeInsert:
            [self.viewTable insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                          withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [self.viewTable deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                          withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}
-(void)控制器:(NSFetchedResultsController*)控制器didChangeSection:(id)sectionInfo
atIndex:(nsInteger)ChangeType:(NSFetchedResultsChangeType)类型的节索引{
开关(类型){
案例NSFetchedResultsChangesInsert:
[self.viewTable insertSections:[NSIndexSet IndexSetWithiIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
打破
案例NSFetchedResultsChangeDelete:
[self.viewTable deleteSections:[NSIndexSet IndexSetWithiIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
打破
}
}

因为每个问题都需要一个快速的答案,这里有一个(基于XJones接受的答案)。与其他答案一样,神奇之处在于。移动:删除并插入,而不是移动行

这是我的NSFRC委托对象:

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
    switch type {
    case .Delete:
        if let deletePath = indexPath {
            self.tableView.deleteRowsAtIndexPaths([deletePath], withRowAnimation: .None)
        }
        break
    case .Insert:
        if let insertPath = newIndexPath {
            self.tableView.insertRowsAtIndexPaths([insertPath], withRowAnimation: .Fade)
        }
        break
    case .Update:
        if let updatePath = indexPath {
            self.tableView.reloadRowsAtIndexPaths([updatePath], withRowAnimation: .None)
        }
        break
    case .Move:
        if let indexPath = indexPath, newIndexPath = newIndexPath {
            self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
            self.tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade)
        }
        break
    }
}

这并不能真正解决问题。删除移动的行并将其重新插入其他位置不会导致相同的错误,但也会丢失行动画。
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

    switch(type) {
        case NSFetchedResultsChangeInsert:
            [self.viewTable insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                          withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [self.viewTable deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                          withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
    switch type {
    case .Delete:
        if let deletePath = indexPath {
            self.tableView.deleteRowsAtIndexPaths([deletePath], withRowAnimation: .None)
        }
        break
    case .Insert:
        if let insertPath = newIndexPath {
            self.tableView.insertRowsAtIndexPaths([insertPath], withRowAnimation: .Fade)
        }
        break
    case .Update:
        if let updatePath = indexPath {
            self.tableView.reloadRowsAtIndexPaths([updatePath], withRowAnimation: .None)
        }
        break
    case .Move:
        if let indexPath = indexPath, newIndexPath = newIndexPath {
            self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
            self.tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade)
        }
        break
    }
}