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)
Related
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.
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 have a method that runs in a background thread using a copy of NSManagedObjectContext which is specially generated when the background thread starts as per Apples recommendations.
In this method it makes a call to shared instance of a class, this shared instance is used for managing property values.
The shared instance that managed properties uses a NSManagedObjectContext on the main thread, now even though the background thread method should not use the NSManagedObjectContext on the main thread, it shouldn't really matter if the shared property manager class does or does not use the such a context as it only returns scalar values back to the background thread (at least that's my understanding).
So, why does the shared property class hang when retrieving values via the main threads context when called from the background thread? It doesn't need to pass an NSManagedObject or even update one so I cannot see what difference it would make.
I can appreciate that my approach is probably wrong but I want to understand at a base level why this is. At the moment I cannot understand this whole system enough to be able to think beyond Apples recommended methods of implementation and that's just a black magic approach which I don't like.
Any help is greatly appreciated.
Does using:
[theContext performBlock:^{
// do stuff on the context's queue, launch asynchronously
}];
-- or --
[theContext performBlockAndWait:^{
// do stuff on the context's queue, run synchronously
}];
-- just work for you? If so, you're done.
If not, take a long, hard look at how your contexts are setup, being passed around, and used. If they all share a root context, you should be able to "move" data between them easily, so long as you lookup any objectIDs always on your current context.
Contexts are bound to threads/queues, basically, so always use a given context as a a reference for where to do work. performBlock: is one way to do this.
I am creating a NSManagedObject (subclass) with certain attributes. At the same time, I am executing some code/a block that does some network operation given the attributes of my NSManagedObject. Now, some times that network operation might fail or take too long, so I want to add the ability to cancel the execution of that code/block.
I was thinking of making the code/block an NSThread, and then I have the ability to call [theThread cancel]. However, how do I associate the NSThread with my NSManagedObject, given that I cannot add properties to NSManagedObject Categories? Is it OK to just add the property to the definition of the NSManagedObject itself? Seems legal, but subsequent changes to the Core Data model would overwrite my code, I guess.
But maybe there is an entirely different and better way to accomplish what I am trying to do? Any ideas?
First, new code really should prefer GCD or NSOperationQueue over NSThread. If you find yourself using NSThread it's time to slow down and revisit your design and implementation requirements.
Second, using NSManagedObject across threads is really, really bad. If you do anything but exceedingly trivial things, it can get very difficult to do right as well.
Finally, no matter how you do your threaded network access, you should prefer to grab the data from the managed object, and pass that instead of the managed object itself
If you must access the managed object, make sure your managed object context is of either NSMainQueueConcurrencyType or NSPrivateQueueConcurrencyType and access the managed object like by invoking performBlock or performBlockAndWait using the managedObjectContext property of the managed object.
EDIT
Ok, let me check this with you. What I a currently doing is spawning a
backgroundContext, create a new NSManagedObject using performBlock,
then save that background Context, switch to the parent context (using
performblock), obtain the newly created object in that context using
existingObjectWithId:. Then, I create a NSOperation subclass, tie the
NSManagedObject (from the parent context) to that NSOperation subclass
(it's a property on the subclass) and put that operation in a
NSOperationQueue. Within that NSOperation, the NSManagedObject gets
changed. It seems to work fine, does that look ok? – user1013725
Um... maybe??? I didn't follow that. Could you please post the code? That would be much more precise and more easy to understand.
#JodyHagins So I am not using performBlock, but maybe that's ok
because the managedObjectContext is the main context? – user1013725
No.
If the main context is created with either alloc] init] or alloc] initWithConcurrencyType:NSConfinementConcurrencyType then you must use it only when you know you are running on the main thread.
If it is created with alloc] initWithConcurrencyType:NSMainThreadConcurrencyType then you must use it only when you know you are running on the main thread or within one of the performBlock methods.
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.