Problem with core data lazy loading

1.7k views Asked by At

Core Data loads objects lazily, and it is supposed to bring in any objects as soon as you try to reference them. However I have encountered a couple of problems with this scheme.

Objective C 2.0 fast enumeration over the objects in a relationship (set) can fail (because the objects in a set are not yet loaded), and awakeFromFetch is not called until a member of the object managed by core data is touched.

For example, if I have a relationship in a subclass of NSManagedObject such as:

@property (retain) NSSet* clips;

Immediately after fetching an instance of that object, if I try using fast enumeration such as this:

for (PClip* clip in self.clips) {  
    // do something with the clip  
}

the body of the loop is never executed. In the debugger you can see that the set of clips (an instance of _NSFaultingMutableSet at run time) is initially empty.

Or say I have a non-persistent member of the clip object that is derived from the persistent state:

@property (retain) NSString* filename

I synchronize this with the using awakeFromFetch method, but awakeFromFetch is not called until some persistent member of the object is first called, so if some code attempts to access this non-persistent value before the object is loaded, it will not yet be defined (i.e. will be nil).

Using the setReturnsObjectsAsFaults: method on the fetch request does not seem to help. It does not appear to force fetching all of the tree of objects attached to the fetched object.

What is the best way to ensure that objects are loaded before working with them? Why did fast enumeration fail to cause the set of objects to load?

4

There are 4 answers

1
Matthias Bauch On

what happens when you replace

for (PClip* clip in clips) {  
    // do something with the clip  
}

with

for (PClip* clip in self.clips) {  
    // do something with the clip  
}

? You can't use coredata values like instance variables, you have to use the setter and getter. Because the getter will get the value from core data.

And then try to create a getter for filename that creates the object when it is accessed for the first time.

From my experience it's usually not necessary to do anything in awakeFromFetch.

0
CuriousKea On

What seems to have worked (although it seems a bit of a hack) is to force the full tree of objects to be traversed from the awakeFromFetch method of the top level object.

For example:

-(void) awakeFromFetch {
    // accessing the count of each relationship forces the set of objects to load  
    self.clips.count;  
    for (PClip* clip in self.clips) {  
        // access a persistent member of each object, which will cause
        // its awakeFromFetch method to be called  
        clip.pathURL;    
    }
}
0
Fnord23 On

I think properties of managed objects are loaded by default.
To load relationships, you might do something like this:
NSArray *relationshipKeys = [NSArray arrayWithObject:@"clips"];
[fetchRequest setRelationshipKeyPathsForPrefetching:relationshipKeys];

0
TonnyTao On

@CuriousKea Just tips, when using FRC, you should not set relationship in awakeFromFetch. because setting relationship will cause FRC to be notified again, and FRC delegate will be called twice. It hurts performance.