0
votes

After dealing with MOCs and queues for a while, I'm still unsure I understand how to use MOCs with queues the right way. I'll address each type of MOC separately:

NSMainQueueConcurrencyType

This one is actually easy. It says that has to run on the main queue. This can be achieved using [context performBlock:block] or by using it directly form the main thread.

If your code is executing on the main thread, you can invoke methods on the main queue style contexts directly instead of using the block based API.

NSConfinementConcurrencyType

that context will not be used by any thread other than the one on which you created it

What does that mean about queues, even serial ones that don't promise to use the same thread all the time?

NSPrivateQueueConcurrencyType

The context creates and manages a private queue

Do I have to use this queue for all processing via [context performBlock:block]?

Can I just use it directly from within a single serial queue that I defined in a fashion similar to NSMainQueueConcurrencyType?

Or in other words, is it safe to do the following:

NSOperationQueue *workQueue = [[NSOperationQueue alloc] init];
workQueue.maxConcurrentOperationCount = 1;

[workQueue addOperationWithBlock:^{
    // It just creates a new MOC with some parent
    _context = [SUDataManager createChildContext]; 
    // .... Do things with context without [_context performBlock:block]...
}];
1
Did the answer below provide a solution? - SwiftArchitect

1 Answers

0
votes

Use blocks!

I will suggest to do exactly the opposite: always use the block approach, and here is why:

The block guarantees that each operation is scheduled and completes before the next one. It's a bit like using { and } after an if() { }. This practice ensures that you are not bound to errors due to source formatting, and that you are writing and executing what you intended.

You just can't run into races if you combine blocks with performBlockAndWait. So unless you have a good reason to do otherwise, and lots of time to spare and debug iCloud, I would choose the robust method every time.

Example:

[self.mainMOC performBlockAndWait:^{
    __strong myClass * strongSelf = weakSelf;
    if(strongSelf) {
        if( [strongSelf.mainMOC hasChanges]) {
            NSError *error = nil;
            [strongSelf.mainMOC save:&error];
        }
    }
}];

Which Concurrency Type to choose

  1. main thread: use NSMainQueueConcurrencyType
  2. any other thread: use `NSPrivateQueueConcurrencyType.

While you could technically use NSConfinementConcurrencyType for main thread, and is possibly there solely for legacy, it is not needed in the MainQueue PrivateQueue model.

How to setup background thread

In your background thread(s), set the parent context. If you do so, and also follow the performBlockAndWait advice, all your operation will occur in sequence, and complete prior the next one executes, ensuring data stability. iCloud is not an environment you want to corrupt...

self.privateMOC = [[NSManagedObjectContext alloc]
    initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[self.privateMOC setParentContext:mainMOC];
[self.privateMOC setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];