5

Tengo imágenes en coredata que estoy tratando de cargar perezosamente para una vista de tabla. Cada celda usa un observador para la entidad de datos central relacionada para actualizar la imagen cuando una esté disponible. El código correspondiente en la entidad es el siguiente:Obteniendo EXC_BAD_ACCESS al usar dispatch_async con Core Data

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"]; 
    }); 
}); 

El proyecto utiliza ARC, ahora no recibo los errores de compilación o advertencias, y cuando lo ejecuto tipo de obras hasta que desplazarse rápidamente, y luego me sale un EXC_BAD_ACCESS en la línea cuando estoy declarando el i.

¿Qué me falta aquí?

+0

¿Has probado con 'NSZombieEnabled'? – zoul

+0

¿Qué ocurre si no usa dispatch_async? simplemente ejecute en el hilo principal –

+0

NSZombie no arroja ninguna luz adicional para mí. Si no envío dispatch_async, bloqueará el hilo principal y se desplazará muy mal. – dizy

Respuesta

7

Aparentemente buscando CoreData objects is not thread safe. Por lo tanto, se sugiere utilizar el mismo persistentStoreCoordinator, pero diferentes ObjectContexts. Aquí está mi código actualizado que ya no presenta problemas:

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"]; 
    }); 
    } 
}); 
+0

¿Podría guardar la imagen en el hilo principal, en el contexto del objeto gestionado de la aplicación? ¿Cómo harías esto, ya que fue creado en el hilo de fondo? – SAHM

1

Dizy, también tenga en cuenta que el objeto de imagen que se crea en el código:

UIImage *i = [UIImage imageWithData:self.imageEntity.data]; 

se establece para autorelease. El método dispatch_async se ejecuta en la cola principal, por lo que existe la posibilidad de que la memoria asignada para la imagen se libere cuando el hilo principal ejecuta el bloque de despacho.

+0

Buen punto, ¿acabo de envolver todo en @autoreleasepool {} para resolver eso? – dizy

+0

Retendré la imagen en el bloque exterior y luego la liberaré en el bloque interno. –

+0

Estoy usando ARC en el proyecto – dizy

0

CoreData no es seguro para subprocesos, debe administrar contextos para evitar bloqueos. Si planea utilizar muchos procesos simultáneos para actualizar datos en Core Data, le sugiero que consulte MagicalRecord, un sorprendente patrón inspirado en Active Record of Rails y que maneje todos estos aspectos de una manera realmente inteligente. .

+0

Gracias por señalar MagicalRecord, suena bastante increíble. – dizy