The docs state that NSManagedObjectContext with NSPrivateQueueConcurrencyType lets the user perform asynchronous code by using performBlock:. But what happens if I want to write an NSOperation subclass for processing managed objects in such a child context/private queue setup?
For example:
// Get managed object IDs from selected objects (defined in one of my own categories).
NSArray * selectedObjIDs = [NSManagedObjectContext IDsWithObjects:self.arrayController.selectedObjects];
NSBlockOperation * operation = [NSBlockOperation blockOperationWithBlock:^
{
NSManagedObjectContext * childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
childContext.parentContext = myMainMOC;
[childContext performBlock:^
{
// Get objects in child context with previously generated managed object IDs (again, from my own category).
NSArray * privateObjects = [childContext objectsWithIDs:selectedObjIDs];
// Do something with the objects.
for( NSManagedObject * object in privateObjects )
{
[object setValue:#"New Title" forKey:#"title"];
}
[childContext save:NULL];
}];
}];
// Execute in our own private NSOperationQueue.
[self.backgroundQueue addOperation:operation];
The code works fine but when setting breakpoints inside both blocks I can see that execution first goes into background thread A (spawned by NSBlockOperation), then into background thread B (dedicated to the child MOC - as expected.
(BTW: I believe I saw an equivalent setup in the sample code of Apple's WWDC session "Advanced NSOperations".)
Question #1: are these two nested dispatches a problem somehow, i.e. in terms of performance? It just doesn't seem right to me - shouldn't the code run in the child MOC's private queue only?
Question #2: imagine I would subclass NSOperation (instead of using NSBlockOperation). Should I override its 'asynchronous' property to return YES to really just use the child MOC's private queue?
Your code isn't exactly wrong but it's more complicated than it needs to be.
When you call performBlock, you are telling the managed object context to execute the block asynchronously on a private queue. That block is already asynchronous with regard to the code that calls performBlock. Wrapping it in an NSOperation is probably safe but also completely unnecessary. As soon as your code calls performBlock, the NSOperation will complete, because of the asynchronous nature of performBlock. In short, the NSOperation is completely unnecessary here.
You could replace performBlock with performBlockAndWait, but that wouldn't make a lot of sense. You'd be forcing the NSOperation to wait, but for no good reason.
Subclassing NSOperation as you mention is also unnecessary unless you have some other asynchronous requirement that you didn't mention. How to configure the NSOperation is irrelevant-- just get rid of it.
Update since in a comment it seems you do have other async requirements...
The code is probably safe depending on what your other operations do.
Since performBlock returns immediately, the next operation in the queue will run in parallel with this one on a different queue. Does that matter? It depends what other operations in the queue are doing. If they depend on this performBlock having finished, that's a problem. You could deal with this using performBlockAndWait to keep the queue serial.
Related
I have a method similar to this:
- (void)handleUpdate
{
dispatch_sync(dispatch_get_main_queue(), ^{
NSArray *objectIDs = [self.objectsInMainContext valueForKeyPath:#"objectID"];
[self.privateContext performBlockAndWait: ^{
// Some processing
}];
});
}
What I called mainContext is associated to main queue, and privateContext is associated to a private queue and is child of the mainContext. This method is called from the privateContext's private queue, and it is not nil when the performBlockAndWait: call is reached, but execution does not enter the block and neither any code after this method is reached...
What could I be missing here?
Thanks in advance
EDIT: I get no error in Xcode, the breakpoints I've set in the code inside the performBlockAndWait: block and after this method call are simply not reached.
EDIT 2: I corrected the code snippet, I wasn't accessing the mainContext but an array of objects associated to mainContext.
If handleUpdate is called from the private queue as you say, then this is a classic deadlock.
You call dispatch_sync on the private queue. That means "wait until this returns before continuing on this queue." Then, inside that block, you enqueue another block that has to wait to run on the private queue and won't return until it does. The queue is now waiting on itself and will never progress.
I suspect you want dispatch_sync to be dispatch_async here.
Alright, there is at least 1 thing that is wrong in your situation:
Calling performBlockAndWait: on the main thread (via passing it to the main queue). The documentation says that performBlockAndWait:
Synchronously performs a given block on the receiver’s queue.
As a result of that call, you will get a dead lock on the main thread (as you use dispatch_sync outside.
UPDATE on the dispatch_async way:
The problem will not disappear. That's because you'll be still using the main queue (which implies the main thread in the end) and the main CD context (which also implies the main thread). The problem is that you make the main thread to wait until the job is done on the same (main) thread - just a dead lock out of the box.
You have several problems.
First, you are using GCD directly to serialize access to a NSMainQueueConcurrency MOC. You should never use anything but the CoreData synchronization mechanisms for accessing CoreData. Yes, it is safe to access a NSMainQueueConcurrencyType MOC from the main thread. However, you should never use GCD directly... especially dispatch_sync.
Second, you are using dispatch_sync which is not reentrant. It can, and will, cause deadlocks. You should hardly ever use this function. Consider it a very sharp instrument, to be used where it is the only solution to a very specific problem.
Third, you are calling performBlockAndWait on a child context, which should never be done because it can cause deadlocks. In general, you should avoid the non-asynchronous threading models unless you really know what you are doing.
Your code should be something more like this. Each performBlock call will post a block onto a message queue, which will get processed independently of any waiting thread.
- (void)handleUpdate
{
[self.mainContext performBlock:^{
NSArray *objectIDs = [self.objectsInMainContext valueForKeyPath:#"objectID"];
[self.privateContext performBlock: ^{
// Some processing with objectIDs
}];
});
}
I'm creating an NSManagedObjectContext in a private queue to handle data updates I take from files and/or services:
NSManagedObjectContext *privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
privateContext.persistentStoreCoordinator = appDelegate.persistentStoreCoordinator;
Since I'm using a private queue, I don't fully understand the difference between performBlock: and performBlockAndWait: methods... To perform my data updates I'm currently doing this:
[privateContext performBlock: ^{
// Parse files and/or call services and parse
// their responses
// Save context
[privateContext save:nil];
dispatch_async(dispatch_get_main_queue(), ^{
// Notify update to user
});
}];
In this case, my data updates are made synchronoulsy and sequentially, so I suppose that is the correct place to save the context, right? If I'm doing something wrong, I'd appreciate if you let me know. On the other hand, would this code be equivalent?:
[privateContext performBlockAndWait: ^{
// Parse files and/or call services and parse
// their responses
// Save context
[privateContext save:nil];
}];
// Notify update to user
Again I guess that is the correct place to save the context... what are the differences between both methods (if any, in this case)?
What if instead of performing synchronous service calls or files parsing, I need to perform asynchronous service calls? How would these data updates be managed?
Thanks in advance
You are correct in that anything you want to do with a MOC must be done within either performBlock or performBlockAndWait. Note that retain/release is thread safe for managed objects, so you don't have to be inside one of those blocks to retain/release reference counts on managed objects.
They both utilize a synchronous queue to process messages, which means that only one block will execute at a time. Well, that's almost true. See the descriptions of performBlockAndWait. In any event, the access to the MOC will be serialized such that only one thread is accessing the MOC at a time.
tl;dr Don't worry about the difference, and always use performBlock.
Factual Differences
There are a number of differences. I'm sure there are more, but here are the ones that I think are most important to understand.
Synchronous vs. Asynchronous
performBlock is asynchronous, in that it returns immediately, and the block is executed at some time in the future, on some undisclosed thread. All blocks given to the MOC via performBlock will execute in the order they were added.
performBlockAndWait is synchronous, in that the calling thread will wait until the block has executed before returning. Whether the block runs in some other thread, or runs in the calling thread is not all that important, and is an implementation detail that can't be trusted.
Note, however, that it could be implemented as "Hey, some other thread, go run this block. I'm gonna sit here doing nothing until you tell me it's done." Or, it could be implemented as "Hey, Core Data, give me a lock that prevents all those other blocks from running so I can run this block on my own thread." Or it could be implemented in some other way. Again, implementation detail, which could change at any time.
I'll tell you this though, the last time I tested it, performBlockAndWait executed the block on the calling thread (meaning the second option in the above paragraph). This is only really information to help you understand what is going on, and should not be relied upon in any way.
Reentrancy
performBlock is always asynchronous, and is thus not reentrant. Well, some may consider it reentrant, in that you can call it from within a block that was called with performBlock. However, if you do this, all calls to performBlock will return immediately, and the block will not execute until at least the currently executing block completely finishes its work.
[moc performBlock:^{
doSomething();
[moc performBlock:^{
doSomethingElse();
}];
doSomeMore();
}];
These functions will always be executed in this order:
doSomething()
doSomeMore()
doSomethingElse()
performBlockAndWait is always synchronous. Furthermore, it is also reentrant. Multiple calls will not deadlock. Thus, if you end up calling performBlockAndWait while you are inside a block that was being run as a result of another performBlockAndWait, then it's OK. You will get the expected behavior, in that the second call (and any subsequent calls) will not cause a deadlock. Furthermore, the second one will completely execute before it returns, as you would expect.
[moc performBlockAndWait:^{
doSomething();
[moc performBlockAndWait:^{
doSomethingElse();
}];
doSomeMore();
}];
These functions will always be executed in this order:
doSomething()
doSomethingElse()
doSomeMore()
FIFO
FIFO stands for "First In First Out" which means that blocks will be executed in the order in which they were put into the internal queue.
performBlock always honors the FIFO structure of the internal queue. Every block will be inserted into the queue, and only run when it is removed, in FIFO order.
By definition, performBlockAndWait breaks FIFO ordering because it jumps the queue of blocks that have already been enqueued.
Blocks submitted with performBlockAndWait do not have to wait for other blocks that are running in the queue. There are a number of ways to see this. One simple one is this.
[moc performBlock:^{
doSomething();
[moc performBlock:^{
doSomethingElse();
}];
doSomeMore();
[moc performBlockAndWait:^{
doSomethingAfterDoSomethingElse();
}];
doTheLastThing();
}];
These functions will always be executed in this order:
doSomething()
doSomeMore()
doSomethingAfterDoSomethingElse()
doTheLastThing()
doSomethingElse()
It's obvious in this example, which is why I used it. Consider, however, if your MOC is getting stuff called on it from multiple places. Could be a bit confusing.
The point to remember though, is that performBlockAndWait is preemptive and can jump the FIFO queue.
Deadlock
You will never get a deadlock calling performBlock. If you do something stupid inside the block, then you could deadlock, but calling performBlock will never deadlock. You can call it from anywhere, and it will simply add the block to the queue, and execute it some time in the future.
You can easily get deadlocks calling performBlockAndWait, especially if you call it from a method that an external entity can call or indiscriminately within nested contexts. Specifically, you are almost guaranteed to deadlock your applications if a parent calls performBlockAndWait on a child.
User Events
Core Data considers a "user event" to be anything between calls to processPendingChanges. You can read the details of why this method is important, but what happens in a "user event" has implications on notifications, undo management, delete propagation, change coalescing, etc.
performBlock encapsulates a "user event" which means the block of code is automatically executed between distinct calls to processPendingChanges.
performBlockAndWait does not encapsulate a "user event." If you want the block to be treated as a distinct user event, you must do it yourself.
Auto Release Pool
performBlock wraps the block in its own autoreleasepool.
performBlockAdWait does not provide a unique autoreleasepool. If you need one, you must provide it yourself.
Personal Opinions
Personally, I do not believe there are very many good reasons to use performBlockAndWait. I'm sure someone has a use case that can't be accomplished in any other way, but I've yet to see it. If anyone knows of that use case, please share it with me.
The closest is calling performBlockAndWait on a parent context (don't ever do this on a NSMainConcurrencyType MOC because it could lock up your UI). For example, if you want to ensure that the database has completely saved to disk before the current block returns and other blocks get a chance to run.
Thus, a while ago, I decided to treat Core Data as a completely asynchronous API. As a result, I have a whole lot of core data code, and I do not have one single call to performBlockAndWait, outside of tests.
Life is much better this way. I have much fewer problems than I did back when I thought "it must be useful or they wouldn't have provided it."
Now, I simply no longer have any need for performBlockAndWait. As a result, maybe it has changed some, and I just missed it because it no longer interests me... but I doubt that.
I was wondering if someone could point me in the right direction about whether or not I am using concurrency correctly. I find that currently, the code is very unwieldy and almost unintuitive. For the purposes of my demonstration, please note the CoreData architecture is as described by Marcus Zarra in his article http://martiancraft.com/blog/2015/03/core-data-stack/. Since the operation could take a very long time, I want this entire process to be done in a background thread.
NSManagedContext *context = [[NSManagedContext alloc] initWithConcurrency:NSPrivateConcurrencyQueue];
context.parentContext = [[CoreDataController sharedDispatch] managedObjectContext];
[context performBlock: ^{
NSManagedObject *someManagedObject = [[context executeFetchRequest:request error:nil] firstObject];
NSString *resultFromLongOperation = [self someLongOperation:someManagedObject];
[self doSomething];
BOOL anotherResultFromLongOperation = [self aDifferentLongOperation:someManagedObject];
}];
So from this short example, we can see that I am manipulating the variable someManagedObject, which is of NSManagedObject type and therefore must be used in a performBlock/performBlockAndWait as prescribed by Apple. Should I be passing in the context into someLongOperation and aDifferentLongOperation? If I don't, wouldn't that mean I would have to create another child context and do whatever the function does inside a performBlock/performBlockAndWait and return the result with a __block type? What about in the event if the result of someLongOperation affects the response of aDifferentLongOperation? Am I still structuring my code properly for a situation like this?
Thanks very much in advanced!
I struggled with this same issue not long ago.
I've looked for best practice scenarios and found that:
You indeed, as you stated, shouldn't violate the one context/thread rule set by Apple.
You should normally use completion blocks or notifications with long running operations, whether it's for not making the UI freeze, or just for code clarity.
When you use a completion block, you can create a child context in your long running operation function (someLongOperation) and do whatever you want with it.
If your result is a NSString, then you shouldn't have a problem, because it's not a NSManagedObject and it not bound to any one context.
If you result is a NSManagedObject, then you should pass its NSManagedObjectID to the completion block (with [managedObject objectID] method).
You can use the context's method obtainPermanentIDsForObjects in case you created a new entity and it doesn't have an object ID yet.
Basically, you can use the same context for every long running operation if they all use the same thread. In my case they don't necessarily so I'm creating a new child context for every long running operation just to be sure (creating a new child context is a very fast operation so performance isn't an issue)
I am using the performBlock: and performBlockAndWait: methods to execute fetch requests on my context on a read only database (it is packaged with my app so never written to).
Am I supposed to be wrapping every NSManagedObject accessor inside performBlockAndWait: as well? - that'd be pretty messy. I'm currently getting crashes whenever CoreData is faulting a one-to-many relationship while CoreData's private queue is doing an execute with performBlock:
Something like:
NSManagedObject* alreadyFetchedObject = ...;
NSArray* alreadyFetchedObject.otherObjects; // Crashes here on main thread (no performBlock wrapped around accessing otherObjects)
.
[context performBlockAndWait:^{
// Currently executing here on CoreData's own queue
result = [context executeFetchRequest:fetchRequest error:nil];
}];
Yes, of course. Maybe with the exception for managed objects which have been associated to a context which is executed on the main thread. But for clarity, I would always wrap accesses into performBlock:. Be careful with performBlockAndWait: - this is error prone for dead locks.
Additionally, when you have statements like this:
NSManagedObject* alreadyFetchedObject = ...;
and access alreadyFetchedObject later, you need to ensure that the corresponding managed object context still exists. Thus, always accessing managed objects with performBlock: or performBlockAndWait: reminds you not to accidentally delete the context ;)
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.
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.
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.