Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core data: executeFetchRequest vs performFetch

I want a thorough list regarding comparison between the two. Things I have known:

executeFetchRequest:

  • Message sent to MOC
  • Return an array of managed objects
  • Goal: fetch objects from persistent store to MOC
  • With table view: has nothing to do with table view
  • Frequency: often used in a loop, so could be called many many times

performFetch:

  • Message sent to FRC
  • After calling it, use fetchedObjects to return an array of managed objects
  • With table view: FRC is specifically for keeping managed objects and table view rows in sync, and use performFetch to initialize that process.
  • Frequency: often only once. Unless fetch request of FRC changes, no need to call performFetch a second time

Please correct me if I am wrong and append the list. Thank you.

like image 346
Philip007 Avatar asked Sep 28 '12 18:09

Philip007


2 Answers

About executeFetchRequest:

Message sent to MOC

Yes

Return an array of managed objects

Yes, but you can also change the type of results you want to retrieve. In NSFetchRequest you can set a different result type with:

- (void)setResultType:(NSFetchRequestResultType)type

where NSFetchRequestResultType can be of different types. Taken from Apple doc:

enum {
   NSManagedObjectResultType        = 0x00,
   NSManagedObjectIDResultType      = 0x01,
   NSDictionaryResultType           = 0x02
   NSCountResultType                = 0x04
};
typedef NSUInteger NSFetchRequestResultType; 

Goal: fetch objects from persistent store to MOC

Yes, creating a NSFetchRequest and performing a request, it the same as creating a SELECT statement in SQL. If you also use a NSPredicate it's the same as using SELECT-WHERE statement.

With table view: has nothing to do with table view

Yes, but with retrieved data you can populate a table

Frequency: often used in a loop, so could be called many many times

It depends, on what you want to achieve. It could be within a loop or not. Executing the request within a loop could have impact on performance but I would not be worried on that. Under the hood Core Data maintains a sort of cache mechanism. Every time you perform a request, if data are not in the cache, Core Data executes a round trip on your store (e.g. sql file) and populate the cache with the objects it has retrieved. If you perform the same query, the round trip will not performed again due to the cache mechanism. Anyway, you could avoid to execute a request within the run loop, simply moving that request outside the loop.

About performFetch:

Message sent to FRC

Yes

After calling it, use fetchedObjects to return an array of managed objects

Yes, but you can also retrieve an object with [_fetchedResultsController objectAtIndexPath:indexPath]; if you are populating a specific cell within a table.

Here I really suggest to read a nice tutorial on NSFetchedResultsController

With table view: FRC is specifically for keeping managed objects and table view rows in sync, and use performFetch to initialize that process.

Yes, a NSFetchedResultsController works in combination with a NSManagedObjectContext for you. Furthermore, it enables lazy loading of data. Suppose you have 1000 elements you retrieve and you want to display them in a UITableView. Setting a request for a NSFetchRequest like:

[fetchRequest setFetchBatchSize:20];

and using it with an instance of a NSFetchedResultsController, it allows to load 20 elements at first. Then when you scroll, other 20 elements are loaded, and so on. Without a NSFetchedResultsController you must implement this behavior manually. Refer to the tutorial I provided for further info.

Frequency: often only once. Unless fetch request of FRC changes, no need to call performFetch a second time

It depends on what you want to achieve. Most of the time you could call it once.

Hope that helps.

Edit

You have to call performFetch explicitly. I like to create a property for NSFetchedResultsController in my header file (.h) like

@property (nonatomic, strong, readonly) NSFetchedResultsController* fetchedResultsController;

and synthesize it in your implementation file (.m) like

@synthesize fetchedResultsController = _fetchedResultsController;

Then always within the .m file override the getter to create an new instance of it:

- (NSFetchedResultsController*)fetchedResultsController
{
    // it already exists, so return it
    if(_fetchedResultsController) return _fetchedResultsController;

    // else create it and return

    _fetchedResultsController = // alloc-init here with complete setup

   return _fetchedResultsController;
}

Once done, within your class (for example in viewDidLoad method) use it like

NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {

    // Handle the error appropriately.
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
like image 157
Lorenzo B Avatar answered Oct 06 '22 09:10

Lorenzo B


You are comparing the wrong elements. NSFetchedResultsController uses the NSManagedObjectContext to perform the fetch, and under proper configuration, monitors the changes in the managed object context to verify the status of the fetch properties it is monitoring, but the actual fetches are done by the context. On both cases, NSManagedObjectContext does the fetch. The difference being that, using the NSManagedObjectContext directly, you get an NSArray type of object (the actual runtime class is different than an array you get using [NSArray array]), while NSFetchedResultsController has a different purpose (have a collection of results and monitor changes to the records and entity on its fetch request). In other words, NSFetchedResultsController works using the context, but it works different than just a simple collection of objects.

One observation: you shouldn't be using executeFetchRequest inside a loop, especially calling it "many many times". Each fetch has its performance cost. You can call executeFetchRequest once, and do a loop to check the result.

like image 41
J2theC Avatar answered Oct 06 '22 07:10

J2theC