Core data saving on child context not working - ios

For concurrency in coredata I am using parent-child MOC concept where parent context is of NSMainQueueConcurrencyType and child context is of NSPrivateQueueConcurrencyType and below is the code to
- (NSManagedObjectContext *)managedObjectContext
{
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];// DONE to avoid crash when the app auto logs out
managedObjectContext.persistentStoreCoordinator = coordinator;
}
return managedObjectContext;
}
-(NSManagedObjectContext *)getPrivateMOC
{
NSManagedObjectContext *private = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[private setParentContext:[self managedObjectContext]];
return private;
}
-(void)storeCustomerData:(NSArray *)custData
{
NSManagedObjectContext *currentbgContext = [self getPrivateMOC];
[currentbgContext performBlock:^{
for (NSDictionary *jsonObject in custData) {
CustomerEntity *custEntity = [NSEntityDescription insertNewObjectForEntityForName:#"CustomerEntity" inManagedObjectContext:currentbgContext];
custEntity.fname = [jsonObject field:#"fname"];
custEntity.lname = [jsonObject field:#"lname"];
}
NSError *error = nil;
if (![currentbgContext save:&error]) {
NSLog(#"Error saving context: %#\n%#", [error localizedDescription], [error userInfo]);
//abort();
}
}];
}
If I use parent context in method storeCustomerData it works but using child context getPrivateMOC() doesn't.
Please let me know if I am missing anything

I guess I understood your problem, Please try the below code
// Used to propegate saves to the persistent store (disk) without blocking the UI
- (NSManagedObjectContext *)masterManagedObjectContext {
if (_masterManagedObjectContext != nil) {
return _masterManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_masterManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
_masterManagedObjectContext.retainsRegisteredObjects = YES; //TO-DO : Need update this later
[_masterManagedObjectContext performBlockAndWait:^{
[_masterManagedObjectContext setPersistentStoreCoordinator:coordinator];
// [_masterManagedObjectContext setUndoManager:nil];
}];
}
return _masterManagedObjectContext;
}
// Return the NSManagedObjectContext to be used in the background during sync
- (NSManagedObjectContext *)backgroundManagedObjectContext {
if (_backgroundManagedObjectContext != nil) {
return _backgroundManagedObjectContext;
}
NSManagedObjectContext *masterContext = [self masterManagedObjectContext];
if (masterContext != nil) {
_backgroundManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_backgroundManagedObjectContext performBlockAndWait:^{
[_backgroundManagedObjectContext setParentContext:masterContext];
}];
}
return _backgroundManagedObjectContext;
}
// Return the NSManagedObjectContext to be used in the background during sync
- (NSManagedObjectContext *)newManagedObjectContext {
NSManagedObjectContext *newContext = nil;
NSManagedObjectContext *masterContext = [self masterManagedObjectContext];
if (masterContext != nil) {
newContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[newContext performBlockAndWait:^{
[newContext setParentContext:masterContext];
}];
}
return newContext;
}
- (void)saveMasterContext {
[self.masterManagedObjectContext performBlockAndWait:^{
NSError *error = nil;
BOOL saved = [self.masterManagedObjectContext save:&error];
if (!saved) {
// do some real error handling
//NSLog(#"Could not save master context due to %#", error);
}
}];
}
- (void)saveBackgroundContext {
[self.backgroundManagedObjectContext performBlockAndWait:^{
NSError *error = nil;
BOOL saved = [self.backgroundManagedObjectContext save:&error];
if (!saved) {
// do some real error handling
//NSLog(#"Could not save background context due to %#", error);
}
}];
}
static dispatch_queue_t coredata_background_save_queue;
dispatch_queue_t background_save_queue()
{
if (coredata_background_save_queue == NULL)
{
coredata_background_save_queue = dispatch_queue_create("com.magicalpanda.coredata.backgroundsaves", 0);
}
return coredata_background_save_queue;
}
- (void)saveDataInBackgroundWithContext:(void(^)(NSManagedObjectContext *context))saveBlock
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self saveDataInContext:saveBlock];
});
}
- (void)saveDataInContext:(void(^)(NSManagedObjectContext *context))saveBlock
{
NSManagedObjectContext *context = [[EFCoreDataController sharedInstance] backgroundManagedObjectContext];
[context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
[[self objectContext] setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
// [[self objectContext] observeContext:context]; //step 3
// block(context); //step 4
if ([context hasChanges]) //step 5
{
NSError *e;
[[self objectContext] save:&e]; //MagicalRecord will dump errors to the console with this save method
}
}

Related

Fatal Exception: NSInternalInconsistencyException Context already has a coordinator; cannot replace

In CoreData, while working with background and main context I am facing issue
Context already has a coordinator; cannot replace."
I am doing reading operation on the main context and writing operation on background context also for background context I am using background Queue. can you suggest what here i am doing wrong
========================================================================
Fatal Exception: NSInternalInconsistencyException
Context already has a coordinator; cannot replace.
#interface CoreDataCustomClass()
#property (nonatomic, strong) NSManagedObjectModel *managedObjectModel;
#property (nonatomic, strong) NSManagedObjectContext *mainContext;
#property (nonatomic, strong) NSManagedObjectContext *backgroundContext;
#property (nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#end
static CoreDataCustomClass *sharedInstance = nil;
#implementation CoreDataCustomClass
+(CoreDataCustomClass *)sharedInstance
{
#synchronized([CoreDataCustomClass class])
{
if (!sharedInstance)
{
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}
return nil;
}
#pragma mark - Core Data stack
- (NSManagedObjectContext *)mainContext {
if (_mainContext.persistentStoreCoordinator) {
return _mainContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
__weak __typeof(self)weakSelf = self;
_mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_mainContext performBlockAndWait:^{
__strong __typeof(weakSelf)strongSelf = weakSelf;
[strongSelf->_mainContext setPersistentStoreCoordinator:coordinator];
}];
}
return _mainContext;
}
-(void)saveMainContext {
NSManagedObjectContext *mainManagedObjectContext = self.mainContext;
[mainManagedObjectContext performBlock:^{
NSError *error = nil;
if ([mainManagedObjectContext hasChanges] && ![mainManagedObjectContext save:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
[mainManagedObjectContext performBlock:^{
NSError *error = nil;
if ([mainManagedObjectContext hasChanges] && ![mainManagedObjectContext save:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}];
}];
}
-(NSManagedObjectContext *)backgroundContext{
if (_backgroundContext.persistentStoreCoordinator){
return _backgroundContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if(coordinator != nil){
__weak __typeof(self)weakSelf = self;
_backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_backgroundContext performBlockAndWait:^{
__strong __typeof(weakSelf)strongSelf = weakSelf;
[strongSelf->_backgroundContext setPersistentStoreCoordinator:coordinator];
[strongSelf->_backgroundContext setUndoManager:nil];
}];
}
return _backgroundContext;
}
-(void)saveBackgroundContext {
NSManagedObjectContext *backgrounManagedObjectContext = self.backgroundContext;
[backgrounManagedObjectContext performBlock:^{
NSError *error = nil;
if ([backgrounManagedObjectContext hasChanges] && ![backgrounManagedObjectContext save:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
[backgrounManagedObjectContext performBlock:^{
NSError *error = nil;
if ([backgrounManagedObjectContext hasChanges] && ![backgrounManagedObjectContext save:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}];
}];
}
- (NSManagedObjectModel *)managedObjectModel {
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"abc" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSDictionary *persistentOptions = #{NSMigratePersistentStoresAutomaticallyOption:#YES, NSInferMappingModelAutomaticallyOption:#YES};
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"abc.sqlite"];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
{
[[NSFileManager defaultManager] removeItemAtURL: storeURL error: nil];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration: nil URL: storeURL options: nil error: &error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
NSDictionary *fileAttributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
if (![[NSFileManager defaultManager] setAttributes:fileAttributes ofItemAtPath:[storeURL path] error:&error]) {
NSLog(#"error");
}
return _persistentStoreCoordinator;
}

Coredata and Cordova in ios 10 and 11

The belowe code works perfectly on native environment but if I use it in a Mobile first generated app it crashes. Moreover it only happens when I compile the project with iOS 10 SDK, up to iOS 9 SDK it works fine.
Message sent to deallocated instance Core Data iOS 10 Mobile First 7.1
I'm getting the following error every time I try to execute a request through NSManagedObjectContext.
*** -[_PFArray count]: message sent to deallocated instance 0x1c524f060
Here's the code:
NSError *error = nil;
NSArray *matches = [self processFetchRequest:fetchRequest error:&error];
if( matches.count > 0 ){
toReturn = (User*)[matches objectAtIndex:0];
}
where:
- (nullable NSArray*) processFetchRequest:(NSFetchRequest*)fetchRequest error:(NSError**)error{
__block NSArray *matches = nil;
#synchronized (self) {
NSManagedObjectContext *context = [self managedObjectContext];
[context performBlockAndWait:^{
matches = [context executeFetchRequest:fetchRequest error:error];
}];
}
return matches;
}
- (NSManagedObjectContext *)managedObjectContext
{
NSManagedObjectContext *context = _managedObjectContext;
if ( context == nil ) {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
}
return _managedObjectContext;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *storeURL = [self getStoreURL];
NSError *errorMetadata = nil;
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *storeOptions = #{NSPersistentStoreFileProtectionKey : NSFileProtectionCompleteUntilFirstUserAuthentication};
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:storeOptions
error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}

nil is not a legal NSPersistentStoreCoordinator for searching for entity name 'GroupMessageInfo'

error message:
persistentStoreCoordinator is nil ??
persistentStoreCoordinator is nil ??
persistentStoreCoordinator is nil ??
persistentStoreCoordinator is nil ??
persistentStoreCoordinator is nil ??
persistentStoreCoordinator is nil ??
persistentStoreCoordinator is nil ??
persistentStoreCoordinator is nil ??
persistentStoreCoordinator is nil ??
------------ persistentStoreCoordinator is nil ??
- (void)updateGroupTableStatus:(TmpGroupMessageInfo*)info {
NSManagedObjectContext *ctx = [self createPrivateObjectContext];
[ctx performBlock:^{
NSPredicate *cdt = [NSPredicate predicateWithFormat:#"groupId == %# && tableId == %#", info.groupId, info.tableId];
NSFetchRequest *fetchRequest = [GroupMessageInfo makeRequest:ctx predicate:cdt orderby:nil offset:0 limit:1];
NSArray *array = [ctx executeFetchRequest:fetchRequest error:nil];
if ([array count] > 0) {
for (GroupMessageInfo *msg in array) {
msg.lastingTime = [NSNumber numberWithLongLong:0];
}
[ctx save:nil];
[self.managedObjectContext performBlock:^{
[self.managedObjectContext save:nil];
}];
}
}];
}
+ (NSFetchRequest*)makeRequest:(NSManagedObjectContext*)ctx predicate:(NSPredicate*)predicate orderby:(NSArray*)orders offset:(int)offset limit:(int)limit {
NSString *className = [NSString stringWithUTF8String:object_getClassName(self)];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:className inManagedObjectContext:ctx]];
if (predicate) {
[fetchRequest setPredicate:predicate];
}
if (orders!=nil) {
[fetchRequest setSortDescriptors:orders];
}
if (offset>0) {
[fetchRequest setFetchOffset:offset];
}
if (limit>0) {
[fetchRequest setFetchLimit:limit];
}
return fetchRequest;}
- (NSManagedObjectContext*)createPrivateObjectContext {
if (_privateManagedObjectContext == nil) {
_privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_privateManagedObjectContext setParentContext:self.managedObjectContext];
}
return _privateManagedObjectContext;
}
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel {
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"PokerSkyModel" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
......
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:optionsDictionary error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
[self cleanData];
abort();
}
return _persistentStoreCoordinator;
}
I came a cross exactly the same crash with yours. Finally I found out that initialize persistentStoreCoordinator of NSManagedObjectContext cannot be placed in background thread!
In your case, you do some CoreData operation at No.11 thread which is obviously not the Main Thread.If it is the first time you access your CoreData, then the persistentStoreCoordinator will be set at your No.11 background thread which lead to the crash. You can also reproduce the crash by putting these code appDidFinishLaunch at AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// your CoreData operation here
}
// ...
}
The solution is quite easy, just ensure the initialization of CoreData(or the first time you use it) should be at Main Thread.When it's not, using GCD to switch to it
dispatch_async(dispatch_get_main_queue(), 0), ^{
// your CoreData initialize here
}

Background thread method not solving freezing issue

I have my app freezing issue, So i used Instruments to find the issue and found that issues are related to CoreData save and fetch. I have tried background coredata method (parent-child, Notification) but my issue has been not resolved. Also I referred http://martiancraft.com/blog/2015/03/core-data-stack/ but dont know how this method implemeted in my app.
instrument log:
https://www.dropbox.com/s/agjtw1wqubsgwew/Instruments9.trace.zip?dl=0
SAVE to DB
-(void)updateThreadEntityWithSyncDetails:(NSMutableDictionary *)inDictionary
{
NSString *loginUser=[[NSUserDefaults standardUserDefaults] valueForKey:#"currentUser"];
AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [sharedDelegate managedObjectContext];
// NSManagedObjectContext *writerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
// [writerContext setPersistentStoreCoordinator:[sharedDelegate persistentStoreCoordinator]];
// create main thread MOC
// context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
// context.parentContext = writerContext;
// NSManagedObjectContext *contextforThread = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
// contextforThread.parentContext = context;
// [contextforThread performBlock:^{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ThreadInfo"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *userPredicate = [NSPredicate predicateWithFormat:#"userEmail == %#",loginUser];
NSPredicate *threadPredicate = [NSPredicate predicateWithFormat:#"threadID == %#",[inDictionary valueForKey:#"thread"]];
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: #[userPredicate, threadPredicate]];
[fetchRequest setPredicate:compoundPredicate];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:nil];
for (ThreadInfo *threadInfo in fetchedObjects)
{
if([[inDictionary allKeys] containsObject:#"userEmail"])
{
if([inDictionary valueForKey:#"userEmail"]!=[NSNull null])
{
threadInfo.userEmail=[inDictionary valueForKey:#"userEmail"];
}
}
if([[inDictionary allKeys] containsObject:#"badgeValue"])
{
if([inDictionary valueForKey:#"badgeValue"]!=[NSNull null])
{
threadInfo.badgeValue=[inDictionary valueForKey:#"badgeValue"];
}
}
if([[inDictionary allKeys] containsObject:#"choice4Percentage"])
{
if([inDictionary valueForKey:#"choice4Percentage"]!=[NSNull null])
{
threadInfo.choice4Percentage=[inDictionary valueForKey:#"choice4Percentage"];
}
}
if([[inDictionary allKeys] containsObject:#"choice5Percentage"])
{
if([inDictionary valueForKey:#"choice5Percentage"]!=[NSNull null])
{
threadInfo.choice5Percentage=[inDictionary valueForKey:#"choice5Percentage"];
}
}
}
NSError *error;
if(![context save:&error]) {
NSLog(#"Child error : %#",error);
}
// [context performBlock:^{
// NSError *error;
// if(![context save:&error]) {
// NSLog(#"%#",error);
// }
// }];
// }];
}
FETCH
-(ThreadInfo *)retrieveSolicitationInfoForThreadID:(NSString*)inThreadID;
{
NSString *loginUser=[[NSUserDefaults standardUserDefaults] valueForKey:#"currentUser"];
AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [sharedDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ThreadInfo"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *userPredicate = [NSPredicate predicateWithFormat:#"userEmail == %#",loginUser];
NSPredicate *threadPredicate = [NSPredicate predicateWithFormat:#"threadID == %#",inThreadID];
\
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: #[userPredicate, threadPredicate]];
[fetchRequest setPredicate:compoundPredicate];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:nil];
if(fetchedObjects.count!=0)
{
ThreadInfo *threadInfo=[fetchedObjects objectAtIndex:0];
return threadInfo;
}
return nil;
}
SYNC
-(void)updateSolicitationWithSyncDetails:(NSDictionary *)inDictionary
{
NSMutableDictionary *paramDict=[NSMutableDictionary dictionaryWithDictionary:inDictionary];
NSString *userEmail=[[NSUserDefaults standardUserDefaults] valueForKey:#"currentUser"];
[paramDict setObject:[NSNumber numberWithBool:NO] forKey:#"isSystemMessage"];
[paramDict setObject:userEmail forKey:#"userEmail"];
if([[inDictionary allKeys] containsObject:#"owned"])
{
BOOL isDuplicate=[[IXDataBaseManager sharedNetworkDataManager] checkForExistenceOfThreadDetailsForSolicitationID:[inDictionary objectForKey:#"solicitation"]];// FETCH
if(!isDuplicate)
{
int randomIndex=[[IXNetworkDataManager sharedNetworkDataManager] getIndexForColorImageForTab:#"OUT"];
[paramDict setObject:message forKey:#"threadDescription"];
[paramDict setObject:[NSNumber numberWithInt:randomIndex] forKey:#"colorCode"];
BOOL isDuplicateVal=[[IXDataBaseManager sharedNetworkDataManager] checkForExistenceOfSolicitationID:[inDictionary objectForKey:#"solicitation"]];// FETCH
[paramDict setObject:message forKey:#"threadDescription"];
ThreadInfo *threadInfo=[[IXDataBaseManager sharedNetworkDataManager] retrieveSolicitationInfoForThreadID:[inDictionary objectForKey:#"solicitation"]];
[paramDict setObject:threadInfo.threadID forKey:#"thread"];
[[IXDataBaseManager sharedNetworkDataManager] updateThreadEntityWithSyncDetails:paramDict];
}
}
}
First, thank you for making the code snippets and the trace available right away. That is very helpful.
So, looking at the trace and the code.
-updateThreadEntityWithSyncDetails: is being called on the main thread and is 33% of the time spent there. Not good.
You are passing nil for error: NEVER do that. Always pass an error pointer and check the results of your call to see if there is an error.
This conditional can be a lot cleaner:
if([[inDictionary allKeys] containsObject:#"userEmail"])
{
if([inDictionary valueForKey:#"userEmail"]!=[NSNull null])
{
threadInfo.userEmail=[inDictionary valueForKey:#"userEmail"];
}
}
Consider:
if (inDictionary[#"userEmail"] != nil && inDictionary[#"userEmail"] != [NSNull null]) {
threadInfo.userEmail = inDictionary[#"userEmail"];
}
Much easier to read.
This re-write of the method will get the work off the main thread:
- (void)updateThreadEntityWithSyncDetails:(NSMutableDictionary*)inDictionary
{
NSString *loginUser = [[NSUserDefaults standardUserDefaults] valueForKey:#"currentUser"];
AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[context setParentContext:[sharedDelegate managedObjectContext]];
[context performBlock:^{
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"ThreadInfo"];
[fetchRequest setReturnsObjectsAsFaults:NO];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"userEmail == %# && threadID == %#",loginUser, inDictionary[#"thread"]];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:nil];
for (ThreadInfo *threadInfo in fetchedObjects) {
if (inDictionary[#"userEmail"] != nil && inDictionary[#"userEmail"] != [NSNull null]) {
threadInfo.userEmail = inDictionary[#"userEmail"];
}
if (inDictionary[#"badgeValue"] != nil && inDictionary[#"badgeValue"] != [NSNull null]) {
threadInfo.badgeValue = inDictionary[#"badgeValue"];
}
if (inDictionary[#"choice4Percentage"] != nil && inDictionary[#"choice4Percentage"] != [NSNull null]) {
threadInfo.choice4Percentage = inDictionary[#"choice4Percentage"];
}
if (inDictionary[#"choice5Percentage"] != nil && inDictionary[#"choice5Percentage"] != [NSNull null]) {
threadInfo.choice5Percentage = inDictionary[#"choice5Percentage"];
}
}
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Child error : %#",error);
}
}];
}
BUT it may still block the main thread as I am guessing the bulk of the CPU time is in executing this fetch. The fetch is slow. You have TWO string compares in that fetch. That is bad and should be corrected. If the ThreadID is not a string then reverse the fetch. Otherwise this is just poor data model design and there isn't going to be much help other than fixing that fetch.
Your other really slow point is in -checkForExistenceOfThreadDetailsForThreadID:. You did not post that method but I suspect it is the same issue, your fetch is costing you a tremendous amount of time AND it is on the main queue.
Overall the design of this is poor and needs to be reworked. You are comparing strings in the data store which is one of the slowest ways to retrieve data. You are also on the main thread for things that do not appear to NEED to be on the main thread.
Remember the golden rule, if the data is NOT being manipulated BY the user then the manipulation MUST NOT be on the main queue. No exceptions.
Have you tried to use an separate Context for the operation and merge it with the mainContext after the the save?
E.g.
NSManagedObjectContext * localContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[localContext setPersistentStoreCoordinator:[[self managedObjectContext] persistentStoreCoordinator]];
after your import is done you save your localContext
NSError * error = nil;
if (![[self localContext] save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
This will write your data to the PersistentStore.
Now your mainContext needs to be informed about the changes in the PersistentStore. This is done by observing for a notification.
e.g.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didSaveNotification:)
name:NSManagedObjectContextDidSaveNotification
object:nil];
and this is the method which reacts to the notification:
- (void)didSaveNotification:(NSNotification *)notification
{
// NSLog(#"%s %#\n",__PRETTY_FUNCTION__,[notification userInfo]);
NSManagedObjectContext * mainContext = [self managedObjectContext];
void (^mergeChanges)(void)=^{
[mainContext mergeChangesFromContextDidSaveNotification:notification];
};
if([NSThread isMainThread]) {
mergeChanges();
} else {
dispatch_sync(dispatch_get_main_queue(), mergeChanges);
}
}
Using different NSManagedContext to work on the same PersistentStore is more threadsafe than working with a single NSManagedContext.
//Your Parent context init
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
[_managedObjectContext setUndoManager:nil];
}
return _managedObjectContext;
}
- (NSManagedObjectContext *)childManagedObjectContext
{
if (_childManagedObjectContext != nil) {
return _childManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_childManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_childManagedObjectContext setPersistentStoreCoordinator:coordinator];
[_childManagedObjectContext setUndoManager:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(_mocDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:nil];
}
return _childManagedObjectContext;
}
- (void)_mocDidSaveNotification:(NSNotification *)notification
{
NSManagedObjectContext *savedContext = [notification object];
// ignore change notifications for the main MOC
if (_managedObjectContext == savedContext)
{
return;
}
if (_managedObjectContext.persistentStoreCoordinator != savedContext.persistentStoreCoordinator)
{
// that's another database
return;
}
dispatch_sync(dispatch_get_main_queue(), ^{
[_managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
});
}
//Way to use the Parent- child context
[UIAppDelegate.childManagedObjectContext performBlock:^{
for (int i=0; i<count; i++) {
//Fill Database and save it in child context first for each entry and save it in child context
[UIAppDelegate.childManagedObjectContext save:nil];
}
dispatch_sync(dispatch_get_main_queue(), ^{
//Finally when u have inserted all entries save in the parent context
[UIAppDelegate.managedObjectContext save:nil];
});
}]
//Your code is huge to understand the logic but if you haven't tried in this way, just give it shot

Ios core data not saved

I'm using core data and i have a problem. When i save the data and the app is still running i can see and get the data that was saved.
Once the application is close, all the fields deleted and just the object is saved. I can see that in the saveContext method.
On first launch when the app is closing the saveContext method is activated. I can see that the managedObjectContext object is inserting new object.
The next times that the app is opening , the managedObjectContext is updating the object so i know it save the objects but when i try to retrive the object it can find .
here is how i insert objects:
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
self.managedObjectContext = appDelegate.managedObjectContext;
#try {
UserData *userData =[NSEntityDescription insertNewObjectForEntityForName:[UserTable tableName] inManagedObjectContext:self.managedObjectContext];
//Insert Values
userData.facebookId=user.id;
userData.name=user.name;
userData.picoAccessToken=[PicoApiManager sharedInstance].accessToken;
userData.picoExpire=[PicoApiManager sharedInstance].expires;
userData.latitude=[NSNumber numberWithDouble:user.latitude];
userData.longitude=[NSNumber numberWithDouble:user.longitude];
userData.pushId=user.pushId;
userData.isActive=[NSNumber numberWithBool:activeStatus];
}
#catch (NSException *exception) {
NSLog(#"Insert exception - %#", exception.description);
}
or
-(void)addPictures:(NSMutableArray *)Pictures;
{
//Setting the isNew field to NO to all the pictures already in the db
[self updateIsNewOfPicture];
for (Picture *picture in Pictures) {
//Checks if inserted picture is already inside the table
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
self.managedObjectContext = appDelegate.managedObjectContext;
#try {
PictureData *pictureData=[NSEntityDescription insertNewObjectForEntityForName:[PicturesTable tableName]inManagedObjectContext:self.managedObjectContext];
//Insert Values
pictureData.url=picture.source;
pictureData.isNew=[NSNumber numberWithBool:YES];
pictureData.isPick=[NSNumber numberWithBool:NO];
pictureData.timeTaken=picture.time_taken;
pictureData.albumData=[self getActiveAlbum];
}
#catch (NSException *exception) {
NSLog(#"Insert exception - %#", exception.description);
}
}
This is app delegate functions:
- (void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![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.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
}
#pragma mark - Core Data stack
// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"Pico-Db" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"Pico.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;
}
// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
This is how i'm trying to get the data :
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
//2
// initializing NSFetchRequest
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
//Setting Entity to be Queried
NSEntityDescription *entity = [NSEntityDescription entityForName:[UserTable tableName] inManagedObjectContext:appDelegate.managedObjectContext];
[fetchRequest setEntity:entity];
NSError* error;
// Query on managedObjectContext With Generated fetchRequest
NSArray *fetchedRecords = [appDelegate.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchedRecords.count >0) {
return YES;
}
return NO;
You must call -[AppDelegate saveContext] after inserting your data in order for CoreData to persist changes to disk. The NSManagedObjectContext will store changes in memory so while your application is still active, you will have access to the data. As soon as the app terminates, however, unless you call -[AppDelegate saveContext] those changes will not be persisted.
Try this in the first example:
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
self.managedObjectContext = appDelegate.managedObjectContext;
#try {
UserData *userData = [NSEntityDescription insertNewObjectForEntityForName:[UserTable tableName] inManagedObjectContext:self.managedObjectContext];
//Insert Values
userData.facebookId=user.id;
userData.name=user.name;
userData.picoAccessToken=[PicoApiManager sharedInstance].accessToken;
userData.picoExpire=[PicoApiManager sharedInstance].expires;
userData.latitude=[NSNumber numberWithDouble:user.latitude];
userData.longitude=[NSNumber numberWithDouble:user.longitude];
userData.pushId=user.pushId;
userData.isActive=[NSNumber numberWithBool:activeStatus];
} #catch (NSException *exception) {
NSLog(#"Insert exception - %#", exception.description);
}
// SAVE CONTEXT:
[appDelegate saveContext];
Try this in the second example:
for (Picture *picture in Pictures) {
// Checks if inserted picture is already inside the table
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
self.managedObjectContext = appDelegate.managedObjectContext;
#try {
PictureData *pictureData = [NSEntityDescription insertNewObjectForEntityForName:[PicturesTable tableName]inManagedObjectContext:self.managedObjectContext];
//Insert Values
pictureData.url=picture.source;
pictureData.isNew=[NSNumber numberWithBool:YES];
pictureData.isPick=[NSNumber numberWithBool:NO];
pictureData.timeTaken=picture.time_taken;
pictureData.albumData=[self getActiveAlbum];
} #catch (NSException *exception) {
NSLog(#"Insert exception - %#", exception.description);
}
}
// SAVE CONTEXT:
[appDelegate saveContext];
To create a fetch request:
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"YourEntityName"];
request.sortDescriptors = [NSSortDescriptor sortDescriptorWithKey:#"attributeName" ascending:YES];
request.predicate = [NSPredicate predicateWithFormat:#"attribute == %#", 13];
NSError *error = nil; //
NSArray *results = [appDelegate.managedObjectContext executeFetchRequest:request error:&error];
if (!results) {
NSLog(#"Error performing fetch request: %#", error.localizedDescription);
}
return ([results count] > 0); // There are more efficient ways of getting count from CoreData

Resources