ios - sending data to NSOperation or should I use NSThread? - ios

I have core data objects that are created/updated via HTTP. I also want to create a background thread to continuously receive timestamp and state info from the app and update the core data objects. Should I use NSOperation or GCD for this? Since it's not just a simple task, NSOperation seems better since I can loop within it, but I can't figure out how to pass information into the operation, from the main thread, while it's running. Is there a simple way of doing this? I have seen many threads/articles about sending messages to the main thread from the operation, but nothing about passing messages to it.
Does using NSOperation/GCD seem like a good solution?

Step back and relook at your architecture. You should be using a managed object context that itself uses a private dispatch queue (option NSPrivateQueueConcurrencyType).
You would then use NSURLConnections to get the data you want, and when you get the data in the delegate method you can asynchronously update the repository using performBlock.
Conversely you may want to retrieve data using performBlockAndWait, using block variables or mutable pre-defined objects to receive the results from the block.

I was looking at this all wrong (essentially wanting to subclass NSThread and pass data to/from it). I didn't think about using blocks appropriately.
Essentially, what I'm doing is:
loadStuff:(NSDictionary *)stuff {
// stuff is data from HTTP GET request
NSManagedObjectContext *context = // init context with NSPrivateQueueConcurrencyType
context.parentContext = // main context
[context performBlock:^{
// insert/update entities
// save context
}];
}
But then I need to loop in a background thread to update the entities:
//inside some method
dispatch_async(global_queue, ^{
// _currentTimestamp is updated by a delegate
NSInterval timestamp = _currentTimestamp;
while (run) {
if (timestamp != _currentTimestamp) {
// do calculation
// update entities if needed
// save context
}
}
});

Related

Know when Core Data has saved all entities using NSXMLParser

