Ser Pounce
Ser Pounce

Reputation: 14553

Bad performance for NSFetchedResultsController

I am trying to load items from CoreData into a UITableView. The initial way I did it was to simply grab all the objects from my BankInfo entity, stuff them into an array, and then use the array to populate the UITableViewCells:

- (NSMutableArray *) bankInfos
{
    NSManagedObjectContext *context = [self managedObjectContext];

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"BankInfo" inManagedObjectContext:context];
    [fetchRequest setEntity:entity];
    NSError *error;
    NSMutableArray *bankInfos = (NSMutableArray*)[context executeFetchRequest:fetchRequest error:&error];
    return bankInfos;
} 

I'd heard that NSFetchedResultsController could improve performance / memory management so I tried it out (basically implemented it the way Ray Wenderlich tutorial recommended):

- (NSFetchedResultsController *)fetchedResultsController {

    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }

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

    NSSortDescriptor *sort = [[NSSortDescriptor alloc]
        initWithKey:@"details.closeDate" ascending:NO];
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];

    [fetchRequest setFetchBatchSize:20];

    NSFetchedResultsController *theFetchedResultsController =
        [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
            managedObjectContext:managedObjectContext sectionNameKeyPath:nil
            cacheName:@"Root"];
    self.fetchedResultsController = theFetchedResultsController;
    _fetchedResultsController.delegate = self;

    return _fetchedResultsController;

}

What I'm finding after using instruments to profile the code is that the NSFetchedResultsController is taking about twice as long to load the objects into the UITableView as my initial method was. This line in particular:

BankInfo *bankInfo = [_fetchedResultsController objectAtIndexPath:indexPath]; 

is taking 292 ms whereas loading the entire array of BankInfos is taking about 150 ms. Anyone know why this is?

Upvotes: 2

Views: 1140

Answers (2)

Ser Pounce
Ser Pounce

Reputation: 14553

The problem I was having didn't have to do with CoreData performance, but had to do with the fact that I was accidentally saving / loading full size images as thumbnails in the tableview. Once I fixed this, the performance issues went away.

Upvotes: 3

Leonardo
Leonardo

Reputation: 9857

Well, we are talking about ms, it is still quite fast.

The fetched results controller is doing a query to sqllite for each cell. You can turn on sqllite debug in xcode: -com.apple.CoreData.SQLDebug 1 and see for yourself. The NSArray is populated, stored, and fetched entirely in memory.

The choice between an array and fetched controller is not to be done by taking 'speed' into account.

Basically, if you have a small array of objects, immutable while on screen, then you can safely choose NSArray as table datasource.

Instead, if you have lot of objects or planning to have a growing numbers of objects, that also need to be refreshed often, NSFetchedResultsController is the preferred choice.

Upvotes: 1

Related Questions