How to populate a core data store programmatically?

Posted by jdmuys on Stack Overflow See other posts from Stack Overflow or by jdmuys
Published on 2010-03-14T23:46:18Z Indexed on 2010/03/15 0:19 UTC
Read the original article Hit count: 839

Filed under:
|
|

I have ran out of hairs to pull with a crash in this routine that populates a core data store from a 9000+ line plist file.

The crash happened at the very end of the routine inside the call to [managedObjectContext save:&error]. While if I save after every object insertion, the crash doesn't happen. Of course, saving after every object insertion totally kills the performance (from less than a second to many minutes).

I modified my code so that it saves every K insertions, and the crash happens as soon as K >= 2.

The crash is an out-of-bound exception for an NSArray:

Serious application error.  Exception was caught during Core Data change processing: *** -[NSCFArray objectAtIndex:]: index (1) beyond bounds (1) with userInfo (null)

Also maybe relevant, when the exception happen, my fetch result controller controllerDidChangeContent: delegate routine is in the call stack. It simply calls my table view endUpdate routine.

I am now running out of ideas. How am I supposed to populate a core data store with a table view?

Here is the call stack:

#0  0x901ca4e6 in objc_exception_throw
#1  0x01d86c3b in +[NSException raise:format:arguments:]
#2  0x01d86b9a in +[NSException raise:format:]
#3  0x00072cb9 in _NSArrayRaiseBoundException
#4  0x00010217 in -[NSCFArray objectAtIndex:]
#5  0x002eaaa7 in -[UITableView(_UITableViewPrivate) _endCellAnimationsWithContext:]
#6  0x002def02 in -[UITableView endUpdates]
#7  0x00004863 in -[AirportViewController controllerDidChangeContent:] at AirportViewController.m:463
#8  0x01c43be1 in -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:]
#9  0x0001462a in _nsnote_callback
#10 0x01d31005 in _CFXNotificationPostNotification
#11 0x00011ee0 in -[NSNotificationCenter postNotificationName:object:userInfo:]
#12 0x01ba417d in -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:]
#13 0x01c03763 in -[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:]
#14 0x01b885ea in -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:]
#15 0x01bbe728 in -[NSManagedObjectContext save:]
#16 0x000039ea in -[AirportViewController populateAirports] at AirportViewController.m:112

Here is the code to the routine. I apologize because a number of lines are probably irrelevant, but I'd rather err on that side. The crash happens the very first time it calls [managedObjectContext save:&error]:

- (void) populateAirports
{
NSBundle *meBundle = [NSBundle mainBundle];
NSString *dbPath = [meBundle pathForResource:@"DuckAirportsBin" ofType:@"plist"];
NSArray *initialAirports = [[NSArray alloc] initWithContentsOfFile:dbPath];

//*********************************************************************************
// get existing countries
NSMutableDictionary *countries = [[NSMutableDictionary alloc] initWithCapacity:200];

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Country" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *values = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (!values) {
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}
int numCountries = [values count];
NSLog(@"We have %d countries in store", numCountries);

for (Country *aCountry in values) {
    [countries setObject:aCountry forKey:aCountry.code];
}

[fetchRequest release];

//*********************************************************************************
// read airports
int numAirports = 0;
int numUnsavedAirports = 0;
#define MAX_UNSAVED_AIRPORTS_BEFORE_SAVE 2
numCountries = 0;
for (NSDictionary *anAirport in initialAirports) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSString *countryCode = [anAirport objectForKey:@"country"];
    Country *thatCountry = [countries objectForKey:countryCode];
    if (!thatCountry) {
        thatCountry = [NSEntityDescription insertNewObjectForEntityForName:@"Country" inManagedObjectContext:managedObjectContext];
        thatCountry.code = countryCode;
        thatCountry.name = [anAirport objectForKey:@"country_name"];
        thatCountry.population = 0;
        [countries setObject:thatCountry forKey:countryCode];
        numCountries++;
        NSLog(@"Found %dth country %@=%@", numCountries, countryCode, thatCountry.name);
    }

    // now that we have the country, we create the airport
    Airport *newAirport = [NSEntityDescription insertNewObjectForEntityForName:@"Airport" inManagedObjectContext:managedObjectContext];

    newAirport.city         = [anAirport objectForKey:@"city"];
    newAirport.code         = [anAirport objectForKey:@"code"];
    newAirport.name         = [anAirport objectForKey:@"name"];
    newAirport.country_name = [anAirport objectForKey:@"country_name"];
    newAirport.latitude     = [NSNumber numberWithDouble:[[anAirport objectForKey:@"latitude"] doubleValue]];
    newAirport.longitude    = [NSNumber numberWithDouble:[[anAirport objectForKey:@"longitude"] doubleValue]];
    newAirport.altitude     = [NSNumber numberWithDouble:[[anAirport objectForKey:@"altitude"] doubleValue]];

    newAirport.country = thatCountry;
//      [thatCountry addAirportsObject:newAirport];
    numAirports++;  numUnsavedAirports++;
    if (numUnsavedAirports >= MAX_UNSAVED_AIRPORTS_BEFORE_SAVE) {
        if (![managedObjectContext save:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
        numUnsavedAirports = 0;
    }
    [pool release];
}

© Stack Overflow or respective owner

Related posts about core-data

Related posts about crash