5
votes

I'm having some trouble with core data / NSFetchedResultsController. I'm not entirely sure where the error is as the message is quite vague.

I have an issue with inserting more than one object when the fetched results controller has no objects fetched. The following code will crash with the following error if I try and insert several objects with none fetched already. It doesn't crash if I use it to insert one object, and it doesn't crash if there are already objects fetched.

The crash occurs on the save: method. titles in an NSArray and in this example it contains 5 strings.

Serious application error. Exception was caught during Core Data change processing: * -[NSCFArray objectAtIndex:]: index (4) beyond bounds (1) with userInfo (null) * Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index (4) beyond bounds (1)'

NSEnumerator *titleEnumerator = [titles objectEnumerator];
NSString *title;
NSMutableArray *tasks = [NSMutableArray array];
Todo *todo;

while(title = [titleEnumerator nextObject])
{
    todo = (Todo *)[NSEntityDescription insertNewObjectForEntityForName:@"Todo" inManagedObjectContext:managedObjectContext];
    todo.title = title;
    todo.state = [NSNumber numberWithInteger:TodoStateIncomplete];
    todo.priority = [NSNumber numberWithInteger:TodoPriorityNormal];
    todo.timeStamp = [NSDate date];
    todo.dueDate = [NSDate distantFuture];
}

NSError *error;

if(![managedObjectContext save:&error])
{
    NSLog(@"Unresolved error %@ %@", error, [error userInfo]);
    abort();
}
4
I've managed to get around the crash with a bit of a dodgy hack. In the loop I check if it's the first time round. If it is I save. This acts as though I'm just adding one object at first which prevents it crashing when I add many. This will get me by but I would still like to know why this is happening so I can fix it properly.Daniel Wood

4 Answers

5
votes

Here's a tip from Marcus Zarra (author of Core Data book):

Core Data error when deleting row in tableView

"Try breaking on objc_exception_throw and see what method is throwing the exception. That should help track it down"

4
votes

Errors like this on a Core Data save when using the iPhone SDK generally point at an error in the NSFetchedResultsController delegate methods. Specifically one of the example pieces of code from Apple is incorrect and frequently produces this error. I would suggest looking at your delegate methods and compare them with the latest example code as you may find that Apple has updated the example code in the documentation and if you re-copy their code this error can go away.

Hope that helps.

0
votes

I know it's unlikely to be the answer, but since the crash is occuring on save: and I do remember having an odd error with this once... The error I had was fixed this way, I reckon it's worth a shot.

So, with that said, try changing

NSError *error;

to

NSError *error = nil;
0
votes

In my case this implementation was helpful (thank's petert):

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller{ [self.tableView beginUpdates];  }

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller{
    [self.tableView endUpdates];}

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

        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
        break;

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

    case NSFetchedResultsChangeUpdate: {

        NSString *sectionKeyPath = [controller sectionNameKeyPath];
        if (sectionKeyPath == nil){
             break;
        }

        [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];

        break;
    }
    case NSFetchedResultsChangeMove: {

        if (newIndexPath != nil) {

            NSUInteger tableSectionCount = [self.tableView numberOfSections];
            NSUInteger frcSectionCount = [[controller sections] count];
            if (frcSectionCount > tableSectionCount)
                [self.tableView insertSections:[NSIndexSet indexSetWithIndex:[newIndexPath section]] withRowAnimation:UITableViewRowAnimationNone];
            else if (frcSectionCount < tableSectionCount && tableSectionCount > 1)
                [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:[indexPath section]] withRowAnimation:UITableViewRowAnimationNone];


            [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [self.tableView insertRowsAtIndexPaths: [NSArray arrayWithObject:newIndexPath]
                                  withRowAnimation: UITableViewRowAnimationRight];

        }
        else {
            [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[indexPath section]] withRowAnimation:UITableViewRowAnimationFade];
        }
        break;
    }
}}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type{
switch (type) {
    case NSFetchedResultsChangeInsert:
        if (!((sectionIndex == 0) && ([self.tableView numberOfSections] == 1)))
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
        break;
    case NSFetchedResultsChangeDelete:
        if (!((sectionIndex == 0) && ([self.tableView numberOfSections] == 1) ))
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];

        break;
    case NSFetchedResultsChangeUpdate:
        break;
    case NSFetchedResultsChangeMove:
        break;
}}