I'm getting the error "Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Context already has a coordinator; cannot replace.'" while executing my app.
In the DataBaseManager.m I have the following code :
AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *privateWriterContext = [sharedDelegate privateWriterContext];
NSManagedObjectContext *context = [sharedDelegate managedObjectContextChild];
context.parentContext = privateWriterContext;
NSManagedObjectContext *contextforThread = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
contextforThread.parentContext = context;
[contextforThread performBlock:^{
//things to do in background
NSError *error;
if (![contextforThread save:&error])
{
// handle error
}
[context performBlock:^{
NSError *error;
if (![context save:&error])
{
// handle error
}
[privateWriterContext performBlock:^{
NSError *error;
if (![privateWriterContext save:&error])
{
// handle error
}
}];
}];
}];
and in appDelegate.m I have the following code :
- (NSManagedObjectContext *)managedObjectContextChild
{
if (_managedObjectContextChild != nil) {
return _managedObjectContextChild;
}
_managedObjectContextChild = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
return _managedObjectContextChild;
}
- (NSManagedObjectContext *)privateWriterContext
{
if (_privateWriterContext != nil) {
return _privateWriterContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_privateWriterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_privateWriterContext setPersistentStoreCoordinator:coordinator];
}
return _privateWriterContext;
}
Xcode shows the line
context.parentContext = privateWriterContext;
when error occurs.
How could I rectify this error?
At the top of - (NSManagedObjectContext *)managedObjectContextChild put a breakpoint and see where else that NSManagedObjectContext is being used.
The error is telling you that it already has a persistent store coordinator, so that's your line of attack.
You are accessing these as single instances so the other areas of the application need to know what to do with them (and what not to do!). You could also inspect persistentStoreCoordinator and parentContext property just before the error to help get a picture of what's happening.
It's nothing terrible and shouldn't be too difficult to find with a couple of sensibly placed breakpoints, however you may have to think about how you manage the various contexts more carefully to solve the issue.
Related
I have a NSManagedObjectContext stacked like this:
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext != nil )
{
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
NSManagedObjectContext* masterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
masterContext.undoManager = nil;
[masterContext setPersistentStoreCoordinator:coordinator];
NSManagedObjectContext* mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
mainContext.parentContext = masterContext;
mainContext.undoManager = nil;
NSManagedObjectContext* workerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
workerContext.parentContext = mainContext;
workerContext.undoManager = nil;
_managedObjectContext = workerContext;
}
return _managedObjectContext;
}
I am using this context everywhere to read/write on objects of type NSManagedObject.
Here is the save method which is recursive in nature and saves the changes
+ (void) saveManagedObjectContext:(NSManagedObjectContext *)managedObjectContext withCompletion:(void (^)(BOOL, NSError *))completion
{
NSLog(#"\nout current queue: %#",[NSOperationQueue currentQueue]);
[managedObjectContext performBlock:^{
NSLog(#"\ninside current queue: %# current thread: %#",[NSOperationQueue currentQueue],[NSThread currentThread]);
NSError *error = nil;
if (![managedObjectContext save:&error]){
completion(NO, error);
} else {
if ([managedObjectContext parentContext] != nil){
[self saveManagedObjectContext:[managedObjectContext parentContext] withCompletion:completion];
} else {
completion(YES, error);
}
}
}];
}
But my app UI gets freeze every time the saveManagedObjectContext: method is called.
Moreover the NSLog statements gives unusual output:
out current queue: <NSOperationQueue: 0x15c527680>{name = 'NSOperationQueue Main Queue'}
inside current queue: (null) current thread: <NSThread: 0x15c6a86d0>{number = 4, name = (null)}
Why current queue is null?
Why my app is freezing or main thread is being blocked ?
In my app, I am using using CoreData to save data, and also I am pushing and pulling data from server. I also sync data across multiple devices, with same account.
But recently, I am getting this crash from crashLytics report.
This NSPersistentStoreCoordinator has no persistent stores. It cannot perform a save operation
So in my below function, I am calling "saveContext" method this way.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//Creating a new instance of an managed object.
Entity *cycle = [NSEntityDescription insertNewObjectForEntityForName:#"Entity" inManagedObjectContext:appDelegate.managedObjectContext];
[cycle setEmailID:m_emailId];
[cycle setStartDate:dateFromServer];
[cycle setEndDate:dateFromServer];
[appDelegate saveContext];
}
//appDelegate.m
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil)
{
return _persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"LoveCycles.sqlite"];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
{
*/
//NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
//abort();
}
return _persistentStoreCoordinator;
}
- (void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil)
{
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error])
{
*/
// NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
//abort();
}
}
}
Create ManagedObject Context (MOC)
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil)
{
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
So I have few doubts.
1) Should I call "[appDelegate saveContext]" everytime, the data is received from server, or it should be saved on only exit of the app.
2) CrashLytics report is pointing to "[appDelegate saveContext]". Also this is happening from iOS 9.
3) I am not able to replicate this issue on my device, running iOS 9. So not understanding, whether user is actually is seeing this crash, and what are the consequences of this error.
Somewhere you need to associate the ManagedObjectContext with the persistentStore, like this (Swift code):
lazy var managedObjectContext: NSManagedObjectContext? = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
if coordinator == nil {
FLOG(5, message: " Error getting managedObjectContext because persistentStoreCoordinator is nil")
return nil
}
var managedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
// Set the MergePolicy to prioritise external inputs
let mergePolicy = NSMergePolicy(mergeType:NSMergePolicyType.MergeByPropertyStoreTrumpMergePolicyType )
managedObjectContext.mergePolicy = mergePolicy
return managedObjectContext
}()
I am using CoreData in my app and I have two main entities, let's say Cats and Dogs.
I have this method that I use to initialise my context:
- (NSManagedObjectContext *)managedObjectContext
{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
It refers to this method in my App Delegate:
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
Now, at some point, I have to update some data in my Cats entity.
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Cats"];
NSMutableArray *catsArrayData = [[context executeFetchRequest:fetchRequest error:nil] mutableCopy];
NSManagedObject *aCat = [catsArrayData objectAtIndex:i];
NSMutableArray *catsArray = [aCat valueForKey:#"cat"];
[catsArray addObject:responseData];
[aCat setValue:catsArray forKey:#"cat"];
NSError *error = nil;
[context save:&error];
Then, I want to do the same for my Dogs entity. So I use the same code, but for the dogs entity. It's in another part of my code, so I have to redefine:
NSManagedObjectContext *context = [self managedObjectContext];
But the problem is that even though I save this second context, it is actually not saved. The data isn't there. How can I resolve this issue?
You need implement your own change notification mechanism, This is good source to learn it. Multi-Context CoreData.
I am modifying an existing Core Data app to iCloud. My problem is that when I try to load seed data after detecting an initial app launch in the appDelegate, my first [NSEntityDescription insertNewObjectForEntityForName: inManagedObjectContext:] statement throws the error "'NSInvalidArgumentException', reason: '+entityForName:XXX nil is not a legal NSManagedObjectContext parameter searching for entity name 'XXX''" I'm sure the inManagedObjectContext parameter I'm passing is the correct one, but sure enough, it's nil in debugger.
Part of my trouble is that I don't understand what cues the app to call the boilerplate core data -managedObjectContext, -managedObjectModel, and -persistentStoreCoordinators methods. The app always seemed to call the methods and instantiate these objects automatically whenever they were required.
The core data stack is basically the standard stuff from a core data app template, with the iCloud option added to persistentStoreCoordinator, and a branch for migrating existing data. Here's the code; trying to limit it to essential elements:
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Notification registrations
[self register_iCloudNotifications];
// Data from defaults
BOOL _firstLocalLaunch = ...
BOOL _update =
BOOL _iCLoudDataSeeded = ...
// FIRST APP LAUNCH LOCALLY; FIRST iCLOUD LAUNCH ANYWHERE
// This is the code that's calling initializeSeedData and getting the error
if (_firstLocalLaunch && !_iCloudDataSeeded) {
[self initializeSeedData];
... update defaults
} else if {
... code for update launch and normal launch
// Safety check for seed data in persistent store; if none, initialize.
NSFetchRequest *modelFetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *modelEntity = [NSEntityDescription entityForName:#"entity" inManagedObjectContext:self.managedObjectContext];
[modelFetchRequest setEntity:modelEntity];
NSArray *fetchResults = [_managedObjectContext executeFetchRequest:modelFetchRequest error:nil];
if (!fetchResults.count) {
NSLog(#"Safety catch: seed data missing");
[self initializeSeedData];
}
return YES;
}
- (void)initializeSeedData
{
SWSEntity *entity1 = [NSEntityDescription insertNewObjectForEntityForName:#"Entity" inManagedObjectContext:_managedObjectContext]; // THIS IS WHATS THROWING THE ERROR
entity.name = #"First seed data item";
[_managedObjectContext save:nil];
}
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"model" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
NSLog(#"exists; returning");
return _persistentStoreCoordinator;
}
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
// NO UPDATE FROM PREVIOUS
if (!_update) {
NSLog(#"Persistent store: adding");
[self add_iCloudPersistentStore];
// UPDATE FROM PREVIOUS VERSION
} else {
NSLog(#"Persistent store: migrating");
...code for migrating existing store to iCloud; not called with this issue
}
return _persistentStoreCoordinator;
}
- (void)add_iCloudPersistentStore
{
NSError *error = nil;
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:[self get_iCloudStoreURL]
options:[self iCloudPersistentStoreOptions]
error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
- (NSDictionary *)iCloudPersistentStoreOptions
{
return #{NSPersistentStoreUbiquitousContentNameKey: #"myAppStore",
NSMigratePersistentStoresAutomaticallyOption : #YES,
NSInferMappingModelAutomaticallyOption : #YES};
}
Interesting thing: If I comment out the [self initializeSeedData] after detecting a first launch, the "safety catch" for adding the data executes, and creates the seed data just fine! The only thing I can figure that's different is that I'm executing a fetchRequest on the managed object context first... somehow the _managedObjectContext is instantiated when the fetch is executed, and still around for [self initializeSedData]. Why would that happen?
In my application, while executing fetch request application get freeze randomly. I have tried with multiple Choice like #synchronized and performblock still hang occur. below is my first fetch request block. Application hang in this fetch request randomly.
+(BXXXX *)getDetailsById:(NSNumber *)Id
{
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:#"BXXXX" inManagedObjectContext:[SDataManager managedObjectContext]];
[fetch setEntity:entityDescription];
[fetch setPredicate:[NSPredicate predicateWithFormat:
#"(BId = %#)",Id]];
__block NSArray *bDetails;
[[SDataManager managedObjectContext] performBlockAndWait:^{
NSError *error = nil;
bDetails = [[SDataManager managedObjectContext] executeFetchRequest:fetch error:&error];
}];
if([bDetails count] == 1)
return [bDetails objectAtIndex:0];
else
return nil;
}
//MY Managed object context declaration
+(NSManagedObjectContext *)managedObjectContext
{
static NSManagedObjectContext *managedObjectContext;
if(managedObjectContext!=nil){
return managedObjectContext;
}
#try {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
}
#catch (NSException *exception) {
NSLog(#"Exception occur %#",exception);
}
return managedObjectContext;
}
Please guide me to fix this issue. I tried hard but i cant fix this issue still now.
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
Your managed object context is going to do all work on the main queue. This will block the main event loop and cause your app to appear to hang.
Move the work off the main queue. See the Core Data Concurrency Guide.
I have fixed this issue by creating two managed object context like this.
// Base
+(NSManagedObjectContext *)managedObjectContext
{
static NSManagedObjectContext *managedObjectContext;
if(managedObjectContext!=nil){
return managedObjectContext;
}
#try {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
}
#catch (NSException *exception) {
NSLog(#"Exception occur %#",exception);
}
return managedObjectContext;
}
//Child
+(NSManagedObjectContext *)childManagedObjectContext
{
static NSManagedObjectContext *managedObjectContext;
if(managedObjectContext!=nil){
return managedObjectContext;
}
#try {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
}
#catch (NSException *exception) {
NSLog(#"Exception occur %#",exception);
}
return managedObjectContext;
}
for executing the fetch request i have used child managed object context with Concurrency Type as NSPrivateQueueConcurrencyType. It working fine for me. Now UI hang is not there.