4

I have a method to create managed objects (IntersectionObject) each with three properties. These three properties are managed objects themselves.

PropertyObject1, PropertyObject2, and PropertyObject3 each has about 20 different possibilities.

An IntersectionObject is essentially a combination of a particular PropertyObject1, PropertyObject2, and PropertyObject3.

There are about 1200 IntersectionObjects and to create them I am using a fetch request to retrieve and set the the correct PropertyObject:

- (PropertyObject1 *)fetchedPropertyObject1FromID: (NSNumber *)propertyObjectID {

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"PropertyObject1"  inManagedObjectContext:[self managedObjectContext]];
    [fetchRequest setEntity:entity];

    NSError *error = nil;
    NSArray *fetchedObjects = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
    if (fetchedObjects == nil) {
        NSLog(@"error fetching PropertyObject from ID, error:%@", error);
    return nil;
    }

    for (PropertyObject1 *object in fetchedObjects) {

        if (object.propertyObjectID.longValue == propertyObjectID.longValue) {
        return object;
        }
    }

    return nil;

}

I am finding that repeating this fetch three times for each of the 1200 IntersectionObjects takes about 2 seconds, and is too slow. Is there a faster way to do this?

EDIT: the accepted answer has the solution that I used in the comments below it. It turns out simply mapping the PropertyObjects to a dictionary was the simplest and quickest way to get the associated objects.

Michael Campsall
  • 4,325
  • 11
  • 37
  • 52
  • I would not recommend calling `longValue` on a `NSManagedObjectID`. Far better to treat it like the object it is an use `-isEqual:` – Marcus S. Zarra Dec 20 '13 at 02:19
  • the ID here was not the NSManagedObjectID but an arbitrary ID from the JSON data (I believe it is an NSNumber), but I am sure your advice still applies, thank you kindly for pointing it out. – Michael Campsall Dec 20 '13 at 03:33
  • In the code above it is the `-objectID` from the `NSManagedObject`. Hopefully you are not overriding the `-objectID` in a `NSManagedObject` subclass. – Marcus S. Zarra Dec 20 '13 at 03:46
  • Yes good point! the ID has a different name in my code, I was just trying to make it more generic by using objectID, not realizing that is already taken. I will change it in the above code to clarify. – Michael Campsall Dec 21 '13 at 00:41

2 Answers2

1

If you have the managed object id, use objectWithID: on the MOC.

If you don't, and you're going to be creating a lot of associations in a short space of time, fetch all of the managed objects from the MOC in batches (perhaps 100 items each batch, see fetchBatchSize on NSFetchRequest), create a dictionary mapping your objectID to the managed objects so you can just do a local lookup. Turn each object back into a fault as you process the batch with refreshObject:mergeChanges:.

Wain
  • 118,658
  • 15
  • 128
  • 151
  • I have not used managedObjectIDs before, but from What I have been reading about them I will need to saveContext before I can use their permanent ID. My question then is: Do I need to do a save context each time I am creating a new PropetyObject and assign the moID as a property of it? – Michael Campsall Dec 19 '13 at 19:50
  • I've edited the answer as what I originally suggested isn't easy to accomplish. Objects do get temporary ids but you would want to save if you were using them. – Wain Dec 19 '13 at 19:54
  • If you only make these associations infrequently then the answer from @RyanG is a good option. – Wain Dec 19 '13 at 19:54
  • This association will only be used once to set up the 1200 objects. Can I simply used the tempID's for that and then saveContext once they are done? – Michael Campsall Dec 19 '13 at 20:00
  • If you are creating all the objects at the same time, just create a mapping dictionary there and then. No need to do any fetches or managed object id lookups then... – Wain Dec 19 '13 at 20:02
  • Thanks so much! the load time is down to 0.26 seconds using the mapping dictionary method. This is my first time using Core Data and apparently I am too focused on using what's in there and forgetting the straightforward solutions. Thanks for your help =) – Michael Campsall Dec 19 '13 at 20:22
0

Use a predicate to do this, also set the fetch limit to 1. This should get your result in a much more optimized fashion:

- (PropertyObject1 *)fetchedPropertyObject1FromID: (NSNumber *)objectID {

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"PropertyObject1"  inManagedObjectContext:[self managedObjectContext]];
    [fetchRequest setEntity:entity];

    [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"objectID == %@", objectID]];
    [fetchRequest setFetchLimit:1];


    NSError *error = nil;
    NSArray *fetchedObjects = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
    if (fetchedObjects == nil) {
        NSLog(@"error fetching PropertyObject from ID, error:%@", error);
    return nil;
    }

    if(fetchedObjects.count > 0)
    {
        [return fetchedObjects objectAtIndex:0];
    }
    return nil;

}
RyanG
  • 4,393
  • 2
  • 39
  • 64
  • 1
    I just tried out this method to compare it to my original attempt: WITH predicate: 2.9 seconds, WITHOUT predicate (original method): 1.8 seconds. Not sure why though... – Michael Campsall Dec 19 '13 at 20:09
  • Adding the predicate causes a string parse (of the predicateWithFormat) plus it does a string compare on the objectID. That is what makes it more expensive. – Marcus S. Zarra Dec 20 '13 at 02:17