2014-07-15 16 views
6

Ho un bug casuale che mi ha afflitto per mesi che ho semplicemente non riesco a capire. Direi che fallisce meno di 1 su 1000 volte. Devo avere CoreData configurato in modo errato ma non riesco a capirlo o ricrearlo. L'essenziale è che ricevo alcune informazioni dal server e sto aggiornando un oggetto CoreData in un thread in background. L'oggetto CoreData non è immediatamente necessario per l'interfaccia utente.CoreData aggiornamento in background thread è a caso EXC_BAD_ACCESS KERN_INVALID_ADDRESS errore

Tutto questo viene eseguito in DataService che ha un riferimento al NSManagedObjectContext che è stato originariamente creato nel AppDelegate. Nota: Tutto ciò che i riferimenti [DataService sharedService] utilizza l'AppDelegate.NSManagedObjectContext:

@interface DataService : NSObject {} 
    @property (nonatomic,strong) NSManagedObjectContext* managedObjectContext; 
@end 

Quando il server restituisce con i dati del metodo updateProduct si chiama:

@implementation DataService 

    + (NSManagedObjectContext*) newObjectContext 
    { 
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];   //step 1 

    AppDelegate* appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate]; 

    [context setPersistentStoreCoordinator:appDelegate.persistentStoreCoordinator]; 
    [context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];    
    [appDelegate.managedObjectContext observeContext:context]; 

    return context; 
    } 

    +(void) saveContext:(NSManagedObjectContext*) context 
    { 
    NSError *error = nil; 
    if (context != nil) { 
     if ([context hasChanges] && ![context save:&error]) { 
     // Handle Error 
     } 
    } 
    } 

    +(void) updateProduct: (Product*) product 
    { 
    if(product == nil) 
     return; 

    //run in background 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), ^(void){ 

     //create private managed object 
     NSManagedObjectContext *context = [DataService newObjectContext]; 

     CoreDataProduct* coreProduct = [DataService product:product.productId withObjectContext:context]; 
     if(product != nil) 
     { 
     //copy data over from product 
     coreProduct.text = product.text; 

     //ERROR HAPPENS HERE on save changes 
     [DataService saveContext:context]; 
     } 

    //remove background context listening from main thread 
    [DataService.managedObjectContext stopObservingContext:context]; 

}); 
    } 

@end 

Io uso il generale NSManagedObjectContext + helper.h categoria di file che è galleggianti intorno GitHub e il mio errore EXC_BAD_ACCESS KERN_INVALID_ADDRESS accade nel [DataService.managedObjectContext mergeChangesFromNotification: (NSNotification *) notifica] metodo che chiama questo

@implementation NSManagedObjectContext (Helper) 

    - (void) mergeChangesFromNotification:(NSNotification *)notification 
    { 
    //ERROR HAPPENS HERE 
    [self mergeChangesFromContextDidSaveNotification:notification]; 
    } 

@end 

io non riesco a capire il motivo per cui il metodo mergeChangesFromContextDidSaveNotification fallisce in modo casuale. Penso che l'errore sia dovuto alla perdita del riferimento al managedObjectContext condiviso originale. Anche se fosse vero, suppongo che mi aspetterei che l'errore si trovi nel metodo updateProduct e non nella classe category.

suppongo che sia il newObjectContext ei metodi stopObservingContext fanno riferimento al managedObjectContext sul thread in background dal thread principale. Dal momento che sto creando un oggetto privato gestitoObjectContext, devo anche rendere consapevole il contesto condiviso del thread principale del contesto privato? Se sì, lo sto facendo in modo errato?

Grazie in anticipo per l'aiuto.

+0

Forse si dovrebbe mostrare il metodo in mancanza, 'mergeChanges ...'. – Mundi

+0

L'ultimo elemento nella traccia dello stack è - [NSManagedObjectContext _mergeChangesFromDidSaveDictionary: usingObjectIDs:], che non credo fornisca molte informazioni. – dirkoneill

+0

Forse non dovresti usare GCD ma le API della concorrenza contestuale come 'performBlock'. Inoltre, inserire un punto di interruzione sulla linea ed esaminare la notifica. – Mundi

risposta

4

Appare perché il nuovo NSManagedObjectContext veniva creato sul thread in background, l'originale/genitore NSManagedObjectContext doveva essere osservato sul thread principale. Una volta modificato ObservContext per osservareContextOnMainThread, questo problema di CoreData sembra essere andato via. Spero che questo aiuti qualcuno.

Ecco il mio metodo aggiornato:

+ (NSManagedObjectContext*) newObjectContext 
    { 
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];   //step 1 

    AppDelegate* appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate]; 

    [context setPersistentStoreCoordinator:appDelegate.persistentStoreCoordinator]; 
    [context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];    
    [appDelegate.managedObjectContext observeContextOnMainThread:context]; 

    return context; 
    } 
Problemi correlati