Core Data Image Caching — 8 Comments

  1. Would it not be better to store the image data on disk and just use the core data objects to keep track of the meta-data (url, last used time stamp), and then override the prepareForDeletion method to clean up the files? Storing the files so you can find them easily and have them unique is easy – just make a hash out of either the url or the core data id (make sure its a permanent one and not the temporary one :P)

    That way you can fetch and loop through and do whatever with the Cache records, without making core data fetch the image data into ram constantly. Should be a reasonable performance boost without much effort…

  2. Hi Tom- Thanks for that suggestion; it’s a nice optimization. One detail I left out of my article is that once I fetch a managed object from CoreData, I keep it in an in-memory dictionary. So once the image data (as part of the managed object) is read in, it’s in memory. So the next time it’s needed, that I/O is not required. That being said, your suggestion is still valid and moves in the direction of combining available caching techniques into interesting hybrid solutions. Thanks!

  3. Did you keep the “data” field because it already existed, for compatibility with previous versions, or for some other reason? I think I would want to fetch the content for an URL and make it into an image straight away, since the request is presumably happening because we need to show that thing on the screen right now.

  4. It was a move of desperation. 🙂 My hope was that the lightweight migration would automagically migrate the original model (url, data, timestamp) to the new model (url, image, timestamp) using a mapping model that mapped data -> image. But that did not work. So I made the new model include all the old model’s attributes plus a new attribute, image. The mapping model maps the old attributes directly to their counterparts in the new model and sets the new image attribute to 0 (or nil, effectively). Then in code, I check for a nil image value in the managed object and I transform in code from the data attribute to the image attribute, and set the data attribute to nil. This is perhaps not the “right” way to have done this, but it worked.

  5. This is easier if you use relationships for Large Data Objects (BLOBs). As with all performance issues I would advise using the simplest method until its a performance problem. I’ve been much happier when I get it to work first and then use profile driven optimizations. Additionally, after 20 years of programming, its also amazing how quickly optimizations with libraries or OS’s obsolete application level optimizations.

  6. Hi Todd- Thanks for your comment. As for using the simplest methods first, I agree. However, in this particular case, where I started using an NSDictionary, I found that reading and writing it became a real bottleneck, which is why I started looking at CoreData.

  7. I think the one thing to keep in mind here is iCloud Backup, specifically if you are using Core Data on iOS.

    Specifically, if you are storing your Core Data sqlite file in the Documents directory iCloud is going to automatically back it up when the user connects their phone to power (if they have so configured ).

    The issue with storing blob data in that sqlite file is that this single file is now growing in size with each blob added and I’d think iCloud would then re-backup the entire file in turn, no? This can get prohibitively slow / data intensive when adding a 5KB image to a 500MB database means a new 500MB+5K backup.

    Unless Apple has some sort of “row-level” backups strategy when dealing with Core Data sqlite files? They do row-level synchronization for iCloud sync so it’s possible – but I can’t find any reference to this anywhere.

  8. Dylan- You are absolutely correct. In the implementations of this CoreData caching scheme I have explicitely NOT put the cache file in the ~/Documents directory for exactly the reasons you highlight. It is a cache, after all. So the right place to put the file is in ~/Library/Caches, so iOS can reclaim that space if it needs to.

    There is a feature added in iOS 5.0.1 that lets an app set a “no backup” bit on any file. That’s another way to tell the system not to back up something.

    Basically, the rule of thumb is that if the user created the data, it should go in ~/Documents. If they didn’t and it can be easily recreated or refetched, it should go into ~/Library/Caches.

Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: