Performance: Master data relationships become erroneous after assignment

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]; } } 
+6
source share
1 answer

Not quite sure what you are trying to do here, but ...

First, you cannot change properties using only errors. Errors are simply placeholders that allow you to measure / count the graph of objects and build relationships. If you actually change the relationship, it will crash, causing the loading of related objects.

If you try to establish relationships between specific Channel , Program and Broadcast objects using only errors, this will not work.

Your itemsForEntity: method itemsForEntity: I do not understand. It will retrieve every existing managed object of the transferred object and then return these objects in the dictionary. This will cause huge memory overhead, especially in the case of Program objects, of which 8000.

You cannot use propertiesToFetch unless you set the extraction return to a dictionary that you do not have. You cannot use the type of the returned dictionary in any case if you need to establish relationships. You use both of these parameters when all you need is data stored in certain attributes. This is not a tool for manipulating the relationships of an object graph.

Setting up relationshipKeyPathsForPrefetching only speeds things up if you know you will be accessing existing relationships. This does not help when you establish relationships first, for example, if there are no existing objects in the broadcasts relationship or you add or remove Broadcast objects, prefetching the broadcasts key line does nothing for you.

I'm not sure I understand your data model well enough, but I think you are doing it wrong. It seems to me that you are trying to use identifier as the primary key in the SQL database, and this is counterproductive. In Core Data, relationships are associated with objects together, not with a common attribute and value.

Typically, if you have two or more objects with the same attribute name with the same value, in most cases you have a poorly designed data model.

+1
source

Source: https://habr.com/ru/post/894916/


All Articles