CoreData Tutorial throws an error, inserting nil

core-data, ios, objective-c

Solution

Take a look at the message that comes with the exception:

`-[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]'`

`__NSPlaceholderArray` isn't your class, so lets look down the stack trace a bit...

2   CoreFoundation                      0x01b0f206 -[__NSPlaceholderArray initWithObjects:count:] + 390
3   CoreFoundation                      0x01b32b89 +[NSArray arrayWithObject:] + 73
4   CoreData                            0x000035ca -[PatientsTableViewController controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:] + 394

Now we're getting somewhere... you do have the code for `-[PatientsTableViewController controller:didchangeObject:...]`, and that method calls `+[NSArray arrayWithObject:]` in four different places. At least one of those lines is the direct cause of the exception. Looking at the documentation for that method, you'll see that both `indexPath` and `newIndexPath` can be `nil` in some situations. Specifically, the line describing `newIndexPath` says:

The destination path for the object for insertions or moves (this value is `nil` for a deletion).

Looking at the code again, you've got:

    case NSFetchedResultsChangeDelete:
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

So, the problem is that you're passing `newIndexPath`, which is documented to be `nil` for deletion, to `+[NSArray arrayWithObject:]`. You probably meant to pass `indexPath` instead.

Similarly, you probably don't mean to use `indexPath` for both deleting and inserting in the `NSFetchedResultsChangeMove` case. It's probably `indexPath` for the deletion, `newIndexPath` for the insertion. Your code, minus the errors, is nearly identical to the example in the Typical Use section of the documentation for `NSFetchedResultsControllerDelegate`, so take a look at that for additional help.

Problem

