I have a significant amount of data that I have to load on first run of an application. I have it all in an xml file (2.5mb). I'm running through it using NSXMLParser. There are about 5 entities I'm interested in.
Here's a sample line of the xml.
Code:
<period weekStartDate="2002-04-26">
<mortgageType type="FIXED" duration="30">
<region code="US">
<rate>6.88</rate>
<points>0.7</points>
</region>
<region code="NE">
<rate>6.90</rate>
<points>0.7</points>
</region>
<region code="SE">
<rate>6.85</rate>
<points>0.7</points>
</region>
<region code="NC">
<rate>6.95</rate>
<points>0.5</points>
</region>
<region code="SW">
<rate>6.89</rate>
<points>0.7</points>
</region>
<region code="W">
<rate>6.83</rate>
<points>0.9</points>
</region>
</mortgageType>
<mortgageType type="FIXED" duration="15">
<region code="US">
<rate>6.35</rate>
<points>0.7</points>
</region>
<region code="NE">
<rate>6.36</rate>
<points>0.6</points>
</region>
<region code="SE">
<rate>6.32</rate>
<points>0.8</points>
</region><region code="NC">
<rate>6.41</rate>
<points>0.4</points>
</region>
<region code="SW">
<rate>6.40</rate>
<points>0.7</points>
</region>
<region code="W">
<rate>6.31</rate>
<points>0.9</points>
</region>
</mortgageType>
<mortgageType type="ARM" duration="5">
<region code="US">
<rate> </rate>
<points> </points>
</region>
<region code="NE">
<rate> </rate>
<points> </points>
</region>
<region code="SE">
<rate> </rate>
<points> </points>
</region>
<region code="NC">
<rate> </rate>
<points> </points>
</region>
<region code="SW">
<rate> </rate>
<points> </points>
</region>
<region code="W">
<rate> </rate>
<points> </points>
</region>
</mortgageType>
<mortgageType type="ARM" duration="1">
<region code="US">
<rate>4.91</rate>
<points>0.7</points>
</region>
<region code="NE">
<rate>5.05</rate>
<points>0.7</points>
</region>
<region code="SE">
<rate>4.90</rate>
<points>0.9</points>
</region>
<region code="NC">
<rate>5.04</rate>
<points>0.6</points>
</region>
<region code="SW">
<rate>4.88</rate>
<points>0.7</points>
</region>
<region code="W">
<rate>4.72</rate>
<points>0.9</points>
</region>
</mortgageType>
</period>
I have about 1,500 lines like that right now. When I run this in the simulator it'll go through somewhere around 100 of them and then the app crashes. There is no error message and no memory warning thrown.
On the device it doesn't crash, but it's incredibly slow. About 2 seconds per line.
Here are the relevant delegate methods.
Code:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {
// Figure out the element type and create the necessary object.
if ([elementName isEqualToString:@"period"]) {
currentWeekStart = [dateFormatter dateFromString:[attributeDict objectForKey:@"weekStartDate"]];
NSLog(@"parsing period %@", [dateFormatter stringFromDate:currentWeekStart]);
}
if ([elementName isEqualToString:@"mortgageType"]) {
NSString *mType = [[attributeDict objectForKey:@"type"] autorelease];
NSString *mDuration = [[attributeDict objectForKey:@"duration"] autorelease];
// Make sure this is already in the db, otherwise it needs to be added
if ([mortgageTypes objectForKey:mType] == nil
|| [[mortgageTypes objectForKey:mType] objectForKey:mDuration] == nil) {
MortgageType *newMortgageType = [(MortgageType*)[NSEntityDescription
insertNewObjectForEntityForName:@"MortgageType"
inManagedObjectContext:[DataManager sharedManager].managedObjectContext]
autorelease];
newMortgageType.MortgageDuration = mDuration;
newMortgageType.MortgageTypeCode = mType;
[DataManager saveContext];
// Refresh my list
mortgageTypes = [MortgageType mortgageTypeDictionary];
}
// Start watching this one
currentMortgageType = [[mortgageTypes objectForKey:mType] objectForKey:mDuration];
}
if ([elementName isEqualToString:@"region"]) {
// Check to make sure the region exists, add if not
NSString *regionCode = [attributeDict objectForKey:@"code"];
if ([regions objectForKey:regionCode] == nil) {
Region *newRegion = [(Region*)[NSEntityDescription
insertNewObjectForEntityForName:@"Region"
inManagedObjectContext:[DataManager sharedManager].managedObjectContext]
autorelease];
newRegion.RegionCode = regionCode;
newRegion.Order = 0;
[DataManager saveContext];
// Refresh my list
regions = [Region regionDictionary];
}
// Make current
currentRegion = [regions objectForKey:regionCode];
// Should have all the necessary info here to see if updating or adding.
NSMutableDictionary *vars = [[[NSMutableDictionary alloc] init] autorelease];
[vars setObject:currentWeekStart forKey:@"WeekBeginDay"];
[vars setObject:currentMortgageType.MortgageTypeCode forKey:@"MortgageTypeCode"];
[vars setObject:currentMortgageType.MortgageDuration forKey:@"MortgageDuration"];
[vars setObject:currentRegion.RegionCode forKey:@"RegionCode"];
NSFetchRequest *fetchRequest = [[[DataManager sharedManager].managedObjectModel
fetchRequestFromTemplateWithName:@"getIndividualSurveyResult"
substitutionVariables: vars] autorelease];
// Should only be one. Grab and update if that's the case.
[fetchRequest setFetchLimit:1];
NSError *error;
NSArray *items = [[DataManager sharedManager].managedObjectContext
executeFetchRequest:fetchRequest error:&error];
// If for some reason this one already exists, update it.
if ([items count]) {
currentSurveyResult = [[items objectAtIndex:0] retain];
}
else {
currentSurveyResult = [(SurveyResult*)[NSEntityDescription
insertNewObjectForEntityForName:@"SurveyResult"
inManagedObjectContext:[DataManager sharedManager].managedObjectContext] retain];
currentSurveyResult.WeekBeginDay = currentWeekStart;
}
}
if ([elementName isEqualToString:@"rate"]) {
}
if ([elementName isEqualToString:@"points"]) {
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
// Add to current string
if (!currentString) {
// currentString is an NSMutableString instance variable
currentString = [[NSMutableString alloc] init];
}
[currentString appendString:string];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
// Close object and save if necessary.
// Do releases in here for mortgage type etc.
if ([elementName isEqualToString:@"period"]) {
[currentWeekStart release];
currentWeekStart = nil;
}
if ([elementName isEqualToString:@"mortgageType"]) {
[currentMortgageType release];
currentMortgageType = nil;
}
if ([elementName isEqualToString:@"region"]) {
// If the survey result and the mortgage type aren't connected, they need to be.
if (!currentSurveyResult.MortgageType) {
currentSurveyResult.MortgageType = currentMortgageType;
// Add new SurveyResult to the related MortgageType
[(MortgageType*)[[mortgageTypes objectForKey:currentMortgageType.MortgageTypeCode]
objectForKey:currentMortgageType.MortgageDuration] addSurveyResultObject:currentSurveyResult];
}
// If the survey result and the region aren't connected, they need to be.
if (!currentSurveyResult.Region) {
currentSurveyResult.Region = currentRegion;
// Add new SurveyResult to related Region
[(Region*) [regions objectForKey:currentRegion.RegionCode] addSurveyResultObject:currentSurveyResult];
}
// Save current survey result and release
//
// Save the db
[DataManager saveContext];
// Release the finished stuff
[currentRegion release];
currentRegion = nil;
[currentSurveyResult release];
currentSurveyResult = nil;
}
if ([elementName isEqualToString:@"rate"]) {
if (currentSurveyResult) {
if ([currentString length])
currentSurveyResult.InterestRate = [NSDecimalNumber decimalNumberWithString:currentString];
}
}
if ([elementName isEqualToString:@"points"]) {
if (currentSurveyResult) {
if ([currentString length])
currentSurveyResult.Points = [NSDecimalNumber decimalNumberWithString:currentString];
}
}
[currentString release];
currentString = nil;
}
I need to figure out how to get this data in there either before or on the first run. Doesn't matter how it happens, but it needs to happen.
Could the problem be that I'm doing the core data save context for every result object I build?
If I create a managed object and fill it with pertinent data, then release it and create a new one before I save the database context, will the released managed object still be inserted when I save the db even though I don't released it through the variable?