I have a random mistake that tormented me for months that I just can't understand. I would say that it fails less than 1 time per 1000 times. I must have the CoreData configuration wrong, but I cannot figure it out or recreate it. The basic idea is that I get some information from the server and then update the CoreData object in the background thread. A CoreData object is not required immediately for the user interface.
All this is done in the DataService, which has a link to the NSManagedObjectContext that was originally created in AppDelegate. Note. Everything that references [DataService sharedService] uses AppDelegate.NSManagedObjectContext:
@interface DataService : NSObject {} @property (nonatomic,strong) NSManagedObjectContext* managedObjectContext; @end
When the server returns with data, the updateProduct method is called:
@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
I use a generic NSManagedObjectContext + Helper.h category file that floats around GitHub and my EXC_BAD_ACCESS KERN_INVALID_ADDRESS error occurs in the [DataService.managedObjectContext mergeChangesFromNotification: (NSNotification *)) method that calls this
@implementation NSManagedObjectContext (Helper) - (void) mergeChangesFromNotification:(NSNotification *)notification { //ERROR HAPPENS HERE [self mergeChangesFromContextDidSaveNotification:notification]; } @end
I canβt understand why the mergeChangesFromContextDidSaveNotification method works randomly. I think the error is due to the loss of the link to the original sharedObjectContext. Although, if that were the case, I would suggest that the error will be in the updateProduct method, and not in the category class.
I believe that the newObjectContext and stopObservingContext methods refer to a managed ObjectContext on a background thread from the main thread. Since I am creating a private managedObjectContext, do I even need to make the general context associated with the main thread accessible to the private context? If so, am I doing it wrong?
Thanks in advance for your help.