Reputation: 7959
I have images in coredata which I'm trying to load lazily for a table view. Each cell uses an observer for the related core data entity to update the image when one becomes available. The relevant code in the entity is as follows:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// The heavy lifting seems to be during firing of the fault and accessing data,
// so i'm trying to do that in the background thread.
UIImage *i = [UIImage imageWithData:self.imageEntity.data];
// I now need to notify observers that the image is ready on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
[self willChangeValueForKey:@"image"];
image = i;
[self didChangeValueForKey:@"image"];
});
});
The project uses ARC, I'm not getting any compiler errors or warnings, and when I run it kind of works until I scroll fast, and then I get a EXC_BAD_ACCESS on the line when i'm declaring the i.
What am I missing here?
Upvotes: 5
Views: 6570
Reputation: 7959
Apparently fetching CoreData objects is not thread safe. So it's suggested to use the same persistentStoreCoordinator, but different ObjectContexts. Here's my updated code that no longer seems to crash:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@autoreleasepool {
// Create a new context
NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] init];
// Use an existing coordinator
NSPersistentStoreCoordinator *coordinator = [[DataSource sharedDataSource] persistentStoreCoordinator];
[backgroundContext setPersistentStoreCoordinator:coordinator];
// Getting objectID does not fire the fault, so we grab it but actually fetch the object
// on a background context to be thread safe.
Image *imageEntity = (Image*)[backgroundContext objectWithID:self.imageEntity.objectID];
image = [UIImage imageWithData:imageEntity.data];
// Notify observers that the image is ready on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
[self willChangeValueForKey:@"image"];
[self didChangeValueForKey:@"image"];
});
}
});
Upvotes: 7
Reputation: 7055
CoreData is not thread safe, you've to manage contexts to avoid crashes. If you plan to heavy use a lot of concurrent processes to update data in Core Data, I would suggest you to take a look to MagicalRecord, an amazing pattern inspired by Active Record of Rails and that handles all these aspects in a really smart way.
Upvotes: 0
Reputation: 3300
Dizy, also keep in mind that the Image object that is created in the code:
UIImage *i = [UIImage imageWithData:self.imageEntity.data];
is set for autorelease. The dispatch_async method runs on the main queue, so there is a chance that the memory allocated for the image may be released by the time the main thread runs the dispatch block.
Upvotes: 1