What is NSManagedObjectContext's performBlock: used for?
Asked Answered
O

2

68

In iOS 5, NSManagedObjectContext has a couple of new methods, performBlock: and performBlockAndWait:. What are these methods actually used for? What do they replace in older versions? What kind of blocks are supposed to be passed to them? How do I decide which to use? If anyone has some examples of their use it would be great.

Oklahoma answered 29/3, 2012 at 8:59 Comment(3)
Did you mean "NSManagedObjectContext"?Snapshot
I am pretty sure he did. Maybe Nevan can suggest this at: stackoverflow.com/tags/nsmanagedobjectcomplex/synonymsIrina
Fixed that spelling mistake, thanks for the heads up.Oklahoma
S
126

The methods performBlock: and performBlockAndWait: are used to send messages to your NSManagedObjectContext instance if the MOC was initialized using NSPrivateQueueConcurrencyType or NSMainQueueConcurrencyType. If you do anything with one of these context types, such as setting the persistent store or saving changes, you do it in a block.

performBlock: will add the block to the backing queue and schedule it to run on its own thread. The block will return immediately. You might use this for long persist operations to the backing store.

performBlockAndWait: will also add the block to the backing queue and schedule it to run on its own thread. However, the block will not return until the block is finished executing. If you can't move on until you know whether the operation was successful, then this is your choice.

For example:

__block NSError *error = nil;
[context performBlockAndWait:^{
    myManagedData.field = @"Hello";
    [context save:&error];
}];

if (error) {
    // handle the error.
}

Note that because I did a performBlockAndWait:, I can access the error outside the block. performBlock: would require a different approach.

From the iOS 5 core data release notes:

