Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CoreData ordered relationships - batch unfaulting using NSFetchRequest

Background - Batch unfaulting:
NSFetchRequest allows batch unfault - for example, use a query of 1000 results, it would bring all as faults, then it would unfault X objects at a time (i.e. index 0-20, then 21-40, etc)

This behavior is great when used in NSFetchResultsController for a UITableViewDataSource, and it allows fast UI scrolling as it doesn't unfault objects one-by-one.

Now to my problem:
I'm using ordered relationships for lists of objects, let's say Posts.

Since a Post may appear on a lot of lists on my model, I can't store its index in every lists on Post entity and use it as a param for ordering results.

As for now, I haven't found a way for NSFetchRequest to fetch according to this order, so I can't use its batch unfaulting. So I'm addressing to the relationship with an index, and I end up unfaulting one-by-one, which causes bumpy scrolling.

Is there any way for NSFetchResultsController to fetch according to order relationships? Or, is there a batch unfaulting API which isn't private?

like image 313
Avishay Cohen Avatar asked Jun 18 '13 08:06

Avishay Cohen


1 Answers

Currently I have a solution for that, but not a clean one:
I want to batch unfault by groups of 20, in an ordered relationship.

So, every time i address an index that its index divides by 20 (index % 20 == 0), I use a new NSFetchRequest for the next 20 and unfault them by calling a common field name.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row % 20 == 0) {
        NSArray *objectsToUnfault = [[someObject.orderedRelationship array] subarrayWithRange:NSMakeRange(indexPath.row, MIN(20, [someObject.orderedRelationship count] - indexPath.row))];
        // It's important to hold on this array, otherwise objects fault back
        self.lastPrefetch = [self preFetchEntityOfClass:[Post class] faultObjects:objectsToUnfault error:&error];
    }
//... continue and create cell
}


- (NSArray *)preFetchEntityOfClass:(Class)entityClass faultObjects:(NSArray*)objects error:(NSError **)error {
    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:NSStringFromClass(entityClass) inManagedObjectContext:self.managedObjectContext];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF in %@", objects];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDescription];
    [request setPredicate:predicate];
    [request setFetchBatchSize:MIN([objects count], 20)];
    NSArray *results = [self.managedObjectContext executeFetchRequest:request error:error];
    // I assume all my objects has this field "uid"
    NSArray *resultsUid = [results valueForKey:@"uid"]; //Batch unfaulting (results is a BatchFaultArray, private class type)
    if ([resultsUid count] != [results count]) {
        NSLog(@"Error: wrong count of uids"); //We need to use resultsUid, to avoid compiler opt
    }
    return results;
}
like image 114
Avishay Cohen Avatar answered Sep 30 '22 10:09

Avishay Cohen