更新:在评论中有人指出我不必要地分派(dispatch)到主线程。在删除调度和不必要的 begin/end updates 之后,现在当我尝试删除一个单元格时,它会调用 didChangeObject 与 case NSFetchedResultsChangeUpdate (相反到 NSFetchedResultsChangeDelete ),它调用 configureCell 。
使程序崩溃的行是 CollectedLeaf* theCollectedLeaf = [collectionFetchedResultsController objectAtIndexPath:indexPath]; 在下面的方法中。崩溃日志是,节列表中索引 20 处没有节 。
- (void)configureCellSpeciesCell *)cell atIndexPathNSIndexPath *)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)controllerWillChangeContentNSFetchedResultsController *)controller
{
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[_table beginUpdates];
}
- (void)controllerNSFetchedResultsController *)controller didChangeObjectid)anObject
atIndexPathNSIndexPath *)indexPath forChangeTypeNSFetchedResultsChangeType)type
newIndexPathNSIndexPath *)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)controllerNSFetchedResultsController *)controller didChangeSectionid <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 = @"ress 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];
}
Best Answer-推荐答案 strong>
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
}
}
正如链接帖子所述,
"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."
关于ios核心数据: make sure object is deleted from data source,我们在Stack Overflow上找到一个类似的问题:
https://stackoverflow.com/questions/47340419/
|