Hey everyone,
I'm relatively new to Core Data on iOS, but I think I've been getting better with it. I've been experiencing a bizarre crash, however, in one of my applications and have not been able to figure it out.
I have approximately 40 objects in Core Data, presented in a UITableView. When tapping on a cell, a UIActionSheet appears, presenting the user with a UIActionSheet with options related to the cell that was selected. So that I can reference the selected object, I declare an NSIndexPath in my header called "lastSelection" and do the following when the UIActionSheet is presented:
// Each cell has a tag based on its row number (i.e. first row has tag 0)
lastSelection = [NSIndexPath indexPathForRow:[sender tag] inSection:0];
NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:lastSelection];
BOOL onDuty = [[managedObject valueForKey:@"onDuty"] boolValue];
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Status" delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil];
if(onDuty) {
[actionSheet addButtonWithTitle:@"Off Duty"];
} else {
[actionSheet addButtonWithTitle:@"On Duty"];
}
actionSheet.actionSheetStyle = UIActionSheetStyleBlackOpaque;
// Override the typical UIActionSheet behavior by presenting it overlapping the sender's frame. This makes it more clear which cell is selected.
CGRect senderFrame = [sender frame];
CGPoint point = CGPointMake(senderFrame.origin.x + (senderFrame.size.width / 2), senderFrame.origin.y + (senderFrame.size.height / 2));
CGRect popoverRect = CGRectMake(point.x, point.y, 1, 1);
[actionSheet showFromRect:popoverRect inView:[sender superview] animated:NO];
[actionSheet release];
When the UIActionSheet is dismissed with a button, the following code is called:
- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex {
// Set status based on UIActionSheet button pressed
if(buttonIndex == -1) {
return;
}
NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:lastSelection];
if([actionSheet.title isEqualToString:@"Status"]) {
if([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:@"On Duty"]) {
[managedObject setValue:[NSNumber numberWithBool:YES] forKey:@"onDuty"];
[managedObject setValue:@"onDuty" forKey:@"status"];
} else {
[managedObject setValue:[NSNumber numberWithBool:NO] forKey:@"onDuty"];
[managedObject setValue:@"offDuty" forKey:@"status"];
}
}
NSError *error;
[self.managedObjectContext save:&error];
[tableView reloadData];
}
This might not be the most efficient code (sorry, I'm new!), but it does work. That is, for the first 25 items in the list. Selecting the 26th item or beyond, the UIActionSheet will appear, but if it is dismissed with a button, I get a variety of errors, including any one of the following:
[__NSCFArray section]: unrecognized selector sent to instance 0x4c6bf90
Program received signal: “EXC_BAD_ACCESS”
[_NSObjectID_48_0 section]: unrecognized selector sent to instance 0x4c54710
[__NSArrayM section]: unrecognized selector sent to instance 0x4c619a0
[NSComparisonPredicate section]: unrecognized selector sent to instance 0x6088790
[NSKeyPathExpression section]: unrecognized selector sent to instance 0x4c18950
If I comment out NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:lastSelection]; it doesn't crash anymore, so I believe it has something do do with that. Can anyone offer any insight? Please let me know if I need to include any other information. Thanks!
EDIT: Interestingly, my fetchedResultsController code returns a different object every time. Is this expected, or could this be a cause of my issue? The code looks like this:
- (NSFetchedResultsController *)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:@"Employee" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:80];
// Edit the sort key as appropriate.
NSString *sortKey;
BOOL ascending;
if(sortControl.selectedSegmentIndex == 0) {
sortKey = @"startTime";
ascending = YES;
} else if(sortControl.selectedSegmentIndex == 1) {
sortKey = @"name";
ascending = YES;
} else {
sortKey = @"onDuty";
ascending = NO;
}
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:ascending];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![fetchedResultsController_ performFetch:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
//NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return fetchedResultsController_;
}
This happens when I set a breakpoint:
(gdb) po [self fetchedResultsController]
<NSFetchedResultsController: 0x61567c0>
(gdb) po [self fetchedResultsController]
<NSFetchedResultsController: 0x4c83630>