I was following the tutorial "CoreData Part 1 and 2" by Dennis Roberson on youtube. GREAT GREAT Tutorial btw! Thanx @ Dennis. anyhow, I must have messed up somewhere along the way and I just can't find where the bug is. In my "PatientView"-Screen when I click on + it goes to the addPatient-Screen, if I don't enter any data and hit cancel it tries to perform a "rollback" action inside my "cancelAndDismiss" Method (found in CoreViewController.m) I set a breakpoint at that line, and when I execute past that line it crashes. Of course I can single step a few other commands, as it collects the data for that step, but it will never get to the next line. here is that method at full ``` -(void)cancelAndDismiss { [self.managedObjectContext rollback]; [self dismissViewControllerAnimated:YES completion:nil]; } ``` This is the output of my error-log: ``` 2014-05-26 18:58:01.118 CoreData[2287:60b] Objects returned: 0 2014-05-26 18:58:02.851 CoreData[2287:60b] _managedObjectContext: <NSManagedObjectContext: 0x8f786e0> 2014-05-26 18:58:02.868 CoreData[2287:60b] Objects returned: 1 2014-05-26 18:58:02.869 CoreData[2287:60b] In here NOW 2014-05-26 18:58:02.871 CoreData[2287:60b] tableView Cell - Lastname: (null) 2014-05-26 18:58:02.871 CoreData[2287:60b] tableView Cell - Firstname: (null) 2014-05-26 18:58:14.840 CoreData[2287:60b] _managedObjectContext: <NSManagedObjectContext: 0x8f786e0> 2014-05-26 19:01:05.959 CoreData[2287:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]' *** First throw call stack: ( 0 CoreFoundation 0x01b5b1e4 __exceptionPreprocess + 180 1 libobjc.A.dylib 0x018da8e5 objc_exception_throw + 44 2 CoreFoundation 0x01b0f206 -[__NSPlaceholderArray initWithObjects:count:] + 390 3 CoreFoundation 0x01b32b89 +[NSArray arrayWithObject:] + 73 4 CoreData 0x000035ca -[PatientsTableViewController controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:] + 394 5 CoreData 0x0036790b -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 2795 6 Foundation 0x015ad049 __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke + 40 7 CoreFoundation 0x01bb6f04 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 20 8 CoreFoundation 0x01b0eefb _CFXNotificationPost + 2859 9 Foundation 0x014e6e41 -[NSNotificationCenter postNotificationName:object:userInfo:] + 98 10 CoreData 0x0026aa13 -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 83 11 CoreData 0x00306173 -[NSManagedObjectContext rollback] + 1939 12 CoreData 0x00005804 -[CoreViewController cancelAndDismiss] + 84 13 CoreData 0x0000512b -[AddPatientViewController cancel:] + 107 14 libobjc.A.dylib 0x018ec880 -[NSObject performSelector:withObject:withObject:] + 77 15 UIKit 0x0059c3b9 -[UIApplication sendAction:to:from:forEvent:] + 108 16 UIKit 0x008898df -[UIBarButtonItem(UIInternal) _sendAction:withEvent:] + 139 17 libobjc.A.dylib 0x018ec880 -[NSObject performSelector:withObject:withObject:] + 77 18 UIKit 0x0059c3b9 -[UIApplication sendAction:to:from:forEvent:] + 108 19 UIKit 0x0059c345 -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 61 20 UIKit 0x0069dbd1 -[UIControl sendAction:to:forEvent:] + 66 21 UIKit 0x0069dfc6 -[UIControl _sendActionsForEvents:withEvent:] + 577 22 UIKit 0x0069d243 -[UIControl touchesEnded:withEvent:] + 641 23 UIKit 0x005dbddd -[UIWindow _sendTouchesForEvent:] + 852 24 UIKit 0x005dc9d1 -[UIWindow sendEvent:] + 1117 25 UIKit 0x005ae5f2 -[UIApplication sendEvent:] + 242 26 UIKit 0x00598353 _UIApplicationHandleEventQueue + 11455 27 CoreFoundation 0x01ae477f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15 28 CoreFoundation 0x01ae410b __CFRunLoopDoSources0 + 235 29 CoreFoundation 0x01b011ae __CFRunLoopRun + 910 30 CoreFoundation 0x01b009d3 CFRunLoopRunSpecific + 467 31 CoreFoundation 0x01b007eb CFRunLoopRunInMode + 123 32 GraphicsServices 0x03a0d5ee GSEventRunModal + 192 33 GraphicsServices 0x03a0d42b GSEventRun + 104 34 UIKit 0x0059af9b UIApplicationMain + 1225 35 CoreData 0x00004ebd main + 141 36 libdyld.dylib 0x021a2701 start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException (lldb) ``` To complete the listing here are the files involved: CoreViewController.m ``` #import "CoreViewController.h" #import "AppDelegate.h" @interface CoreViewController () @property (nonatomic, strong)NSManagedObjectContext *managedObjectContext; @end @implementation CoreViewController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization } return self; } - (NSManagedObjectContext*)managedObjectContext { return [(AppDelegate*) [[UIApplication sharedApplication]delegate]managedObjectContext]; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } /* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */ -(void)cancelAndDismiss { [self.managedObjectContext rollback]; [self dismissViewControllerAnimated:YES completion:nil]; } -(void)saveAndDismiss { NSError *error = nil; if ([self.managedObjectContext hasChanges]) { if (![self.managedObjectContext save:&error]) { // save failed NSLog(@"Save failed: %@", [error localizedDescription]); } else { // save succeeded NSLog(@"Save Succeeded"); } } [self dismissViewControllerAnimated:YES completion:nil]; } @end ``` PatientsTableViewController.m ``` #import "PatientsTableViewController.h" #import "AppDelegate.h" #import "AddPatientViewController.h" @interface PatientsTableViewController () @property (nonatomic, strong) NSManagedObjectContext *managedObjectContext; @property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController; @end @implementation PatientsTableViewController - (id)initWithStyle:(UITableViewStyle)style { self = [super initWithStyle:style]; if (self) { // Custom initialization } return self; } - (NSManagedObjectContext*)managedObjectContext { return [(AppDelegate*)[[UIApplication sharedApplication]delegate]managedObjectContext]; } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([[segue identifier]isEqualToString:@"addPatient"]) { UINavigationController *navigationController = segue.destinationViewController; AddPatientViewController *addPatientViewController = (AddPatientViewController*) navigationController.topViewController; Patient *addPatient = [NSEntityDescription insertNewObjectForEntityForName:@"Patient" inManagedObjectContext:[self managedObjectContext]]; addPatientViewController.addPatient = addPatient; } } - (void)viewDidLoad { [super viewDidLoad]; NSError *error = nil; if (![[self fetchedResultsController]performFetch:&error]) { NSLog(@"Error! %@", error); abort(); } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [[self.fetchedResultsController sections]count]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { id<NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections]objectAtIndex:section]; NSLog(@"Objects returned: %d", [sectionInfo numberOfObjects]); return [sectionInfo numberOfObjects]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSLog(@"In here NOW"); static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; // Configure the cell... Patient *patient = [self.fetchedResultsController objectAtIndexPath:indexPath]; cell.textLabel.text = patient.patientLastName; cell.detailTextLabel.text = patient.patientFirstName; NSLog(@"tableView Cell - Lastname: %@", patient.patientLastName); NSLog(@"tableView Cell - Firstname: %@", patient.patientFirstName); return cell; } /* // Override to support conditional editing of the table view. - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { // Return NO if you do not want the specified item to be editable. return YES; } */ // Override to support editing the table view. - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { // Delete the row from the data source NSManagedObjectContext *context = [self managedObjectContext]; Patient *patientToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath]; [context deleteObject:patientToDelete]; NSError *error = nil; if (![context save:&error]) { NSLog(@"Error! %@", error); } } } /* // Override to support rearranging the table view. - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { } */ /* // Override to support conditional rearranging of the table view. - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { // Return NO if you do not want the item to be re-orderable. return YES; } */ /* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */ #pragma mark - Fetched Results Controller Section - (NSFetchedResultsController*)fetchedResultsController { if (_fetchedResultsController != nil) { return _fetchedResultsController; } NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init]; NSManagedObjectContext *context = [self managedObjectContext]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Patient" inManagedObjectContext:context]; [fetchRequest setEntity:entity]; NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:@"patientLastName" ascending:YES]; NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor, nil]; fetchRequest.sortDescriptors = sortDescriptors; _fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil]; _fetchedResultsController.delegate = self; return _fetchedResultsController; } #pragma mark - Fetched Results Controller Delegates - (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 { UITableView *tableView = self.tableView; // creating a temporary placeholder switch (type) { case NSFetchedResultsChangeInsert: [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeUpdate: { Patient *changePatient = [self.fetchedResultsController objectAtIndexPath:indexPath]; UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; cell.textLabel.text = changePatient.patientLastName; } break; case NSFetchedResultsChangeMove: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; } } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { switch (type) { case NSFetchedResultsChangeInsert: [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; } } @end ``` @EDIT: I narrowed it down further. It comes to this piece of code: ``` - (NSManagedObjectContext *)managedObjectContext { /* if (_managedObjectContext != nil) { NSLog(@"_managedObjectContext: %@", _managedObjectContext); return _managedObjectContext; } */ NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; if (coordinator != nil) { _managedObjectContext = [[NSManagedObjectContext alloc] init]; [_managedObjectContext setPersistentStoreCoordinator:coordinator]; } return _managedObjectContext; } ``` Which is in my AppDelegate.m file. As you can see I commented-out the top piece of it, without that code snippet it works fantastic, with it, it crashes. here is the top portion of AppDelegate.m file, not the whole code this time ;-) ``` #import "AppDelegate.h" @implementation AppDelegate @synthesize managedObjectContext = _managedObjectContext; @synthesize managedObjectModel = _managedObjectModel; @synthesize persistentStoreCoordinator = _persistentStoreCoordinator; ``` I hope someone can help me and point me in the right direction, as I'm tapping in the dark :-/ Thank you folks, and sorry for too much code

Original source