5

Mam obrazy w coredata, które próbuję załadować leniwie dla widoku tabeli. Każda komórka używa obserwatora dla pokrewnej głównej jednostki danych, aby zaktualizować obraz, gdy będzie on dostępny. Odpowiedni kod w jednostce jest następujący:Uzyskiwanie EXC_BAD_ACCESS przy użyciu dispatch_async z 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"]; 
    }); 
}); 

Projekt wykorzystuje ARC, nie otrzymuję żadnych błędów kompilatora lub ostrzeżenia, a gdy uruchamiam to niby działa, dopóki nie przewinąć szybko, a potem dostać EXC_BAD_ACCESS na linii, gdy deklaruję i.

Czego mi tu brakuje?

+0

Czy próbowałeś z 'NSZombieEnabled'? – zoul

+0

co się stanie, jeśli nie używasz dispatch_async? wystarczy uruchomić na głównym wątku –

+0

NSZombie nie rzuca dla mnie dodatkowego światła. Jeśli nie dokonam dispatch_async, zablokuje on główny wątek i przewinie się naprawdę słabo. – dizy

Odpowiedz

7

Najwyraźniej pobieranie CoreData objects is not thread safe. Sugeruje się użycie tego samego persistentStoreCoordinator, ale innego ObjectContexts. Tu jest mój zaktualizowany kod, który nie wydaje się już do katastrofy:

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

Czy można następnie zapisać obraz w wątku głównym w kontekście obiektu zarządzanego aplikacji? Jak byś to zrobił, skoro został stworzony w wątku tła? – SAHM

1

Dizy, również pamiętać, że obiekt Obraz, który jest tworzony w kodzie:

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

jest ustawiony autorelease. Metoda dispatch_async działa w głównej kolejce, więc istnieje szansa, że ​​pamięć przydzielona dla obrazu może zostać zwolniona do czasu, gdy główny wątek uruchomi blok wysyłki.

+0

Dobra uwaga, czy po prostu zawijam całość w @autoreleasepool {}, aby rozwiązać ten problem? – dizy

+0

Zatrzymałbym obraz w zewnętrznym bloku, a następnie zwolniłbym go w wewnętrznym bloku. –

+0

Używam ARC w projekcie – dizy

0

CoreData nie jest bezpieczna dla wątków, musisz zarządzać kontekstami, aby uniknąć awarii. Jeśli planujesz intensywnie używać wielu równoległych procesów do aktualizacji danych w Core Data, proponuję, abyś spojrzał na MagicalRecord, niesamowity wzór inspirowany Active Record of Rails i który obsługuje wszystkie te aspekty w naprawdę sprytny sposób .

+0

Dzięki za wskazanie MagicalRecord brzmi niesamowicie. – dizy

Powiązane problemy