Search code examples
objective-ccore-datansmanagedobjectcontext

Saving NSManagedObjectContext with NSPrivateQueueConcurrencyType


Im currently learning how to use core-data in a multithreaded environment;

I therefore created a small project with two NSManagedObjectContext: A main NSManagedObjectContext with NSMainQueueConcurrencyType for reads and its child NSManagedObjectContext with NSPrivateQueueConcurrencyType for create/update/delete operations.

It has often been said that saving an NSManagedObjectContext with NSPrivateQueueConcurrencyType should be done through performBlock: like so:

[context performBlock:^
{
    Book *mutableBook = [self getMutableVersionOfBook:book];

    [context deleteObject:mutableBook];

    [context save:nil];
}];

What happens if the performBlock: is omitted, like so:

Book *mutableBook = [self getMutableVersionOfBook:book];

[context deleteObject:mutableBook];

[context save:nil];

Does the save then happen on the thread the save was called on? What can happen if performBlock: isn't used?


Solution

  • A private queue MOC should only be touched via a -performBlock: or -performBlockAndWait:. If you touch it any other way then you are violating the thread boundary rule of Core Data and you will eventually cause data corruption.

    In some situations this will cause a crash in your application because violating that thread boundary is an application level error. Apple has turned that crash off a number of times and it may or may not be a crash state right now. In my opinion, It should be a crash state all the time.

    As a general rule, I recommend that you use thread confined MOCs as children of the main MOC as opposed to using private MOCs. While private MOCs are nice and useful, the structure of every action must be in a block and that you cannot access the resulting NSManagedObject instances except in those blocks is limiting. Better to spin off an operation into a queue, create a thread confined MOC in that queue and then have cleaner code than constantly having to dive into blocks (or worse create huge un-maintanable blocks).

    Finally. You are passing nil to -save:. Never do that. You are hiding a potential problem when you do that. Even if "it is just an example", it is a terrible habit and you should break it immediately. Even in example code, pass in a NSError and check the error. Even if you just pass the results to NSLog you will at least avoid a surprise.