NSManagedObjectContext now provides structured support for concurrent operations. When you create a managed object context using initWithConcurrencyType:, you have three options for its thread (queue) association

  • Confinement (NSConfinementConcurrencyType).

    This is the default. You promise that context will not be used by any thread other than the one on which you created it. (This is exactly the same threading requirement that you've used in previous releases.)

  • Private queue (NSPrivateQueueConcurrencyType).

    The context creates and manages a private queue. Instead of you creating and managing a thread or queue with which a context is associated, here the context owns the queue and manages all the details for you (provided that you use the block-based methods as described below).

  • Main queue (NSMainQueueConcurrencyType).

    The context is associated with the main queue, and as such is tied into the application’s event loop, but it is otherwise similar to a private queue-based context. You use this queue type for contexts linked to controllers and UI objects that are required to be used only on the main thread.

Snapshot answered 3/4, 2012 at 23:58 Comment(14)
Thanks for the answer. performBlockAndWait: doesn't block the main thread, does it? It's confusing that it can be attached to the main run loop, but has a name that makes it seem like it makes you wait.Oklahoma
Also, what are the "backing queue" and "long persist operations". Sorry for all the questions, I have no clue about threading.Oklahoma
@Snapshot I think you may have things reversed (or I'm reversed in reading this). performBlockAndWait would block whatever thread it's called from -- main or otherwise -- and doesn't care which thread the MOC is associated with. Or did I misread you?Krona
@RonLugg, thanks for calling me out. I'm now reading it the way you are reading it.Snapshot
If performBlockAndWait: is called from the main thread, it will block the main thread until the, er, block has completed. "long persist" is vague, but consider that persisting data to disk could be a longer operation than storing the same data to an in memory context. (credit @Krona for pointing out incorrect, misleading information in original comment)Snapshot
If performBlockAndWait will block the thread it calls, why we bother using it? Why not use coredata like usual?Drummond
do I need to create my ManagedObject in peformBlock?Concoction
But what if I'm accessing the context from the right thread, the one where it was initialized? Do I need to use performBlock or performBlockAndWait or can I do just like before?Kraut
@JimThio why use performBlockAndWait... because sometimes you need to retrieve something from core data, maybe do something on the object, and then process afterwards. performBlock would return immediately - as it just schedules work onto the queue of the context. If this work is on a background thread and background context, you need to ensure that the work is executed on the correct queue - which is what the performBlock method does. Remember a queue is not a thread - and the queue could run on any number of threads.Pahoehoe
It is my understanding that performBlockAndWait runs on the Caller's thread.Interfaith
Could someone clear up "the block will not return until the block is finished executing"Athapaskan
@marciokoko: -performBlockAndWait: will not return until the ^{block} is finished executing. It behaves just like any plain old nested subroutine. It's execution is synchronous.Nagle
@Snapshot I'm not sure, should I wrap all my entity modifications in a performBlock, or only the saving part ?Marcelline
"Note that because I did a performBlockAndWait:, I can access the error outside the block." Because the error was declared with __block you can access it outside the block. "performBlock: would require a different approach." Do you mean that because performBlock: executes asynchronously, therefor accessing an error outside of the block would be a game of change: there might be an error, but not at the moment you access the pointer.Candelabra
I
-4

They allow you to access the same managedObjectContext accross threads.

I am not really sure I am correct, but this is how I use it.

You use performBlockAndWait is like "usual". You do not need it if you execute the managedObjectContext only on one thread. If you execute it on many threads then yes you will need performBlock.

So, if you're on main thread, you do not need to do performBlockAndWait for the main managedObjectContext. At least I don't and is doing fine.

However if you access that managedObjectContext on other threads then yes you will need to do performBlockAndWait.

So that's the purpose of performBlock and performBlockAndWait.

Would someone please correct me if I am wrong here. Of course if you access the context only on one thread then you can simply use the default.

Illnatured answered 22/11, 2012 at 5:34 Comment(11)
The two methods have to do with the behavior of instruction execution on the thread where you're invoking them. One blocks the calling thread waiting for a return, the other doesn't (call returns immediately even though work is still running). Either can be called from any thread.Stephanystephen
-1: This answer is simply wrong. To start- from the docs on NSManagedObjectContext: Core Data uses thread (or serialized queue) confinement to protect managed objects and managed object contexts. A consequence of this is that a context assumes the default owner is the thread or queue that allocated it—this is determined by the thread that calls its init method. You should not, therefore, initialize a context on one thread then pass it to a different thread. Thereby, NSManagedObjectContexts are NOT thread safe and you can't access them across threads.Gurolinick
Further, these methods have to do with concurrency (synchronous or asynchronous processing). See answer from @Snapshot and related comments for discussion regarding this.Gurolinick
@Gurolinick I guess you can actually access the NSManagedObjectContexts across threads. What you can't do is pass NSManagedObjects across threads. You can say to your context in the main thread to save by doing [mainContext performBlock:^{ NSError *error = nil; [mainContext save:&error]; }; from a different thread.Kraut
I always access core data objects from it's own thread. Hence, I very rarely every use performBlock or performBlockAndWait. Things work fine.Illnatured
@FábioOliveira No - Apple is very explicit about this: you cannot access them across threads. PerformBlock allows you to have one thread ask the other thread to execute the code for it. The compiler will allow you to plain access, but if you do it you guarantee sooner or later a runtime consistency error (i.e.: corrupted CoreData database, and all hell breaks loose. Been there, seen that)Bangs
@Adam, you're right. Apple states it in developer.apple.com/library/mac/documentation/Cocoa/Reference/…: performBlock: and performBlockAndWait: ensure the block operations are executed on the queue specified for the context. The performBlock: method returns immediately and the context executes the block methods on its own thread. With the performBlockAndWait: method, the context still executes the block methods on its own thread, but the method doesn’t return until the block is executed.Kraut
It is my understanding that performBlockAndWait runs on the Caller's thread, unlike performBlock.Interfaith
@BradThomas performBlockAndWait: runs on the context's thread, just like performBlock: does, but makes caller's thread wait until block execution is finished.Neely
@Neely I'd like this clarified so I challenge you thus… what you say doesn't jive with the convincing top answer (and convincing question) posted on this thread… #11832446Interfaith
@BradThomas I tried po [NSThread currentThread] in the debugger inside and outside performBlockAndWait: call and... Oh, it really run on the caller thread. Thank you for the link.Neely

© 2022 - 2024 — McMap. All rights reserved.