Reputation: 12499
I get some data from a couple of web services that are called asynchronously. When I receive their responses, I need to create and save corresponding entities in Core Data with the information received. Since the services callbacks ara asynchronous, and I could be already saving the response of one of the services when I receive the another, I wrote a couple of methods like this:
- (void)createEntity
{
@autoreleasepool {
dispatch_queue_t queue = dispatch_queue_create(kSaveQueue, NULL);
dispatch_async(queue, ^{
// Context for background operations
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] init];
NSPersistentStoreCoordinator *mainThreadContextPSC = [self.context persistentStoreCoordinator];
[tmpContext setPersistentStoreCoordinator:mainThreadContextPSC];
@try {
// Parse service response and create entity
// Save context
[tmpContext save:nil];
dispatch_async(dispatch_get_main_queue(), ^{
// Notify end of operation
});
}
@catch (NSException *ex) {
NSLog(@"exception: %@", [ex description]);
}
});
}
}
Actually, I have two methods like this, one for let's say EntityA, and another for EntityB, and each one is called when I receive the corresponding service response (serviceA, serviceB). In my tests I see that both tmpContext
are always saved in iOS 8, but in iOS 7 it is only the first called which is saved, and the second entity is not persisted in Core Data
.
Why does this work in iOS 8 but it doesn't in iOS 7?
Thanks in advance
Upvotes: 0
Views: 272
Reputation: 1706
Above answer from Mundi is right and good explanations.. I can give you the code I use to create a thread context and save and stop context
+ (NSManagedObjectContext*)startThreadContext {
AppDelegate *theDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = theDelegate.managedObjectContext;
NSThread *thread = [NSThread currentThread];
if ([thread isMainThread]) {
return moc;
}
// get thread dictionary
NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
if ( [threadDictionary objectForKey:@"managedObjectContext"] == nil ) {
// create a context for this thread
NSManagedObjectContext *newMoc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[newMoc setPersistentStoreCoordinator:[theDelegate persistentStoreCoordinator]];
// Register for context save changes notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:newMoc];
[newMoc setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
[newMoc processPendingChanges]; // flush operations for which you want undos
[[newMoc undoManager] disableUndoRegistration];
newMoc.undoManager = nil;
// cache the context for this thread
[threadDictionary setObject:newMoc forKey:@"managedObjectContext"];
}
return [threadDictionary objectForKey:@"managedObjectContext"];
}
+ (void)saveAndStopThreadContext:(NSManagedObjectContext *)context {
// save managed object
NSError* error = nil;
BOOL success = [context save:&error];
if ( !success ) {
ERRLOG(@"[stopThreadContext] failed to save managedObjectContext (err:%@)", error );
}
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:context];
NSThread *thread = [NSThread currentThread];
if (![thread isMainThread]) {
NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
[threadDictionary removeObjectForKey:@"managedObjectContext"];
}
}
And you can use it like this
// get managed object context
NSManagedObjectContext* moc = [CoreDataHelper startThreadContext];
// perform update
[moc performBlock:^{
/*
Do something...
*/
// save and stop thread context
[CoreDataHelper saveAndStopThreadContext:moc];
}];
Upvotes: 0
Reputation: 80265
Your approach to create context with alloc init
and then assign the persistent store coordinator is deprecated.
Instead, use the factory method initWithConcurrencyType:
and pass NSPrivateQueueConcurrencyType
for a background thread. Associate with the parent context by calling setParentContext:
.
You can also do background operations by taking advantage of the context's performBlock
and performBlockAndWait
APIs rather than dropping down to GCD.
Upvotes: 1