Keeping BLOBs (Binary Large OBjects) in your Core Data store is generally a bad idea. If you happen to have a large graph of managed objects in your application, it becomes increasingly difficult to manage memory. Faulting can help to a certain extent, but I’ve found it very difficult to successfully manage memory by faulting unused objects in a case where you need all your BLOBs in a single run.
For example, I was writing some code that would print all the photos (represented by the
Photo class) in my managed object graph to a PDF file. I tried to ease up on the memory by faulting every
Photo object as soon as it was printed. The trouble was – the faults weren’t fired until the entire PDF-printing code ended. Thus, I had all my
Photo objects eating up memory, and as soon as all of them were printed (and assuming the app didn’t crash from the lack of memory), they were turned into faults, all at once. Not good.
So I figured I should only keep a URL reference to a photo in my
Photo class, and then load the
UIImage objects from disk when I needed them. The
UIImage objects would not be tied to Core Data in any way, so I could
release them at any time.
The header file is pretty straightforward:
The key to this technique is defining virtual properties, and here we have two of them –
thumbnail. Virtual properties are a nice way of presenting a usable interface to the users of your class, while hiding the actual complexity behind it. Here, we actually don’t have any
UIImage references as part of the
Photo interface (thus, “virtual”). Instead, we have two
NSStrings that hold paths to our images on disk. Whenever a user tries to get the value of the
thumbnail) property, our virtual getter loads the image from disk using the
thumbnailPath) property, and returns a
UIImage object. Similarly, when a user wants to set the
image property, the virtual setter takes the
UIImage object it received, saves it to disk, and saves the path to
This simple technique allows us to separate image handling from Core Data, since we have complete control over the
UIImage objects, while Core Data only handles the getting/setting of
The thumbnail is declared as
readonly, since it is created as a side effect of setting an
image. In other words, there is no way to explicitly define a thumbnail, it is generated for you every time you call the
On to the implementation:
generatePath method generates and returns a unique path for the image file, using the current timestamp. The actual filename doesn’t have to be “pretty” because you never get to see it anyway.
fullThumbnailPath methods return a full path to the image and the thumbnail, respectively. Every time you deploy your app to the device, the actual path to the app folder changes, since your app is given a unique ID for that deployment (for example,
/Applications/932479BB-20CD-4EF1-891A-270BCA026AEC/YourApp.app/). These methods fetch the actual app folder path, append the file name (
thumbnailPath), and return the resulting full path to the JPEG file.
As I mentioned previously, the
setImage: method actually behaves as both the image setter, and the thumbnail setter. Note that for resizing the image I’m using the
-resizedImageWithSize: method defined in a custom category on
UIImage. This basic technique is described in my previous article.
The getters simply read the image/thumbnail from a file and return an autoreleased
And that’s it! You can now use your managed objects without worrying about Core Data hogging up your memory with image data. Of course, you are still responsible for any objects that you explicitly retain in your code.
Obviously, this technique is applicable to any type of BLOB, not just images. Have fun!