I have an application with basic data with 2 views. The first view lists the "Rooms", the second - "Scenes" in the rooms. There is a NavItem edit button on the Rooms page, which, when clicked, allows you to add a NavItem button. You can delete and add numbers from here. Added rooms simply appear with the name "New Room" by default in the table. The second view is a list of scenes in the selected room. Here you can delete and add scenes, and added scenes simply appear in the table with the name "New Scene". Nothing special.
I use FetchedResultsController in both view controllers, and in Scenes that has NSPredicate , only scenes from the selected room are returned. I also use the delegate methods controllerWillChangeContent , controllerDidChangeContent , etc. For table view updates.
At first, everything works fine, but usually after navigating through rooms and scenes, and then trying to delete a scene, it will work. This will inevitably fail if you play with it long enough. This only happens when a scene is deleted. If you press the edit button and delete the scene, and it will work, then all subsequent deletions in this editing session will always work. This will only happen the first time you delete an editing session.
The error I get is strange:
Application termination due to an uncaught exception 'NSInvalidArgumentException', reason: '- [__ NSCFType controllerWillChangeContent:]: unrecognized selector sent to instance 0x5e02d70
The first part of this error sometimes changes. Sometimes it is __NSCFType , sometimes it is CALayer . This error occurs only when deleting episodes. Adding scenes is a 100% penalty.
I read another post about stackoverflow, which suggests that such errors may occur due to memory management issues. I double-checked the code, and also ran it using tools with a leak tool. No leaks.
Is there anything else I can check? Any ideas?
Here is the corresponding code ..
From ScenesTableViewController.m:
// used to show/hide the add button - (void)setEditing:(BOOL)editing animated:(BOOL)animate { [super setEditing:editing animated:animate]; if(editing) { self.navigationItem.leftBarButtonItem = addButton; } else { self.navigationItem.leftBarButtonItem = nil; } } // called when the add button is pressed - (void)addAction { NSEntityDescription *myContentEntity = [NSEntityDescription entityForName:@"Scene" inManagedObjectContext:managedObjectContext]; Scene *contentToSave = [[Scene alloc] initWithEntity:myContentEntity insertIntoManagedObjectContext:managedObjectContext]; [contentToSave setValue:@"New Scene" forKey:@"Name"]; [parentRoom addRoomToScenesObject:contentToSave]; NSError *error; if (![managedObjectContext save:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); exit(-1); } [contentToSave release]; } // delegate method that being sent unrecognised selectors - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { // The fetch controller is about to start sending change notifications, so prepare the table view for updates. [self.tableView beginUpdates]; } // cell display code - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = nil; cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (!cell) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease]; [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator]; } [self configureCell:cell atIndexPath:indexPath]; return cell; } - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { NSManagedObject *mo = nil; NSString *temp = nil; mo = [fetchedResultsController objectAtIndexPath:indexPath]; temp = [mo valueForKey:@"Name"]; [[cell textLabel] setText:temp]; } // cell editing code - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { // Delete the managed object at the given index path. [managedObjectContext deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]]; NSError *error; if (![managedObjectContext save:&error]) { // Update to handle the error appropriately. NSLog(@"Unresolved error %@, %@", error, [error userInfo]); exit(-1); // Fail } } else if (editingStyle == UITableViewCellEditingStyleInsert) { // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view } } // NSFetchedResultsController code - (NSFetchedResultsController *)fetchedResultsController { if (fetchedResultsController != nil) { return fetchedResultsController; } /* Set up the fetched results controller. */ // Create the fetch request for the entity. NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; // Edit the entity name as appropriate. NSEntityDescription *entity = [NSEntityDescription entityForName:@"Scene" inManagedObjectContext:managedObjectContext]; NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"Name" ascending:YES]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:nameDescriptor, nil]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(SceneToRoom == %@)", parentRoom]; [fetchRequest setSortDescriptors:sortDescriptors]; [fetchRequest setPredicate:predicate]; [fetchRequest setEntity:entity]; // Edit the section name key path and cache name if appropriate. // nil for section name key path means "no sections". NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil]; aFetchedResultsController.delegate = self; self.fetchedResultsController = aFetchedResultsController; [aFetchedResultsController release]; [fetchRequest release]; [nameDescriptor release]; [sortDescriptors release]; return fetchedResultsController; }