Core Data BLOB Management with Virtual Properties

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 – image and 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 image (or thumbnail) property, our virtual getter loads the image from disk using the imagePath (or 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 imagePath.

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 imagePath and thumbnailPath properties.

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 image setter.

On to the implementation:

The 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.

fullImagePath and 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 (imagePath or 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 UIImage reference.

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!

6 responses to “Core Data BLOB Management with Virtual Properties”

  1. Chris W.

    What would be the best way to access this class? I must be missing something because every time I try to create a new instance of this class to say set an image I get yelled at for trying to assign void.

  2. Chris W.

    I realize that I’m having trouble understanding NSManagedObjects. Maybe you could show the best way to implement this.

  3. Nik

    Hey Vladimir!
    I did just start working with CoreData.
    After googeling for a while I thought I best ask here for a hint.

    I would like to save a PDF-file which is stored online into a CoreData entity, called PDF.
    That entity does also store the name of the PDF and its url-Link (both given by the user).
    I think I do understand how to save information/data in coreData in general, but I don’t seem to be able to download a PDF-file, store it in CoreData (as NSData) and load it to a webView whenever I want.
    I tried this code to download it:
    NSString *vc1 = [urlField text];
    NSURL *url2 = [NSURL URLWithString:vc1];
    NSData *data = [NSData dataWithContentsOfURL:url2];
    [self.currentPDF setPdf:data];

    and in a different (the webView) file this to load the PDF:
    [webView loadData:pdf MIMEType:@”application/pdf” textEncodingName:@”utf-8″ baseURL:nil];

    The webView leaves me with a blank screen.

    Greetz
    Nik

  4. Dan

    Hi Vladimir,

    Thanks for sharing – I also had some BLOB’s in my core data model which wasn’t working from a memory perspective, I had an idea of what to do, but your code saved me the work and made it straight forward.

    Nice One!

    Cheers,
    Dan.

Leave a Reply