Core data 删除搜索栏表视图中的单元格会导致执行两次删除

Core data 删除搜索栏表视图中的单元格会导致执行两次删除,core-data,ios7,tableview,searchbar,Core Data,Ios7,Tableview,Searchbar,我用NSFetchedResultsController实例备份了一个由核心数据备份的表视图。 此表有一个显示筛选数据的搜索栏。搜索栏有一个单独的NSFetchedResultsController(FRC from now)实例 到目前为止,数据是按预期在表视图中获取和显示的,并且在搜索栏的表视图中也正确显示(搜索数据时) 我的问题是,如果我试图删除搜索栏表视图中的单元格,则会出现coredata异常: 错误:严重的应用程序错误。从中捕获了一个异常 调用时NSFetchedResultsCon

我用
NSFetchedResultsController
实例备份了一个由核心数据备份的表视图。 此表有一个显示筛选数据的搜索栏。搜索栏有一个单独的
NSFetchedResultsController
(FRC from now)实例

到目前为止,数据是按预期在表视图中获取和显示的,并且在搜索栏的表视图中也正确显示(搜索数据时)

我的问题是,如果我试图删除搜索栏表视图中的单元格,则会出现
coredata
异常:

错误:严重的应用程序错误。从中捕获了一个异常 调用时NSFetchedResultsController的委托 -controllerDidChangeContent:。尝试从节0中删除行0,该节在使用userInfo(null)进行更新之前仅包含0行

进一步检查表明,FRC的
controllerWillChangeContent
方法在一个单元格删除时被调用两次!。这会导致对同一单元格调用两次
deleteRowsAtIndexPaths
(从而导致coredata异常)

再多玩几次,我发现这个问题在搜索后第一次显示搜索栏的表视图时就出现了。(即使返回并删除常规表视图中的单元格,也会出现问题)

我已确保在代码中只调用一次
deleteRowsAtIndexPaths
方法(在FRC的
didChangeObject
delegate方法中)

现在我不确定要显示哪一个代码,所以我不会把它都说出来。让我知道你需要看哪一个,如果你有什么问题的想法

这是指示表格删除行的代码:

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

    DLog(@"didChangeObject type %lu with object %@", (unsigned long)type, anObject);
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

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

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

        case NSFetchedResultsChangeMove:
            [self.tableView deleteRowsAtIndexPaths:[NSArray
                                               arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [self.tableView insertRowsAtIndexPaths:[NSArray
                                               arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}
在删除选定单元格的对象后保存上下文时,将调用此方法

保存上下文的方法:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [_managedObjectContext deleteObject:[_notesFetcher objectAtIndexPath:indexPath]];
        NSError *error;
        if (![_managedObjectContext save:&error]) {
            DLog(@"Failed deleting object : %@", [error localizedDescription]);
        }
    }   
}
出于某种原因,此方法只调用一次,但对同一单元格使用相同的更改类型(2-Delete)调用两次
didChangeObject

更新

在方法didChangeObject中记录堆栈跟踪时,我得到以下结果:

[UITableView AnimatedElectionFrowWithCell:+107 12 UIKit
0x01316a35-[UITableViewCell\u swipeDeleteButtonPushed]+70 13 libobjc.A.dylib 0x02346874-[NSObject 性能选择器:withObject:withObject:+77 14 UIKit
0x010a8c8c-[UIApplication sendAction:to:from:forEvent:][108 15 UIKit 0x010a8c18-[UIKit应用 sendAction:toTarget:fromSender:forEvent:+61 16 UIKit
0x011a06d9-[UIControl发送操作:发送到:forEvent:+66 17 UIKit
0x011a0a9c-[UIControl\u发送操作预防:带事件:][577 18 UIKit 0x0119fd4b-[UIControl touchesEnded:withEvent:+641 19 UIKit
0x0141ad7f\u UIgestureRecognitizerUpdate+7166 20 UIKit
0x010e5d4a-[UIWindow\u sendGesturesForEvent:+1291 21 UIKit
0x010e6c6a-[UIWindow sendEvent:+1030 22 UIKit
0x010baa36-[UIApplication sendEvent:+242 23 UIKit
0x010a4d9f_UIApplicationHandleEventQueue+11421


如您所见,表的
swipeDeleteButtonPushed
方法在一次删除操作中被调用两次,正如我前面所述。如果我只按了一次删除按钮,怎么会触发两次?有什么想法吗?

您需要始终检查哪个控制器正在调用您的委托方法,并相应地采取行动。同样,您需要检查哪个
tableView
正在调用您的委托方法。假设您的
viewController
搜索获取的ResultsController和正常
获取的ResultsController
以及搜索表视图和正常表视图的
代理

所以你需要一些东西,比如

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{

    If (tableView == searchTableView) {
      // do stuff to searchTableView

    } else  {
      // do stuff to normalTableView

    }
}
或者在
fetchedResultsController
委托方法中(,我很确定这将是在同一个tableView上得到两个调用的地方)类似于

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

    If (controller == searchFetchedResultsController) {
       // do stuff to searchTableview

    } else {
       // do stuff to normaltableview

    }
}

您能显示删除表视图行的代码吗?我猜您在搜索和常规视图中使用的是2个FRC,具有相同的控制器。在“更新”方法中,您正在更新同一个表两次。@MartinR I添加了相关的where删除occurs@DanShelly事实上,我使用同一控制器作为两个FRC的委托,但在需要时我确实使用了正确的控制器(搜索和选择单元格会调用正确的序列,并显示正确的数据)我在某个地方读到,
self.tableView
始终指向当前表视图(即,如果显示搜索栏的表视图,则
self.tableView
将指向该表),并且应该在表的委托方法中使用。@giorashc:这不正确。您必须更新
self.tableView
(始终是“主”表视图)或
self.searchDisplayController.searchResultsTableView
,具体取决于搜索是否处于活动状态。所以@Dan似乎走上了正确的轨道。我确实检查了显示哪个表视图以使用正确的FRC。即使我从主表视图中删除(搜索并返回主表并删除单元格后),也会出现此问题您确定没有错误地从两个表视图中删除它吗,您应该能够从搜索表视图中删除,并且应该自动从主表视图中删除。请记住,当您删除对象时,两个FetchedResultsController都将调用其委托方法,这似乎正是您上面描述的,但是,您的代码没有检查哪个fetchedresultscontroller正在调用委托方法。上面列出的didChangeObject方法没有检查哪个fetchedresultscontroller正在调用委托方法哇,我肯定是这样做的