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.
Forse si dovrebbe mostrare il metodo in mancanza, 'mergeChanges ...'. – Mundi
L'ultimo elemento nella traccia dello stack è - [NSManagedObjectContext _mergeChangesFromDidSaveDictionary: usingObjectIDs:], che non credo fornisca molte informazioni. – dirkoneill
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