Using ios 5 parent, child managed entity:
I have managed object contexts arranged in the following order:
persistent store coordinator ---> Private Queue Managed Object Context ( for saving to disk in background) -----> Main Queue Managed Object Context (for UI) -----> Misc. Private Managed Object Contexts (for temporary jobs like UIImagePNGRepresentation() for example)
The model looks like this:
Image Entity -> title : string , image : relationship(ImageBlob) optional ImageBlob Entity -> image : Binary Data, imageEntity : relationship(Image)
reverse relationships established.
as soon as the user completes the image selection:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { // get the main queue managed object context NSManagedObjectContext* mainQueueManagedObjectContext = self.managedObjectContext; // get the image UIImage* image = [info objectForKey:UIImagePickerControllerOriginalImage]; // create an object, using the managed object context for the main queue NSManagedObject *newImage = [NSEntityDescription insertNewObjectForEntityForName:@"Image" inManagedObjectContext:mainQueueManagedObjectContext]; // edit not expensive properties [newImage setValue:[NSString stringWithFormat:@"new title %i", [self tableView:self.tableView numberOfRowsInSection:0]] forKey:@"title"]; // lets save the main context to get a permanant objectID [self saveContextForManagedObjectContext:mainQueueManagedObjectContext]; // get the permenant objectID, Thread Safe.. NSManagedObjectID* imageObjectID = newImage.objectID; // create a private queue concurrent managed object context NSManagedObjectContext* privateQueueManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; // set the main queue as the parent [privateQueueManagedObjectContext setParentContext:mainQueueManagedObjectContext]; // we have to use blocks here, as this managed object context will work in a private queue [privateQueueManagedObjectContext performBlock: ^{ // get the png representation in background NSData* data = UIImagePNGRepresentation(image); // get the managed object using the thread safe objectID NSManagedObject* imageObjectInPrivateQueue = [privateQueueManagedObjectContext objectWithID:imageObjectID]; // insert a new object for the ImageBlob entity NSManagedObject *imageBlobInPrivateQueue = [NSEntityDescription insertNewObjectForEntityForName:@"ImageBlob" inManagedObjectContext:privateQueueManagedObjectContext]; // set our image data [imageBlobInPrivateQueue setValue:data forKey:@"image"]; // set the relationship to the original record [imageObjectInPrivateQueue setValue:imageBlobInPrivateQueue forKey:@"image"]; // save changes to private queue context to main queue context [self saveContextForManagedObjectContext:privateQueueManagedObjectContext]; // since we are not in the main queue, we have to ask the main managed object context using performBlock [mainQueueManagedObjectContext performBlock: ^{ // what time is it before launching save in main queue NSDate* startDate = [NSDate date]; // launch save on main queue [self saveContextForManagedObjectContext:mainQueueManagedObjectContext]; // what time is it after finishing save in main queue NSDate* finishDate = [NSDate date]; // see how long UI blocked NSLog(@"blocked UI for %f seconds", [finishDate timeIntervalSinceDate:startDate]); }]; }]; if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { [self.popOverController dismissPopoverAnimated:YES]; } else { [self dismissViewControllerAnimated:YES completion:nil]; } }
and here is how to do it:
-(void)saveContextForManagedObjectContext:(NSManagedObjectContext*)managedObjectContext {
This greatly reduces the user interface lock, on the iphone 4, choosing a 5-megapixel image blocks the user interface in just 0.015 seconds.
on the other hand, downloading an image will also block the user interface for a noticeable amount of time, so you can also download it in the background.