A setting that allows the child NSManagedObjectContext to retrieve when it represents the parent context.

Background

Saving a lot of data at a time is very slow.

Current setting

My application has a private-queue NSManagedObjectContext as a parent that directly communicates with the NSPersistentStoreCoordinator to save the data. The context of the child main queue is consumed by the NSTreeController for the user interface ( NSOutlineView ).

(My goal was to prevent the beach ball from appearing . I am currently fixing the problem by saving data only when the application is inactive. But since the data that is planned to be deleted is not deleted yet, it may still appear as a result sampling. This is another problem I'm trying to solve.)

Problem

The main-queue context for children can only wait on retrieval when the parent context is busy with persistence.

Related issues

I will update this question when I have new results.

+1
source share
1 answer

I assume that you are developing for OS X / macOS ( NSTreeController and NSOutlineView ). I have no experience with macOS - I'm developing for iOS - so you may need to consider this when you read my answer.

I haven't made the switch to fast yet - my code is probably Objective-C ...

I'll start by preparing the Core Data stack.

I set two general properties in the header file:

 @property (nonatomic, strong) NSManagedObjectContext *mocPrivate; @property (nonatomic, strong) NSManagedObjectContext *mocMain; 

Although this is optional, I also prefer to set private properties for Core Data objects, including, for example:

 @property (nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator; 

Once I pointed to my model URL, installed the NSManagedObjectModel managed object model, pointed to my store URL for my NSPersistentStore and set my NSPersistentStoreCoordinator (PSC) persistent storage coordinator, I set up my two managed object contexts (MOCs).

As part of the “build” method of my base data stack, after I completed the code in the paragraph above, I 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 my console, but I excluded it here to keep the code clean.)

Pay attention to two important aspects of this code ...

  • establish a private queue MOC to interact with the PSC; and
  • set the primary MOC queue as a child of the private queue MOC queue.

Why is this done? First, highlight a couple of important points:

  • Saving memory is relatively fast; and
  • Saving to disk is relatively slow.

The private queue is asynchronous to the main queue. The user interface (UI) runs in the main queue. The private queue runs in a separate thread in the background, which works to maintain context and coordinate data storage with PSC, perfectly managed by Core Data and iOS. The main queue works with the main thread with the user interface.

Written in another way ...

  • Hard work that completes the irregular (basic data-driven) data constant for the PSC (saves to disk) ends in a private queue; and
  • In the main queue, work is completed to complete the regular (managed by the developer) data storage in the MOC (stored in memory).

In theory, this should ensure that your user interface is never blocked.

But there is more to it. How we manage the save process is important ...

I am writing a public method:

 - (void)saveContextAndWait:(BOOL)wait; 

I call this method from any class that needs to save 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 will work through this to explain what is happening.

I offer the developer the ability to save and wait (block), and depending on the developer’s use of the saveContextAndWait:wait method, the saveContextAndWait:wait private queue “saves” using either:

  • performBlockAndWait method (method for invoking a developer with wait = TRUE or YES ); or
  • performBlock method (a method for invoking a developer with wait = FALSE or NO ).

The method first checks to see if there are any changes in the main MOC queue. Do not do any work if we do not need!

Secondly, the method completes the (synchronous) call to performBlockAndWait in the main MOC queue. This makes a call to the save method in the code block and waits for completion before allowing the code to continue. Remember that this is the usual “saving" of small data sets. The option (asynchronous) for calling performBlock is not required here and will actually lead to a disruption in the effectiveness of the method, as I experienced when I learned to implement this in my code (it was not possible to save data due to the save call in the main MOC queue trying to execute after terminating save in the private MOC queue).

Thirdly, we write a small block in a block that contains code to save a private MOC queue.

Fourth, the method checks to see if there are any changes in the MOC of the private queue. This may not be necessary, but it is harmless to include here.

Fifth, depending on the option that the developer wants to implement ( wait = YES or NO ), the method calls either performBlockAndWait or performBlock on the block inside the block (under the third above).

At this last stage, regardless of the implementation ( wait = YES or NO ), the function of saving data to disk from the private MOC queue in the PSC is abstracted in the private queue on the asynchronous stream into the main stream. Theoretically, “saving to disk” via PSC can take as much time as he likes, because it has nothing to do with the main stream. And since the private MOC queue has all the data in memory, the main MOC queue is fully and automatically informed of the changes, since it is a child node of the MOC of the private queue.

If you are importing large amounts of data into the application, then I am working on an implementation, then it makes sense to import this data into the private MOC queue.

The MOC private queue does two things here:

  • It coordinates data storage (to disk) using PSC;
  • Since he is the parent of the MOC main queue (in memory), the MOC of the main queue will be notified of data changes in the MOC of the private queue, and the merges are managed by Core Data;

Finally, I use the NSFetchedResultsController (FRC) to manage my data retrievals, which are all completed in relation to the main MOC queue. This maintains a data hierarchy. Because changes are made to the dataset in any context, FRC updates the view.

This is a simple solution! (Once I spent weeks stumbling about it, and a few weeks updated my code.)

There is no need to track notifications of mergers or other changes in the MOC. Core Data and iOS process everything in the background.

So, if this does not work for you - let me know - I may have ruled out or missed something since I wrote this code a year ago.

+1
source

Source: https://habr.com/ru/post/1244190/


All Articles