ios核心数据:确保从数据源中删除对象

时间:2022-12-04 15:51:53

UPDATE: In the comments someone pointed out that I was unnecessarily dispatching to the main thread. After removing the dispatches and unnecessary begin/end updates, now when I try to delete a cell, it calls didChangeObject with case NSFetchedResultsChangeUpdate (as opposed to NSFetchedResultsChangeDelete), which calls configureCell.

更新:在评论中有人指出我不必要地调度到主线程。删除调度和不必要的开始/结束更新后,现在当我尝试删除一个单元格时,它调用带有NSFetchedResultsChangeUpdate(而不是NSFetchedResultsChangeDelete)的函数didChangeObject,它调用configureCell。

The line that crashes the program is CollectedLeaf* theCollectedLeaf = [collectionFetchedResultsController objectAtIndexPath:indexPath]; in the below method. The crash log is, no section at index 20 in sections list.

崩溃程序的行是CollectedLeaf * theCollectedLeaf = [collectionFetchedResultsController objectAtIndexPath:indexPath];在下面的方法中。崩溃日志是,部分列表中的索引20处没有任何部分。

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

I am getting an Invalid update: invalid number of rows in section error every time I swipe to delete a cell from my table. I get the breakpoint specifically at [_table endUpdates] in controllerDidChangeContent.

我收到一个无效更新:每次我从表中删除一个单元格时,部分错误中的行数无效。我在controllerDidChangeContent中的[_table endUpdates]中专门得到了断点。

In many SO posts, the reason for this error is that the object had not been deleted from data source before deleting the row from the table. I delete the object from the data source in commitEditingStyle, which is called before deleteRowsAtIndexPaths in didChangeObject.

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

The fact that I'm still getting the Invalid update despite my order leads me to think that I am not properly/successfully deleting it from the data source. I am still new to iOS - how can I make sure an object is removed from my core data?

事实上,尽管我的订单仍然无法获得无效更新,但我认为我没有正确/成功地从数据源中删除它。我还是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];
}

1 个解决方案

#1


0  

This post saved me

这篇文章救了我

objectAtIndexPath is pretty buggy and can only be called within a bounds check, so I now do all of my work with the core data object within the nest if statement

objectAtIndexPath很糟糕,只能在边界检查中调用,所以我现在用nest 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
        }

    }

As the linked post describes,

正如链接的帖子所描述的,

"The reason you are getting an exception is that the state of the NSFetchedResultsController is getting out of sync with the state of the tableview. When your crash happens the tableView just asked your delegate methods (numberOfSectionsInTableView and tableView:numberOfRowsInSection: for the number of rows and sections. When it asked that, the NSFetchedResultsController had data in it, and gave positive values (1 section, 3 rows). But in-between that happening and your crash, the entities represented by the NSFetchedResultsController were deleted from the NSManagedObjectContext. Now cellForRowAtIndexPath: is being called with an outdated indexPath parameter."

“你获得异常的原因是NSFetchedResultsController的状态与tableview的状态不同步。当你的崩溃发生时,tableView只询问你的委托方法(numberOfSectionsInTableView和tableView:numberOfRowsInSection:for the rows of rows当它询问时,NSFetchedResultsController中有数据,并给出了正值(1节,3行)。但是在发生这种情况和崩溃之间,NSFetchedResultsController表示的实体已从NSManagedObjectContext中删除了。正在使用过时的indexPath参数调用cellForRowAtIndexPath:“

#1


0  

This post saved me

这篇文章救了我

objectAtIndexPath is pretty buggy and can only be called within a bounds check, so I now do all of my work with the core data object within the nest if statement

objectAtIndexPath很糟糕,只能在边界检查中调用,所以我现在用nest 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
        }

    }

As the linked post describes,

正如链接的帖子所描述的,

"The reason you are getting an exception is that the state of the NSFetchedResultsController is getting out of sync with the state of the tableview. When your crash happens the tableView just asked your delegate methods (numberOfSectionsInTableView and tableView:numberOfRowsInSection: for the number of rows and sections. When it asked that, the NSFetchedResultsController had data in it, and gave positive values (1 section, 3 rows). But in-between that happening and your crash, the entities represented by the NSFetchedResultsController were deleted from the NSManagedObjectContext. Now cellForRowAtIndexPath: is being called with an outdated indexPath parameter."

“你获得异常的原因是NSFetchedResultsController的状态与tableview的状态不同步。当你的崩溃发生时,tableView只询问你的委托方法(numberOfSectionsInTableView和tableView:numberOfRowsInSection:for the rows of rows当它询问时,NSFetchedResultsController中有数据,并给出了正值(1节,3行)。但是在发生这种情况和崩溃之间,NSFetchedResultsController表示的实体已从NSManagedObjectContext中删除了。正在使用过时的indexPath参数调用cellForRowAtIndexPath:“