Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting EXC_BAD_ACCESS when using dispatch_async with Core Data

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?

like image 938
dizy Avatar asked Feb 23 '12 06:02

dizy


2 Answers

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"];
    });
  }
});
like image 110
dizy Avatar answered Oct 16 '22 03:10

dizy


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.

like image 1
Jack Cox Avatar answered Oct 16 '22 03:10

Jack Cox