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?
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With