I am using NSXMLParser to read a large XML file. Once I get the data element that I want I create an NSManagedObject and save that on a background context. The imported file will have n number of imported items, it is never known.
NSManagedObjectContext *backgroundContext = [ZSSCoreDataManager sharedService].persistentContainer.newBackgroundContext;
[backgroundContext performBlock:^{
// Create and insert new entity here
[self createAndInsertWithDictionary:object];
// Save
if ([backgroundContext hasChanges]) {
NSError *error = nil;
if (![backgroundContext save:&error]) {
NSLog(#"%#", error);
}
// Save was successful, reset to release memory
[backgroundContext reset];
}
}];
Because parsing can happen faster than a save: can complete, how can I know when the XML file is done reading AND all my Core Data objects have completed saving? I know of the parserDidEndDocument: method, but this doesn't help to know when all of my object have completed saving to Core Data.
Ideas?
One obvious way would be to replace performBlock with performBlockAndWait, so that you know once the block has finished that the save has also finished.
If you don't want to wait, this sounds like a job for a dispatch group. That would allow scheduling a function to be called only after a bunch of asynchronous code had finished.
Create a dispatch group with dispatch_group_create
Every time you start one of these blocks, call dispatch_group_enter before doing anything else.
Every time a save completes, call dispatch_group_leave
Use dispatch_group_notify to schedule a block that will only be executed when every "enter" has been matched by a "leave".
Put code in the "notify" block that should run once every save has finished.

Core Data concurrency issue and how to fix

I use multiple contexts in my Core Data app, and have recently had some core data concurrency crashes. I have added -com.apple.CoreData.ConcurrencyDebug 1 to help track these down, but I am not understanding how to fix the issue that is shown.
Here is what I am doing:
- (void)getEvents:(void (^)(NSArray *fetchedItems))completionBlock {
// Initialize Fetch Request
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"ZSSCDEvent"];
// Initialize Asynchronous Fetch Request
NSAsynchronousFetchRequest *asynchronousFetchRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest:request completionBlock:^(NSAsynchronousFetchResult *result) {
dispatch_async(dispatch_get_main_queue(), ^{
// Dismiss Progress HUD
[SVProgressHUD dismiss];
// Process Asynchronous Fetch Result
if (result.finalResult) {
NSArray *results = result.finalResult;
completionBlock(results);
// Reload Table View
[self.activityIndicator stopAnimating];
[self.tableViewList reloadData];
}
});
}];
// Execute Asynchronous Fetch Request
[self.managedObjectContext performBlock:^{
// Execute Asynchronous Fetch Request
NSError *asynchronousFetchRequestError = nil;
NSAsynchronousFetchResult *asynchronousFetchResult = (NSAsynchronousFetchResult *)[self.managedObjectContext executeRequest:asynchronousFetchRequest error:&asynchronousFetchRequestError];
if (asynchronousFetchRequestError) {
NSLog(#"Unable to execute asynchronous fetch result.");
NSLog(#"%#, %#", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription);
}
}];
}
This gives me a Enqueued from com.apple.main-thread (Thread 1) error. This is where I am confused, since I am running this on the main thread and didn't think I needed to use my private context here.
Any ideas on why I am getting a concurrency issue here?
EDIT: It looks like someone else had the exact issue and thinks it is an Xcode bug: CoreData asynchronous fetch causes concurrency debugger error
Every managedObject has a context. Each context has one and only one thread that it can run on. ManagedObjects are NOT thread safe - not even for reading. Passing managedObjects around with completions blocks is a bad practice. It can be hard to figure out which managedObjects are supposed to be on which thread. Also, even if you are passing it around on the correct thread it is still a bad practice. When you do a dispatch_async the entity may have deleted from the database in the interim and accessing the managedObject will cause a crash. A good practice is that any method that does a fetch should be explicitly told which context to use and return synchronously. In your code the method is using self.managedObjectContext, but I have no way to know looking at you code what thread that is related to. Furthermore the pointer to the context may change and that can cause bugs that are very hard to track down.
NSAsynchronousFetchResult contains managedObjects, so is not thread safe and can only be used inside that completion block (which is running on the correct thread for the objects). You cannot pass them to another thread. If you return them in a block then that code must also not pass them to another thread. You should just do whatever you need to do with them inside the block and then discard them.
If you need to display the information to the user, then generally it is better to just do the fetch on the main thread synchronously and get managedObjects that are associated with a main thread context. If your fetch is taking to long then you should fix that - there is no reason for a fetch to take so long. In your case you appear to be fetching ALL the items in you database. That is a mistake. You should use a predicate to only get the ones that you need.
Another possibility is to copy the values of managedObjects into a thread safe object (a dictionary or a NSObject subclass).
TL;DR You probably don't need a NSAsynchronousFetchRequest. Use a regular fetch request on the main thread and return synchronously. Use a predicate to limit the results only to the objects you are actually displaying.

Setup that allows a child NSManagedObjectContext to Fetch when it's parent context is Saving

Background
Saving a large amount of data at a time is very slow.
Current Setup
In my app there's a private-queue NSManagedObjectContext as the parent that talks to the NSPersistentStoreCoordinator directly to save data. A child main-queue context is consumed by a NSTreeController for the UI(NSOutlineView).
(My goal was to prevent any occurence of the beach ball. Currently I remedy the problem by only saving data when the app goes inactive. But since the data that are planed to be deleted are not deleted yet, they may still come up in a fetch result. That's another problem I'm trying to solve.)
The Problem
The child main-queue context can only wait when fetching when the parent context is busy saving.
Related Problems
Core Data: parent context blocks child This perhaps is essentially the same question. I've noticed the answer says the setup is intrinsically wrong. Then I wonder is there a way to do the writing and reading at the same time with Core Data?
Correct implementation of parent/child NSManagedObjectContext A commonly asked question lacks answers with insights (sorry...).
What is the most efficient way to delete a large number (10.000+) objects in Core Data? If the objects can't be designated by a NSPredicate, we still need to rely on the traditional delete() (and maybe the Cascade delete rule) Also, it doesn't eliminate the blocking-the-UI issue.
I will update this question when I have more findings.
I'm guessing you're developing for OS X / macOS (NSTreeController & NSOutlineView). I've no experience with macOS - I develop for iOS - so you might need to take that into account when you're reading my response.
I've not yet made the switch to swift - my code is, perhaps obviously, Objective-C...
I'll start with how I prepare the Core Data stack.
I set up two public properties in the header file:
#property (nonatomic, strong) NSManagedObjectContext *mocPrivate;
#property (nonatomic, strong) NSManagedObjectContext *mocMain;
Although this is unnecessary, I also prefer to set up private properties for my Core Data objects, including, for example:
#property (nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator;
Once I've pointed to my model URL, established my managed object model NSManagedObjectModel, pointed to my store URL for my NSPersistentStore and established my persistent store coordinator NSPersistentStoreCoordinator (PSC), I set up my two managed object contexts (MOC).
Within the method to "build" my Core Data stack, after I've completed the code per the above paragraph, I then include the following...
if (!self.mocPrivate) {
self.mocPrivate = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[self.mocPrivate setPersistentStoreCoordinator:self.persistentStoreCoordinator];
} else {
// report to console the use of existing MOC
}
if (!self.mocMain) {
self.mocMain = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[self.mocMain setParentContext:self.mocPrivate];
} else {
// report to console the use of existing MOC
}
(I usually include a few NSLog lines in this code to report to my console but I've excluded that here to keep the code clean.)
Note two important aspects to this code...
set the private queue MOC to interact with the PSC; and
set the main queue MOC as the child of the private queue MOC.
Why is this done? First let's highlight a couple of important points:
Saves to memory are relatively fast; and
Saves to disc are relatively slow.
The private queue is asynchronous to the main queue. The User Interface (UI) operates on the main queue. The private queue operates on a separate thread "in the background" working to maintain context and coordinate data persistence with the PSC, perfectly managed by Core Data and iOS. The main queue operates on the main thread with the UI.
Written a different way...
Heavy work completing irregular (managed by Core Data) data persistence to the PSC (saves to disc) is completed in the private queue; and
Light work completing regular (managed by developer) data persistence to the MOC (saves to memory) is completed in the main queue.
In theory this should ensure your UI is never blocked.
But there is more to this solution. How we manage the "save" process is important...
I write a public method:
- (void)saveContextAndWait:(BOOL)wait;
I call this method from any class that needs to persist data. The code for this public method:
- (void)saveContextAndWait:(BOOL)wait {
// 1. First
if ([self.mocMain hasChanges]) {
// 2. Second
[self.mocMain performBlockAndWait:^{
NSError __autoreleasing *error;
BOOL success;
if (!(success = [self.mocMain save:&error])) {
// error handling
} else {
// report success to the console
}
}];
} else {
NSLog(#"%# - %# - CORE DATA - reports no changes to managedObjectContext MAIN_", NSStringFromClass(self.class), NSStringFromSelector(_cmd));
}
// 3. Third
void (^savePrivate) (void) = ^{
NSError __autoreleasing *error;
BOOL success;
if (!(success = [self.mocPrivate save:&error])) {
// error handling
} else {
// report success to the console
}
};
// 4. Fourth
if ([self.mocPrivate hasChanges]) {
// 5. Fifth
if (wait) {
[self.mocPrivate performBlockAndWait:savePrivate];
} else {
[self.mocPrivate performBlock:savePrivate];
}
} else {
NSLog(#"%# - %# - CORE DATA - reports no changes to managedObjectContext PRIVATE_", NSStringFromClass(self.class), NSStringFromSelector(_cmd));
}
}
So I'll work through this to explain what is happening.
I offer the developer the option to save and wait (block), and depending on the developer's use of the method saveContextAndWait:wait, the private queue MOC "saves" using either:
the performBlockAndWait method (developer calls method with wait = TRUE or YES); or
the performBlock method (developer calls method with wait = FALSE or NO).
First, the method checks whether there are any changes to the main queue MOC. Let's not do any work unless we have to!
Second, the method completes a (synchronous) call to performBlockAndWait on the main queue MOC. This performs the call to save method in a code block and waits for completion before allowing the code to continue. Remember this is for regular "saves" of small data sets. The (asynchronous) option to call performBlock is not required here and in fact will derail the effectiveness of the method, as I experienced when I was learning to implement this in my code (failure to persist data due to the save call on the main queue MOC attempting to complete after completion of the save on the private queue MOC).
Third, we write a little block within a block that contains the code to save the private queue MOC.
Fourth, the method checks whether there are any changes to the private queue MOC. This may be unnecessary but it is harmless to include here.
Fifth, depending on the option the developer chooses to implement (wait = YES or NO) the method calls either performBlockAndWait or performBlock on the block within a block (under third above).
In this last step, regardless of the implementation (wait = YES or NO), the function of persisting data to disc, from the private queue MOC to the PSC, is abstracted to the private queue on an asynchronous thread to the main thread. In theory the "save to disc" via the PSC can take as long as it likes because it has nothing to do with the main thread. AND because the private queue MOC has all the data in memory, the main queue MOC is fully and automatically informed of the changes because it is the child of the private queue MOC.
If you import large volumes of data into app, something I am currently working on implementing, then it makes sense to import this data into the private queue MOC.
The private queue MOC does two things here:
It coordinates data persistence (to disc) with the PSC;
Because it is the parent of the main queue MOC (in memory), the main queue MOC will be notified of the data changes in the private queue MOC and merges are managed by Core Data;
Finally, I use NSFetchedResultsController (FRC) to manage my data fetches, which are all completed against the main queue MOC. This maintains data hierarchy. As changes are made to the data sets in either context, the FRC updates the view.
This solution is simple! (Once I spent weeks wrangling my head around it and another few weeks refining my code.)
There is no requirement to monitor notifications for merges or other changes to MOC. Core Data and iOS handle everything in the background.
So if this doesn't work for you - let me know - I may have excluded or overlooked something as I wrote this code well over a year ago.

Core Data background context best practice

I have a large import task I need to do with core data.
Let say my core data model look like this:
Car
----
identifier
type
I fetch a list of car info JSON from my server and then I want to sync it with my core data Car object, meaning:
If its a new car -> create a new Core Data Car object from the new info.
If the car already exists -> update the Core Data Car object.
So I want to do this import in background without blocking the UI and while the use scrolls a cars table view that present all the cars.
Currently I'm doing something like this:
// create background context
NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[bgContext setParentContext:self.mainContext];
[bgContext performBlock:^{
NSArray *newCarsInfo = [self fetchNewCarInfoFromServer];
// import the new data to Core Data...
// I'm trying to do an efficient import here,
// with few fetches as I can, and in batches
for (... num of batches ...) {
// do batch import...
// save bg context in the end of each batch
[bgContext save:&error];
}
// when all import batches are over I call save on the main context
// save
NSError *error = nil;
[self.mainContext save:&error];
}];
But I'm not really sure I'm doing the right thing here, for example:
Is it ok that I use setParentContext ?
I saw some examples that use it like this, but I saw other examples that don't call setParentContext, instead they do something like this:
NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
bgContext.persistentStoreCoordinator = self.mainContext.persistentStoreCoordinator;
bgContext.undoManager = nil;
Another thing that I'm not sure is when to call save on the main context, In my example I just call save in the end of the import, but I saw examples that uses:
[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:nil usingBlock:^(NSNotification* note) {
NSManagedObjectContext *moc = self.managedObjectContext;
if (note.object != moc) {
[moc performBlock:^(){
[moc mergeChangesFromContextDidSaveNotification:note];
}];
}
}];
As I mention before, I want the user to be able to interact with the data while updating, so what if I the user change a car type while the import change the same car, is the way I wrote it safe?
UPDATE:
Thanks to #TheBasicMind great explanation I'm trying to implement option A, so my code looks something like:
This is the Core Data configuration in AppDelegate:
AppDelegate.m
#pragma mark - Core Data stack
- (void)saveContext {
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
DDLogError(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
// main
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
_managedObjectContext.parentContext = [self saveManagedObjectContext];
return _managedObjectContext;
}
// save context, parent of main context
- (NSManagedObjectContext *)saveManagedObjectContext {
if (_writerManagedObjectContext != nil) {
return _writerManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_writerManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_writerManagedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _writerManagedObjectContext;
}
And this is how my import method looks like now:
- (void)import {
NSManagedObjectContext *saveObjectContext = [AppDelegate saveManagedObjectContext];
// create background context
NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
bgContext.parentContext = saveObjectContext;
[bgContext performBlock:^{
NSArray *newCarsInfo = [self fetchNewCarInfoFromServer];
// import the new data to Core Data...
// I'm trying to do an efficient import here,
// with few fetches as I can, and in batches
for (... num of batches ...) {
// do batch import...
// save bg context in the end of each batch
[bgContext save:&error];
}
// no call here for main save...
// instead use NSManagedObjectContextDidSaveNotification to merge changes
}];
}
And I also have the following observer:
[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:nil usingBlock:^(NSNotification* note) {
NSManagedObjectContext *mainContext = self.managedObjectContext;
NSManagedObjectContext *otherMoc = note.object;
if (otherMoc.persistentStoreCoordinator == mainContext.persistentStoreCoordinator) {
if (otherMoc != mainContext) {
[mainContext performBlock:^(){
[mainContext mergeChangesFromContextDidSaveNotification:note];
}];
}
}
}];
This is an extremely confusing topic for people approaching Core Data for the first time. I don't say this lightly, but with experience, I am confident in saying the Apple documentation is somewhat misleading on this matter (it is in fact consistent if you read it very carefully, but they don't adequately illustrate why merging data remains in many instances a better solution than relying on parent/child contexts and simply saving from a child to the parent).
The documentation gives the strong impression parent/child contexts are the new preferred way to do background processing. However Apple neglect to highlight some strong caveats. Firstly, be aware that everything you fetch into your child context is first pulled through it's parent. Therefore it is best to limit any child of the main context running on the main thread to processing (editing) data that has already been presented in the UI on the main thread. If you use it for general synchronisation tasks it is likely you will be wanting to process data which extends far beyond the bounds of what you are currently displaying in the UI. Even if you use NSPrivateQueueConcurrencyType, for the child edit context, you will potentially be dragging a large amount of data through the main context and that can lead to bad performance and blocking. Now it is best not to make the main context a child of the context you use for synchronisation, because it won't be notified of synchronisation updates unless you are going to do that manually, plus you will be executing potentially long running tasks on a context you might need to be responsive to saves initiated as a cascade from the edit context that is a child of your main context, through the main contact and down to the data store. You will have to either manually merge the data and also possibly track what needs to be invalidated in the main context and re-sync. Not the easiest pattern.
What the Apple documentation does not make clear is that you are most likely to need a hybrid of the techniques described on the pages describing the "old" thread confinement way of doing things, and the new Parent-Child contexts way of doing things.
Your best bet is probably (and I'm giving a generic solution here, the best solution may be dependent on your detailed requirements), to have a NSPrivateQueueConcurrencyType save context as the topmost parent, which saves directly to the datastore. [Edit: you won't be doing very much directly on this context], then give that save context at least two direct children. One your NSMainQueueConcurrencyType main context you use for the UI [Edit: it's best to be disciplined and avoid ever doing any editing of the data on this context], the other a NSPrivateQueueConcurrencyType, you use to do user edits of the data and also (in option A in the attached diagram) your synchronisation tasks.
Then you make the main context the target of the NSManagedObjectContextDidSave notification generated by the sync context, and send the notifications .userInfo dictionary to the main context's mergeChangesFromContextDidSaveNotification:.
The next question to consider is where you put the user edit context (the context where edits made by the user get reflected back into the interface). If the user's actions are always confined to edits on small amounts of presented data, then making this a child of the main context again using the NSPrivateQueueConcurrencyType is your best bet and easiest to manage (save will then save edits directly into the main context and if you have an NSFetchedResultsController, the appropriate delegate method will be called automatically so your UI can process the updates controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:) (again this is option A).
If on the other hand user actions might result in large amounts of data being processed, you might want to consider making it another peer of the main context and the sync context, such that the save context has three direct children. main, sync (private queue type) and edit (private queue type). I've shown this arrangement as option B on the diagram.
Similarly to the sync context you will need to [Edit: configure the main context to receive notifications] when data is saved (or if you need more granularity, when data is updated) and take action to merge the data in (typically using mergeChangesFromContextDidSaveNotification:). Note that with this arrangement, there is no need for the main context to ever call the save: method.
To understand parent/child relationships, take Option A: The parent child approach simply means if the edit context fetches NSManagedObjects, they will be "copied into" (registered with) first the save context, then the main context, then finally edit context. You will be able to make changes to them, then when you call save: on the edit context, the changes will saved just to the main context. You would have to call save: on the main context and then call save: on the save context before they will be written out to disk.
When you save from a child, up to a parent, the various NSManagedObject change and save notifications are fired. So for example if you are using a fetch results controller to manage your data for your UI, then it's delegate methods will be called so you can update the UI as appropriate.
Some consequences: If you fetch object and NSManagedObject A on the edit context, then modify it, and save, so the modifications are returned to the main context. You now have the modified object registered against both the main and the edit context. It would be bad style to do so, but you could now modify the object again on the main context and it will now be different from the object as it is stored in the edit context. If you then try to make further modifications to the object as stored in the edit context, your modifications will be out of sync with the object on the main context, and any attempt to save the edit context will raise an error.
For this reason, with an arrangement like option A, it is a good pattern to try to fetch objects, modify them, save them and reset the edit context (e.g. [editContext reset] with any single iteration of the run-loop (or within any given block passed to [editContext performBlock:]). It is also best to be disciplined and avoid ever doing any edits on the main context.
Also, to re-iterate, since all processing on main is the main thread, if you fetch lots of objects to the edit context, the main context will be doing it's fetch processing on the main thread as those objects are being copied down iteratively from parent to child contexts. If there is a lot of data being processed, this can cause unresponsiveness in the UI. So if, for example you have a large store of managed objects, and you have a UI option that would result in them all being edited. It would be a bad idea in this case to configure your App like option A. In such a case option B is a better bet.
If you aren't processing thousands of objects, then option A may be entirely sufficient.
BTW don't worry too much over which option you select. It might be a good idea to start with A and if you need to change to B. It's easier than you might think to make such a change and usually has fewer consequences than you might expect.
Firstly, parent/child context are not for background processing. They are for atomic updates of related data that might be created in multiple view controllers. So if the last view controller is cancelled, the child context can be thrown away with no adverse affects on the parent. This is fully explained by Apple at the bottom of this answer at [^1]. Now that is out of the way and you haven't fallen for the common mistake, you can focus on how to properly do background Core Data.
Create a new persistent store coordinator (no longer needed on iOS 10 see update below) and a private queue context. Listen for the save notification and merge the changes into the main context (on iOS 10 the context has a property to do this automatically)
For a sample by Apple see "Earthquakes: Populating a Core Data Store Using a Background Queue"
https://developer.apple.com/library/mac/samplecode/Earthquakes/Introduction/Intro.html
As you can see from the revision history on 2014-08-19 they added
"New sample code that shows how to use a second Core Data stack to fetch data on a background queue."
Here is that bit from AAPLCoreDataStackManager.m:
// Creates a new Core Data stack and returns a managed object context associated with a private queue.
- (NSManagedObjectContext *)createPrivateQueueContext:(NSError * __autoreleasing *)error {
// It uses the same store and model, but a new persistent store coordinator and context.
NSPersistentStoreCoordinator *localCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[AAPLCoreDataStackManager sharedManager].managedObjectModel];
if (![localCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil
URL:[AAPLCoreDataStackManager sharedManager].storeURL
options:nil
error:error]) {
return nil;
}
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[context performBlockAndWait:^{
[context setPersistentStoreCoordinator:localCoordinator];
// Avoid using default merge policy in multi-threading environment:
// when we delete (and save) a record in one context,
// and try to save edits on the same record in the other context before merging the changes,
// an exception will be thrown because Core Data by default uses NSErrorMergePolicy.
// Setting a reasonable mergePolicy is a good practice to avoid that kind of exception.
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
// In OS X, a context provides an undo manager by default
// Disable it for performance benefit
context.undoManager = nil;
}];
return context;
}
And in AAPLQuakesViewController.m
- (void)contextDidSaveNotificationHandler:(NSNotification *)notification {
if (notification.object != self.managedObjectContext) {
[self.managedObjectContext performBlock:^{
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}];
}
}
Here is the full description of how the sample is designed:
Earthquakes: Using a "private" persistent store coordinator to fetch data in background
Most applications that use Core Data employ a single persistent store coordinator to mediate access to a given persistent store. Earthquakes shows how to use an additional "private" persistent store coordinator when creating managed objects using data retrieved from a remote server.
Application Architecture
The application uses two Core Data "stacks" (as defined by the existence of a persistent store coordinator). The first is the typical "general purpose" stack; the second is created by a view controller specifically to fetch data from a remote server (As of iOS 10 a second coordinator is no longer needed, see update at bottom of answer).
The main persistent store coordinator is vended by a singleton "stack controller" object (an instance of CoreDataStackManager). It is the responsibility of its clients to create a managed object context to work with the coordinator[^1]. The stack controller also vends properties for the managed object model used by the application, and the location of the persistent store. Clients can use these latter properties to set up additional persistent store coordinators to work in parallel with the main coordinator.
The main view controller, an instance of QuakesViewController, uses the stack controller's persistent store coordinator to fetch quakes from the persistent store to display in a table view. Retrieving data from the server can be a long-running operation which requires significant interaction with the persistent store to determine whether records retrieved from the server are new quakes or potential updates to existing quakes. To ensure that the application can remain responsive during this operation, the view controller employs a second coordinator to manage interaction with the persistent store. It configures the coordinator to use the same managed object model and persistent store as the main coordinator vended by the stack controller. It creates a managed object context bound to a private queue to fetch data from the store and commit changes to the store.
[^1]: This supports the "pass the baton" approach whereby—particularly in iOS applications—a context is passed from one view controller to another. The root view controller is responsible for creating the initial context, and passing it to child view controllers as/when necessary.
The reason for this pattern is to ensure that changes to the managed object graph are appropriately constrained. Core Data supports "nested" managed object contexts which allow for a flexible architecture that make it easy to support independent, cancellable, change sets. With a child context, you can allow the user to make a set of changes to managed objects that can then either be committed wholesale to the parent (and ultimately saved to the store) as a single transaction, or discarded. If all parts of the application simply retrieve the same context from, say, an application delegate, it makes this behavior difficult or impossible to support.
Update: In iOS 10 Apple moved synchronisation from the sqlite file level up to the persistent coordinator. This means you can now create a private queue context and reuse the existing coordinator used by the main context without the same performance problems you would have had doing it that way before, cool!
By the way this document of Apple is explaining this problem very clearly. Swift version of above for anyone interested
let jsonArray = … //JSON data to be imported into Core Data
let moc = … //Our primary context on the main queue
let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
privateMOC.parentContext = moc
privateMOC.performBlock {
for jsonObject in jsonArray {
let mo = … //Managed object that matches the incoming JSON structure
//update MO with data from the dictionary
}
do {
try privateMOC.save()
moc.performBlockAndWait {
do {
try moc.save()
} catch {
fatalError("Failure to save context: \(error)")
}
}
} catch {
fatalError("Failure to save context: \(error)")
}
}
And even simpler if you are using NSPersistentContainer for iOS 10 and above
let jsonArray = …
let container = self.persistentContainer
container.performBackgroundTask() { (context) in
for jsonObject in jsonArray {
let mo = CarMO(context: context)
mo.populateFromJSON(jsonObject)
}
do {
try context.save()
} catch {
fatalError("Failure to save context: \(error)")
}
}

iOS5: Determine if core data method is running in an asynch dispatch queue?

How can I tell if a method is running asynchronously or not?
I have several core data related methods which are sometimes called asynchronously via an asynch dispatch queue (GCD) and other times are called synchronously.
// sometimes this happens
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[[DataServices sharedInstance] doSomeCoreDataStuff:^(BOOL result, NSString *message)
{
// do some post-stuff here
}];
});
// other times this happens
[[DataServices sharedInstance] doSomeCoreDataStuff:^(BOOL result, NSString *message)
{
// do some post-stuff here
}];
In addition to this, I am using a singleton NSManagedObjectContext throughout my app. Since context is not thread-safe, in the case when the core data methods are running asynchronously I need to create a new context within those methods, otherwise I want to use the singleton instance context.
The only approach that comes to mind is something like [[NSThread mainThread] isMainThread], but GCD may or may not do work on the main thread, so this will not work.
The right way to deal with this is to configure your managed object context to use one the thread confinement concurrency types-- NSPrivateQueueConcurrencyType or NSMainQueueConcurrencyType-- and then use its performBlock: method when you want to use it. That is,
[[DataServices sharedInstance] performBlock:^{
// do Core Data stuff here
}];
You can do that on any thread or queue, and it's asynchronous. There's also performBlockAndWait: if you need to get the results immediately.
You'd use that call everywhere. The only exception is if you use NSMainQueueConcurrencyType and you know that you're running on the main queue, you could make calls on the context directly instead of via performBlock:

Resources