views:

785

answers:

2

Here is the exception:

Serious application error.  Exception was caught during 
Core Data change processing: *** -[NSCFArray removeObjectAtIndex:]: 
index (0) beyond bounds (0) with userInfo (null)

Here is the relevant code:

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath 
{
    NSMutableArray *array = [[fetchedResultsController fetchedObjects] mutableCopy];

    id objectToMove = [[array objectAtIndex:fromIndexPath.row] retain];
    [array removeObjectAtIndex:fromIndexPath.row];
    [array insertObject:objectToMove atIndex:toIndexPath.row];
    [objectToMove release];

    for (int i=0; i<[array count]; i++) {
     [(NSManagedObject *)[array objectAtIndex:i] setValue:[NSNumber numberWithInt:i] forKey:JKChecklistRow];
    }
    [array release];
}

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller 
{
    [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 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)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView endUpdates];
}

I have a suspicion that the crash has something to do with the Core Data store getting out of sync with the unsaved context. If I save the context in the -tableView:moveRowAtIndexPath:fromIndexPath:toIndexPath: method, the program crashes faster, I haven't been able to figure out why, though.

+1  A: 

If you use sections while moving rows, NSFetchedResultsController and the table view get out o f sync immediately. I guess it's a bug in this class. So it's not the unsaved context in my experience.

One of the problems was, that the index paths are not up to date after the move and so the number of rows on that path is no longer correct, which leads to the "index beyond bounds". Let's say you have an index to (1,1) and remove the row at (1,1). The index points stil to (1,1), but the content for the section 1 is no longer the same etc. etc.

Just make it visible for you with something like NSUInteger tableSectionCount = [self.tableView numberOfSections]; NSUInteger frcSectionCount = [[controller sections] count]; NSLog(@"tableSectionCount: %d",tableSectionCount); NSLog(@"frcSectionCount: %d",frcSectionCount); and you will see.

Furthermore it's very difficult to find all the cases where the NSFRC is using NSFetchedResultsChangeMove or NSFetchedResultsChangeUpdate. It depends strongly from if it's necessary to reorder the rows. At the end you have to synchronise tabel view and NSFRC by yourself for every specific case. It costs me three days at the end to figure it out.

Very helpful is this: http://iphonedevelopment.blogspot.com/2009/11/one-fix-to-nsfetchedresultscontroller.html. I've sent the author some more findings, so I guess there will be updates on it.

But again: the key is keeping the sections up to date on your own.

Good luck! Gerd

Gerd
Thanks for the help! Since I was moving the rows while in Edit Mode my solution was simpler than that, but I'm sure Jeff's code will come in handy in the future.
kubi
A: 

I figured it out. Actually, Jeff LaMarche figured it out.

The bit of code that saved me:

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    // In the simplest, most efficient, case, reload the table view.
    if (!self.tableView.editing) 
        [self.tableView reloadData];
}

Since I was moving these rows around in the table-view itself, the view does already reflects the changes. When I updated my data source, my delegate would try and rearrange rows that had already been moved, causing the crash.

kubi
Oh snap. One caveat to this solution is that, apparently, swipe-to-delete puts the table-view into edit mode. My if statement causes the data to not be reloaded now. Fixed one issue, caused another. :-)
kubi