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?
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"];
});
}
});
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With