Enchilada
Enchilada

Reputation: 3919

A simple way to make Core Data fetching perform a bit better

Let's say I have a basic iPhone app that maintains contacts. I have an entity named "Contact" that has the attributes "name", "address" and "photo". The app's main view is just a table view that displays the name of each contact. The user can then click on each name to display the corresponding photo and address (in a separate view controller).

The problem is, photos can have a very large size.

Isn't it the case that when I fetch managed objects, all of its attributes also get brought into memory? In this case, it could take a long time to load the app and display the names in the table view, because the app is also underneath fetching all the photos (and addresses), right?

For this reason, wouldn't you say that I should—in order to make the main view more snappy (and the app faster to load)—make a new "ContactFacade" entity which has only the "name" attribute and a relationship to a "Contact" entity that contains further details about that contact?

Upvotes: 0

Views: 475

Answers (4)

chris
chris

Reputation: 16463

Apple has a small section on their Core Data Performance page called "Large Data Objects (BLOBs)". The part of interest is:

For small to modest sized BLOBs (and CLOBs), you should create a separate entity for the data and create a to-one relationship in place of the attribute.

I have seen great performance improvements in my app after implementing this change. However, I was in a situation where I needed to fetch 300+ objects in a single request for displaying on a map. In your situation you can use a NSFetchedResultsController to manage the data in your table, and this will limit the fetches to a small subset of your data.

Upvotes: 0

adonoho
adonoho

Reputation: 4339

Enchilada,

Large BLOBs should always be in "leaf" entities. A "leaf entity only contains the NSData BLOB and the inverse relation. This also allows you to easily exploit iOS v5/Mac OS X Lion's automatic BLOB saving in the filesystem. By doing this, you can control explicitly when they are loaded. Just as importantly, by using -refreshObject:mergeChanges: judiciously, you can force the BLOBs from your memory and, potentially, the row cache. This one change will dramatically improve your fetch and loading performance.

One caveat to depending upon Core Data's file mapping for large BLOBs is that it doesn't use memory mapping to open the large file. This will hit your resident footprint in memory. You will need to explicitly unload these BLOBs when the memory warnings come, and they will come.

Andrew

Upvotes: 1

Abhi Beckert
Abhi Beckert

Reputation: 33379

Instead of storing the images in core data, store them as files on the disk and only store the filename in core data.

If you use UIImage to read an image from a file, it will automatically remove itself from ram and load itself back into ram, as necessary.

If you want really good performance, you should make sure the images are smaller, or perhaps have two sizes (thumb and full size).

Also, pay attention to what image format you should use. PNG will use more disk space, but it requires less CPU time to render than a JPEG.

Upvotes: 4

Damien Del Russo
Damien Del Russo

Reputation: 1048

Core data figures this stuff out for you - it will not fetch all the photos and such until you actually need to show them. The UI will not block.

For excellent example, check out the Stanford iOS class in iTunes U, specifically the Core Data lecture and Core Data demo (lectures 13 and 14). They even have a CoreDataTableViewController that you can easily hook up to your FetchResultsController, which I have been using in my current project.

http://www.stanford.edu/class/cs193p/cgi-bin/drupal/node/289

The example app there not only displays large photos, it does it efficiently from the network.

Enjoy,

Damien

Upvotes: 1

Related Questions