I'm trying to import a large data set (~6,000) in to my core data application. I've read the Apple document "Efficiently Importing Data" and I think I set it up correctly. The weird thing is the application isn't crashing in the simulator, although it does if I run it with the Leaks instrument, but it isn't saving all the data. Sometimes it wi开发者_Python百科ll only save 3-4 hundred other times it will save 3-4 thousand and rarely the whole data set. I think it's probably memory leak related and I'm pretty new to using NSAutoReleasePool, any help would be much appreciated.
NSURL *url = [NSURL URLWithString:@""];
NSString *responseString = [NSString stringWithContentsOfURL:url encoding:NSASCIIStringEncoding error:nil];
if (responseString) {
NSArray *players = [responseString componentsSeparatedByString:@";"];
NSUInteger LOOP_LIMIT = 100, count = 0;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSManagedObjectContext *context = [[AppController sharedAppController] managedObjectContext];
[context setUndoManager:nil];
for (int i=0; i<([players count] - 1); i++) {
NSArray *info = [[players objectAtIndex:i] componentsSeparatedByString:@","];
NSString *dateInfo = [info objectAtIndex:10];
NSLocale *usLocale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease];
NSDateFormatter *fo = [[[NSDateFormatter alloc] init] autorelease];
[fo setDateFormat:@"MM/dd/yyyy"];
[fo setLocale:usLocale];
[fo setTimeZone:[NSTimeZone systemTimeZone]];
NSDate *dob = [fo dateFromString:dateInfo];
Players *player = [NSEntityDescription insertNewObjectForEntityForName:@"Players"
inManagedObjectContext:context];
NSNumberFormatter *f = [[[NSNumberFormatter alloc] init] autorelease];
[f setNumberStyle:NSNumberFormatterNoStyle];
player.playerID = [f numberFromString:[info objectAtIndex:0]];
player.lastName = [info objectAtIndex:1];
player.firstName = [info objectAtIndex:2];
player.position = [info objectAtIndex:4];
NSString *teamName = [info objectAtIndex:3];
NSFetchRequest *req = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *ent = [NSEntityDescription entityForName:@"Teams" inManagedObjectContext:context];
[req setEntity:ent];
[req setIncludesPropertyValues:NO];
NSPredicate *pre = [NSPredicate predicateWithFormat:@"team=%@", teamName];
[req setPredicate:pre];
NSArray *list = [context executeFetchRequest:req error:nil];
if ([list count]) {
Teams *team = [list objectAtIndex:0];
player.team_Players_Teams = team;
}
count++;
if (count == LOOP_LIMIT) {
[context save:nil];
[context reset];
[pool drain];
pool = [[NSAutoreleasePool alloc] init];
count = 0;
}
}
if (count != 0) {
NSLog(@"In Save Remaining");
[context save:nil];
[context reset];[pool drain];
}
I can't see anything dodgy in the code either. Definitely no errors appearing in the log?
Btw one other optimisation tip covered in the Core Data pdf for importing data is to move the predicate creation outside the loop and use substitution variables.
I can't see any obvious leaks but:
You could minimise the amount of memory used by
alloc init retain
-ing theNSLocale
,NSDateFormatter
,NSNumberFormatter
and thenrelease
-ing them after the loop is complete. These don't seem to change between runs of the loop.I don't know Core Data but where does the
NSManagedObject
/Player *player
object get released? Is this autoreleased through Core Data?As an aside, you can use
[list lastObject]
rather than[list count]
and[list objectAtIndex:0]
as the last two will crash if list is nil
Update based on response:
If nothing appears to make a difference then the next step is to simplify the code so as to remove any error sources.
Carry out my suggestions in #1 above to minimise the amount of code in the loop.
Check you're releasing the
list
object somewhere or that it is allocated as autorelease.Remove the intermediate saving (all that code inside
count == LOOP_LIMIT
) and only save and drain the pool once all the arrays have been processed. You also should not need the code following on inside theif (count != 0)
Replace the
error:nil
statements with propererror:&&error
and log errors. To log the errors do the following (apologies but the code formatting doesn't seem to work - no idea why):NSError *error = nil; //Declared upfront
// Your streamlined code
// ....
error = nil; //Just before a fetchRequest or context:save
/* After looping through all your code now attempt to save */
if(![context save:&error]) {
NSLog(@"Failed to save to data store: %@", [error localizedDescription]); NSArray *detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey]; if(detailedErrors != nil && [detailedErrors count] > 0) { for(NSError *detailedError in detailedErrors) { NSLog(@"DetailedError: %@", [detailedError userInfo]); } } else { NSLog(@" %@", [error userInfo]); }
}
Then check the log to see if you're getting any odd messages. You can also use similar code anywhere in Core Data that you need to check for errors (i.e.
executeFetchRequest
). It's worth a shot to find out what the error is here.
精彩评论