user480777
user480777

Reputation: 31

App is crashing when deleting from UITableView due to self referencing connection

I'm brand new to iPhone development (and first question posted here) and am sort of stuck with Core Data and Table Views.

In short, my app is crashing when I delete a row from my UITableView due to NSFetchedResultsChangeUpdate being called on a record that has already been removed due to a cascade delete on a self referring table.

Here is a description of the data model:

There are two Entities; Person and Connection.

Person contains name (String), connections (To Many Relationship to Connection->source, cascade delete rule) and connectedby (To Many Relationship to Connection->connection, cascade delete rule)

Connection contains relationship (String), source (Relationship to Person->connections, nullify delete rule) and connection (Relationship to Person->connectedby, nullify delete rule)

The idea being that there are two people connected by a relationship (eg Mother or Son)

In my TableViewController I implement the following:

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

and

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
    UITableView *tableView = self.tableView;
    Person *person = nil;
    switch(type) {
    case NSFetchedResultsChangeInsert:
        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];         
        break;
    case NSFetchedResultsChangeDelete:
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;
    case NSFetchedResultsChangeUpdate:
        person = (Person *)[fetchedResultsController objectAtIndexPath:indexPath];
        [self configureCell:(PersonTableViewCell *)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
        break;          
    case NSFetchedResultsChangeMove:
        person = (Person *)[fetchedResultsController objectAtIndexPath:indexPath];
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;
    } 
}

and

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

Here are the sample records I created for testing this:

Person *person1 = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.managedObjectContext];
[person1 setName:[NSString stringWithFormat: @"Tommy"]];

Person *person2 = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.managedObjectContext];
[person2 setName:[NSString stringWithFormat: @"Jane"]];

Connection *connection = [NSEntityDescription insertNewObjectForEntityForName:@"Connection" inManagedObjectContext:managedObjectContext];
[connection setConnection:person2];
[connection setSource:person1];
[connection setRelationship:[NSString stringWithFormat:@"Mother"]];

Connection *connection2 = [NSEntityDescription insertNewObjectForEntityForName:@"Connection" inManagedObjectContext:managedObjectContext];
[connection2 setConnection:person1];
[connection2 setSource:person2];
[connection2 setRelationship:[NSString stringWithFormat:@"Son"]];

When I delete the record at indexPath[0,0] i.e. Jane in this example since the view is sorted by name, I generate the following error:

2010-10-19 16:09:01.461 HelpMe[6324:207] 
Serious application error.  
Exception was caught during Core Data change processing.  
This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.
*** -[NSMutableArray objectAtIndex:]: index 1 beyond bounds [0 .. 0] with userInfo (null)
Detected an attempt to call a symbol in system libraries that is not present on the iPhone:
_Unwind_Resume called from function -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] in image CoreData.

The delete seems to correctly generate a NSFetchedResultsChangeDelete for indexPath [0,0] but also then immediately generates a NSFetchedResultsChangeUpdate for [0,1] which no longer exists since [0,1] is seemingly now in [0,0] after the delete.

Without the associated Connection record, it deletes fine.

I can seemingly work around this by simply calling [self.tableView reloadData] on controllerDidChangeContent instead of implementing begin/end updates and didChangeOnject: but I do not believe this is the proper way to handle this.

I appreciate any help anyone can offer.

Upvotes: 3

Views: 1105

Answers (2)

Paul Kramer
Paul Kramer

Reputation: 1

At the top of - (void)controller:(NSFetchedResultsController *)controller didChangeObject: I was fetching the entity out of the fetchedResultsController. Well, that really messes up things when you have just deleted the object and need to update the table. I moved the entity fetch into the NSFetchedResultsChangeUpdate portion. This then kept me from crashing out.

Upvotes: 0

Madmax
Madmax

Reputation: 21

I resolved a similar issue this by providing a non-nil sectionNameKeyPath to NSFetchedResultsController (in initWithFetchRequest).

I then avoided sections by using:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    return @"";
}

Very annoying.

FYI, my issue was this error:

Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. * -[NSMutableArray objectAtIndex:]: index 0 beyond bounds for empty array with userInfo (null)

Upvotes: 2

Related Questions