5

Ho immagini in coredata che sto cercando di caricare pigramente per una vista tabella. Ogni cella utilizza un osservatore per la relativa entità di dati di base per aggiornare l'immagine quando diventa disponibile. Il codice rilevante l'entità è la seguente:Ottenere EXC_BAD_ACCESS quando si utilizza 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"]; 
    }); 
}); 

Il progetto utilizza ARC, non sto ottenendo eventuali errori di compilazione o avvisi, e quando l'eseguo tipo di lavori fino a quando ho scorrimento veloce, e poi ottengo un EXC_BAD_ACCESS sulla linea quando dichiaro l'i.

Cosa mi manca qui?

+0

Hai provato con 'NSZombieEnabled'? – zoul

+0

cosa succede se non si utilizza dispatch_async? corri sul thread principale –

+0

NSZombie non fa più luce extra per me. Se non dispatch_async bloccherà il thread principale e scorrerà molto male. – dizy

risposta

7

Apparentemente in grado di recuperare CoreData objects is not thread safe. Quindi si consiglia di utilizzare lo stesso persistenteStoreCoordinator, ma diversi ObjectContexts. Ecco il mio codice aggiornato che non sembra più in 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"]; 
    }); 
    } 
}); 
+0

Potrebbe quindi salvare l'immagine sul thread principale, nel contesto dell'oggetto gestito dell'applicazione? Come faresti questo, dal momento che è stato creato sul thread in background? – SAHM

1

Dizy, anche tenere a mente che l'oggetto immagine che si crea nel codice:

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

è fissato per il rilascio automatico. Il metodo dispatch_async viene eseguito sulla coda principale, pertanto esiste la possibilità che la memoria allocata per l'immagine possa essere rilasciata quando il thread principale esegue il blocco di invio.

+0

Buon punto, mi limito a racchiudere l'intera cosa in @autoreleasepool {} per risolvere il problema? – dizy

+0

Vorrei conservare l'immagine nel blocco esterno e rilasciarla nel blocco interno. –

+0

Utilizzo ARC nel progetto – dizy

0

CoreData non è thread-safe, è necessario gestire i contesti per evitare arresti anomali. Se si prevede di utilizzare molti processi simultanei per aggiornare i dati in Core Data, ti suggerisco di dare un'occhiata a MagicalRecord, un modello sorprendente ispirato da Active Record of Rails e che gestisce tutti questi aspetti in un modo davvero intelligente .

+0

Grazie per aver segnalato MagicalRecord, sembra davvero fantastico. – dizy

Problemi correlati