Reputation: 16976
I'm having a problem with KVO exceptions being thrown if I attempt to prefetch related entities in Core Data. It doesn't make any sense to me and I can't seem to duplicate the behavior in a simplified project. I'm targeting 10.8 and using ARC.
My data models a music library and I have three entities of interest: Track
, Artist
, and Album
. A track has a single artist and a single album, while an album has a single artist. My Album
and Artist
objects are uniqued by name (only one artist with a given name in the store).
I want to display a list of tracks with the following fields: title, artist.name, album.title. This is easily accomplished using an NSArrayController for the Track
entity and bindings in an NSTableView.
However, since I know that I will be using the artist and album relationships I would like to prefetch them when the tracks are loaded. I created an NSArrayController subclass with a custom performFetch: method that looks like this:
- (BOOL) fetchWithRequest:(NSFetchRequest *)fetchRequest merge:(BOOL)merge error:(NSError *__autoreleasing *)error
{
NSEntityDescription *entityDescription = [Track entityInManagedObjectContext:[self managedObjectContext]];
fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entityDescription];
[fetchRequest setRelationshipKeyPathsForPrefetching:@[@"album", @"artist"]];
return [super fetchWithRequest:fetchRequest merge:merge error:error];
}
I set the NSArrayController's class in IB to my subclass and I expected that the UI lag would disappear (faults were firing with each scroll). However, an exception is thrown:
2013-01-01 10:48:06.965 XXX[10593:303] Cannot update for observer for the key path "artist.name" from , most likely because the value for the key "artist" has changed without an appropriate KVO notification being sent. Check the KVO-compliance of the Track class.
Before the custom NSArrayController
subclass everything worked correctly. By simply commenting out the setRelationshipKeyPathsForPrefetching
line in my NSArrayController
subclass everything works again. I can't determine what the link is between prefetching and KVO. Have any Core Data experts seen something like this before?
Upvotes: 3
Views: 474
Reputation: 513
I ran into essentially the same problem, and this is what I learned while resolving it:
It seems that NSArrayController does not play nicely with setRelationshipKeyPathsForPrefetching:
in all situations. I was executing a prefetching request in a child context and this ended up breaking KVC for an NSArrayController
in the parent context, which I imagine to be unintended behavior. In any case, the way I fixed it was to use the "old" style (10.4) prefetching, which would look something like...
NSFetchRequest *trackReq = [[NSFetchRequest alloc] initWithEntityName:@"Track"];
NSArray *fetchedTracks = [context executeFetchRequest:trackReq error:&error];
NSFetchRequest *batchFaultReq = [[NSFetchRequest alloc] initWithEntityName:@"Artist"];
[batchFaultReq setPredicate:[NSPredicate predicateWithFormat:@"SELF.tracks in %@", fetchedTracks];
NSArray *faultedItems = [context executeFetchRequest:batchFaultReq error:&error];
// do something with fetchedTracks
... how you plug this into your NSArrayController
subclass is another matter - it's not immediately clear to me how to use this method within a fetchWithRequest:
override since you need the fetched results before you can execute the batch faulting request. On a side note, you've experimented with the lazy fetching flag?
Upvotes: 1