【问题标题】:ios Core Data: make sure object is deleted from data sourceios Core Data:确保从数据源中删除对象
【发布时间】:2017-11-16 22:47:15
【问题描述】:

更新:在 cmets 中,有人指出我不必要地分派到主线程。删除调度和不必要的begin/end updates 后,现在当我尝试删除一个单元格时,它会调用didChangeObjectNSFetchedResultsChangeUpdate(而不是NSFetchedResultsChangeDelete),后者调用configureCell

在下面的方法中,导致程序崩溃的行是CollectedLeaf* theCollectedLeaf = [collectionFetchedResultsController objectAtIndexPath:indexPath];。崩溃日志是no section at index 20 in sections list

- (void)configureCell:(SpeciesCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
    CollectedLeaf* theCollectedLeaf = [collectionFetchedResultsController objectAtIndexPath:indexPath];
    [cell setCollectedLeaf:theCollectedLeaf];
}

每次滑动以从表格中删除单元格时,我都会收到 Invalid update: invalid number of rows in section 错误。我在controllerDidChangeContent 中的[_table endUpdates] 处获得断点。

在许多 SO 帖子中,出现此错误的原因是在从表中删除行之前尚未从数据源中删除对象。我从commitEditingStyle中的数据源中删除了对象,该对象在didChangeObject中的deleteRowsAtIndexPaths之前调用。

尽管我下了订单,但我仍然收到 Invalid update 的事实让我认为我没有正确/成功地从数据源中删除它。我还是 iOS 新手 - 如何确保从我的核心数据中删除对象?

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

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

        case NSFetchedResultsChangeInsert:
            [_table beginUpdates];

            [_table insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                          withRowAnimation:UITableViewRowAnimationFade];
            [_table endUpdates];
            break;

        case NSFetchedResultsChangeDelete:
            NSLog(@"delete called");
            [_table deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                          withRowAnimation:UITableViewRowAnimationFade];


            break;

        case NSFetchedResultsChangeUpdate:
           // [self configureCell:[_table cellForRowAtIndexPath:indexPath]
                   // atIndexPath:indexPath];
            [_table reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            NSLog(@"fetched update");
            break;

        case NSFetchedResultsChangeMove:
            [_table deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                          withRowAnimation:UITableViewRowAnimationFade];
            [_table insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                          withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

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

        case NSFetchedResultsChangeDelete:
            [_table deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeMove:
            break;
        case NSFetchedResultsChangeUpdate:
            break;
    }
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    [_table endUpdates];
}
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(_segmentedControl.selectedSegmentIndex == 1) {
        NSLog(@"index path at editingStyleForRowAtIndexPath %@", indexPath);
        return UITableViewCellEditingStyleDelete;
    }
    return NULL;
}

- (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
    // [super tableView: tableView didEndEditingRowAtIndexPath:indexPath];
    if(_segmentedControl.selectedSegmentIndex == 1) {
        for (UITableViewCell *cell in [_table visibleCells]) {
            for (UIView *subview in cell.contentView.subviews)
                [subview.layer removeAllAnimations];
        }
    }
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    /*Only allow deletion for collection table */
    if(_segmentedControl.selectedSegmentIndex == 1) {
        if (editingStyle == UITableViewCellEditingStyleDelete)
        {
            NSLog(@"index path at commitediting style %@", indexPath);
            CollectedLeaf* collectedLeaf = [collectionFetchedResultsController objectAtIndexPath:indexPath];
            LeafletPhotoUploader * leafletPhotoUploader = [[LeafletPhotoUploader alloc] init];
            leafletPhotoUploader.collectedLeaf = collectedLeaf;

            if([LeafletUserRegistration isUserRegistered]) {
                [leafletPhotoUploader deleteCollectedLeaf:collectedLeaf delegate:self];
            }

            // Delete the managed object for the given index path
            NSManagedObjectContext *context = [collectionFetchedResultsController managedObjectContext]; 
                [context deleteObject:[collectionFetchedResultsController objectAtIndexPath:indexPath]];

            // Save the context.
            NSError *error;
            if (![context save:&error])
            {
                NSLog(@"Failed to save to data store: %@", [error localizedDescription]);
                NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
                if(detailedErrors != nil && [detailedErrors count] > 0)
                {
                    for(NSError* detailedError in detailedErrors)
                    {
                        NSLog(@"  DetailedError: %@", [detailedError userInfo]);
                    }
                }
                else
                {
                    NSLog(@"  %@", [error userInfo]);
                }
            }
        }
    }
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"speciesCell";

    SpeciesCell* speciesCell = (SpeciesCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    [self configureCell:speciesCell atIndexPath:indexPath];
    speciesCell.labelCheckMark.backgroundColor = [UIColor blackColor];

    return speciesCell;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    return 1;
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{

    id <NSFetchedResultsSectionInfo> sectionInfo = [[collectionFetchedResultsController sections] objectAtIndex:section];
    if([sectionInfo numberOfObjects] == 0){
        UILabel *noDataLabel         = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, tableView.bounds.size.height)];
        noDataLabel.text             = @"Press the Snap It! tab to start collecting!";
        //Start your collection by tapping on the Snap It! tab.";
        noDataLabel.textColor        = [UIColor whiteColor];
        noDataLabel.textAlignment    = NSTextAlignmentCenter;
        tableView.backgroundView = noDataLabel;
        tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    }
    return [sectionInfo numberOfObjects];
}

【问题讨论】:

  • 为什么你在主线程上调度几乎所有东西?您的代码已经在主线程上运行。问题是你调度删除一个对象然后保存上下文,当删除完成时你的上下文没有保存。
  • @kirander 我更新了我的帖子,谢谢!
  • 显示 cellForRow、numberOfSections 和 numberOfCells 方法
  • numberOfCells?我有numberOfRowsInSection
  • @Matt 您是否尝试添加一个异常断点来确定是哪一行引发了此错误?

标签: ios objective-c uitableview core-data nsfetchrequest


【解决方案1】:

This post saved me

objectAtIndexPath 有很多问题,只能在边界检查中调用,所以我现在使用嵌套 if 语句中的核心数据对象完成所有工作

    CollectedLeaf* theCollectedLeaf = nil;
    if ([[collectionFetchedResultsController sections] count] > [indexPath section]){
        id <NSFetchedResultsSectionInfo> sectionInfo = [[collectionFetchedResultsController sections] objectAtIndex:[indexPath section]];
        if ([sectionInfo numberOfObjects] > [indexPath row]){
            theCollectedLeaf = [collectionFetchedResultsController objectAtIndexPath:indexPath];

        //do whatever else I need, delete the object, etc
        }

    }

正如链接帖子所述,

"你得到一个异常的原因是 NSFetchedResultsController 与状态不同步 表视图。当你的崩溃发生时,tableView 只是问你 委托方法(numberOfSectionsInTableView 和 tableView:numberOfRowsInSection:用于行数和节数。 当它询问时, NSFetchedResultsController 中有数据,并且 给出正值(1 节,3 行)。但在这之间 发生和你的崩溃,由 NSFetchedResultsController 已从 NSManagedObjectContext。现在 cellForRowAtIndexPath: 被调用 带有过时的 indexPath 参数。”

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 2011-01-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多