Will Lisac
Will Lisac

Reputation: 236

NSManagedObjectContext's performBlockAndWait: not executing on the receiver's queue

I've noticed that it's possible for an NSManagedObjectContext with an NSMainQueueConcurrencyType to performBlockAndWait: and execute the block on a queue other than the receiver's (main) queue.

For example, the following code results in my parentContext executing the block on the childContext's queue if my parentContext is of type NSMainQueueConcurrencyType and my childContext is of type NSPrivateQueueConcurrencyType:

[childContext performBlockAndWait:^{
    //Thread 1, Queue: NSManagedObjectContext Queue
    [parentContext performBlockAndWait:^{
        //Thread 1, Queue: NSManagedObjectContext Queue
        //This is the same queue as the child context's queue
    }];
}];

In contrast, the following code works as expected – my parentContext executes the block on the main queue:

[childContext performBlock:^{
    [parentContext performBlockAndWait:^{
        //Thread 1, Queue: com.apple.main-thread
    }];
}];

Is this the expected behavior? It is certainly confusing me since the docs state "performBlockAndWait: synchronously performs a given block on the receiver’s queue."

Upvotes: 4

Views: 922

Answers (1)

Léo Natan
Léo Natan

Reputation: 57040

You should not be worried on what thread blocks are executed. What the performBlock: and performBlockAndWait: methods guarantee is thread safety. Thus, calling performBlockAndWait: from the main thread does not mean there would be a context switch to a background thread - it is very expensive and it is not needed. If during the operation of the block (on the main thread), an attempt is performed to perform a block, it would be blocked until the currently executing block is finished. At the end of the day, the result would be the same as if a context switch was performed, only faster. On the other hand, calling performBlock: will queue the block on an arbitrary queue, often executing on a background thread.

In the example above, since you performBlockAndWait:, your private queue context executes your block on the main thread, as does the main context block. In your second example, you schedule the block to run asynchronously, so it is executed on a background thread.

You should not judge a thread's queue by it's name. To see if you are on the main queue, you can use dispatch_get_current_queue() and test if it is equal to dispatch_get_main_queue().

Upvotes: 3

Related Questions