I have a problem that I can't understand how to handle because does not happen logically.
I have some NSOperation
s that run concurrently. For example,
- (void)main
{
@autoreleasepool
{
AppDelegate *appController = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.managedObjectContext = [[NSManagedObjectContext alloc] init];
[self.managedObjectContext setUndoManager:nil];
[self.managedObjectContext setPersistentStoreCoordinator: [appController persistentStoreCoordinator]];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:self.managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:@"Entity" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%K != %@",@"number1",[NSNumber numberWithInt:2]]];
NSError *error;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *obj in fetchedObjects) {
//Do Something with managed object then save
NSError *error = nil;
//[episode release];
if (![self.managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate.
// You should not use this function in a shipping application, although it may be useful
// during development. If it is not possible to recover from the error, display an alert
// panel that instructs the user to quit the application by pressing the Home button.
//
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
}
- (void)mergeChanges:(NSNotification *)notification
{
AppDelegate *appController = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *mainContext = [appController managedObjectContext];
// Merge changes into the main context on the main thread
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
}
This is my typical NSOperation
, that works concurrently and update my object in the core data, and sometime with no explanation the app crash and I receive that error on this line:
if (![self.managedObjectContext save:&error])
In my crash reports, so my question is, there is a way to prevent the app crash and fix the error? Can I can use a @syncronized
when I perform save
? Is this due to different threads and different objects? How can I fix this?
Move the code you use to merge changes in the app delegate.
So, within application:didFinishLaunchingWithOptions:
register for the notification.
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:nil]; // set nil here
Then, always within the app delegate create your mergesChanges:
method. Here you need to be sure that the notification runs on the main thread and the context you received the notification is different from the main one.
- (void)mergeChanges:(NSNotification *)notification
{
if ([notification object] == [self managedObjectContext])
return;
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(mergeChanges:) withObject:notification waitUntilDone:YES];
return;
}
NSManagedObjectContext *mainContext = [self managedObjectContext];
// Merge changes into the main context on the main thread
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
}
P.S. Here you are using a non-concurrent NSOperation
that, if inserted into a NSOperationQueue
, will run in a concurrent manner (the queue, by means of GCD will manage this for you).