Storing identical data efficiently in a Core Data data model
I have a data model that contains several entities, each with several different attributes that store image data. These will all be small images and I need to store them in the persistent store rather than as external files.
While I can just store the image data in a Binary or Transformable attribute, it's quite likely that the user will specify identical images for two or more of these attributes, so I'd rather store one copy of each unique images rather than duplicating the image data.开发者_开发知识库
I have messed around with creating an "ImageBlob" entity to store the image data and using relationships to do this but I'm a newbie with Core Data and it is not immediately apparent to me if this is the right way to go. In particular, how do I deal with the following situations?
- I want all of my image attributes in multiple entities to use the same "image data store" so that only one instance of each image blob is stored
- I need to ensure that if no objects are using an image in the data store that it is removed
What would be the best way of handling this?
My first question would be how do you plan to identify when two objects are using the same image? Is there a property on the image you can store and query to determine whether the image you're setting already exists? And how expensive, computationally, is that? If it takes a lot of time, you might end up optimizing for storage and impacting performance.
However, if you do have a way of doing this efficiently, you could create an ImageBlob
entity to do what you describe. The entity that uses ImageBlob
s should have an imageBlob
or imageBlobs
relationship with ImageBlob
. ImageBlob
should have an inverse relationship with a name like, for example, users
.
In your code, when you want to reuse an ImageBlob
, it's as simple as doing something like this:
NSManagedObject *blob = // get the image blob
NSManagedObject *user = // get the user
[user setValue:blob forKey:@"imageBlob"]; // do this if it uses a single image
[[user mutableSetValueForKey:@"imageBlobs"] addObject:blob]; // do this if it uses multiple images
Another consideration you'll want to think about is what to do with blobs that are no longer needed. Presumably, you want to drop any images that aren't being used. To do this, you can sign up your application delegate or NSPersistentDocument
subclass (depending on whether your app is document-based or not) for the NSManagedObjectContextObjectsDidChangeNotification
notification. Whenever the managed object context changes, you can delete any unneeded images like this:
- (void)managedObjectContextObjectsDidSave:(NSNotification *)notification {
NSManagedObjectContext *managedObjectContext = [notification object];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntity entityWithName:@"ImageBlob" inManagedObjectContext:managedObjectContext]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"users.@count == 0"];
NSArray *unusedBlobs = [managedObjectContext executeFetchRequest:fetchRequest error:nil]; // Don't be stupid like me; catch and handle the error
[fetchRequest release];
for (NSManagedObject *blob in unusedBlobs) {
[managedObjectContext deleteObject:blob];
}
}
You could add a unique property called md5 to the Image
Entity to make sure you're only storing the same images once.
As for the Core Data stuff, I think something like this might work:
Then, make an abstract parent Entity (Parent
). Add a relationship from Parent
to Image
called image
, and set "Cascade" for the deletion method so that when you delete Parent
, Image
is also deleted. Add a relationship from Image
to Parent
called parent
, or whatever, and set "Nullify" for the deletion method so that when you delete Image
, the image for Parent
is set to nil
. Then, add your other entities and set their parent to Parent
.
精彩评论