Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory efficient way of inserting an array of objects with Core Data

I'm working on a piece of code for an iPhone application that fetches a bunch of data from a server and builds objects from it on the client. It ends up creating roughly 40,000 objects. They aren't displayed to the user, I just need to create instances of NSManagedObject and store them to persistent storage.

Am I wrong in thinking that the only way to do this is to create a single object, then save the context? is it best to create the objects all at once, then somehow save them to the context after they're created and stored in some set or array? If so, can one show some example code for how this is done or point me in the direction to code where this is done?

The objects themselves are relatively straight forward models with string or integer attributes and don't contain any complex relationships.

like image 784
randombits Avatar asked Apr 09 '10 16:04

randombits


1 Answers

In any case, don't save after inserting every object, or be prepared for dreadful performances.

Here is the code I use to populate a Core Data repository upon first launch.

#define MAX_UNSAVED_AIRPORTS_BEFORE_SAVE 1000
int numAirports = 0;
int numUnsavedAirports = MAX_UNSAVED_AIRPORTS_BEFORE_SAVE; // *** bug. see below
for (NSDictionary *anAirport in initialAirports) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    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]];

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

Also don't forget to save one last time after the loop.

Also be aware that a bug exists that will lead to a crash if all three of the following conditions are met:

  1. The Repository is empty
  2. You have a UITableView with sections
  3. Your first save saves more than one object.

The workaround in the code above is to initialize the numUnsavedAirports to MAX_UNSAVED_AIRPORTS_BEFORE_SAVE in order to make sure the first save happens after the first insert.

I hope this helps.

like image 130
Jean-Denis Muys Avatar answered Oct 11 '22 12:10

Jean-Denis Muys