I have a Core Data model representing a TV guide on iOS 4+, with 3 classes:
Channel (BBC 1)Program (Top Gear)Broadcast (Top Gear on BBC 1 on Monday at 8pm).
I have about 40 channels, 8000 programs and 6000 broadcasts, and I would like to configure the import process so that it does not take minutes to launch.
Importing channels and programs is easy because they are independent entities. However, the broadcast is related to the channel and the program (from 1 to many), and both channels and programs have an inverse relationship with broadcasts (multi-to-1). To speed things up, I have a dictionary in the memory of channels and programs that have only their web service identifier: I create a broadcast and look through both dictionaries to get the corresponding channel and program without going back to the database.
But when I designate a program or channel for broadcasting, access to the inverse relations of the channels and programs immediately causes an error of both objects, which causes a significant slowdown (requests 6000 * 2) and subsequent memory pressure, as shown in the Failure report of the main data. I tried to prefetch the broadcasts relationship on both channels and programs, but the connection still causes failures.
Do you have any idea why the reverse relationship is accessed and their parents are to blame? How to avoid reading from the database while maintaining relationships?
UPDATE: sample code, my assignment / update method for a Broadcast instance. The dictionary variable comes from the web service, and channels and programs contain the error and program channel numbers indexed by the web service identifier. The malfunction occurs on the lines self.program = program and self.channel = channel .
- (BOOL)assignWithDictionary:(NSDictionary *)dictionary channels:(NSDictionary *)channels programs:(NSDictionary *)programs { // Add channel relationship NSNumber *channelIdentifier = [dictionary objectForKey:@"channel_id"]; if (self.channel == nil || ![self.channel.identifier isEqualToNumber:channelIdentifier]) { Channel *channel = [channels objectForKey:channelIdentifier]; if (channel == nil) { NSLog(@"Broadcast %@ has invalid channel: %@", identifier, channelIdentifier); return NO; } self.channel = channel; } // Same to add a program relationship // ... }
And my select query to get channels or a list of programs:
- (NSDictionary *)itemsForEntity:(NSEntityDescription *)entity { NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease]; NSError *error = nil; NSArray *itemsArray = nil; request.entity = entity; request.relationshipKeyPathsForPrefetching = [NSArray arrayWithObject:@"broadcasts", nil]; request.propertiesToFetch = [NSArray arrayWithObjects:@"identifier", @"version", nil]; itemsArray = [self.context executeFetchRequest:request error:&error]; NSAssert1(error == nil, @"Could not fetch the items from the database: %@", error); { NSMutableDictionary *items = [NSMutableDictionary dictionaryWithCapacity:itemsArray.count]; for (NSManagedObject *item in itemsArray) { [items setObject:item forKey:[item valueForKey:@"identifier"]]; } return [NSDictionary dictionaryWithDictionary:items]; } }