So, after learning about completion blocks a while back, I like using completion blocks a lot. I like closure and I like the ability to pass just about anything anywhere I want.
As someone who's new to thread programming, I've been staying away from GCD and NSOperation--but lately I've had to program asynchronous updated to Core Data stuff and I am beginning to have doubts to my "all completion block all the time" approach.
So here's one example of what I'm questioning myself: I have a series of potentially rather large data (images, sound, video, what have you) to upload to a server somewhere. The metadata for these data is stored in Core Data, and I have a timestamp that I use to decide which objects should get uploaded. All these uploads should be done sequentially.
What I have coded is a essentially basically just a function with a completion block in it, that has a call to itself at the end of the block, like this:
(void)uploadAllAsynchronously {
... // First figure out what to upload based on core data
// Here comes the completion block in question
void(^blk)(BOOL) = ^(BOOL)uploadSuccess {
... // if upload successful, update core data to mark what has been uploaded
[self uploadAllAsynchronously]; // Recursively calls the function that contains this block. I actually have a weak self, or if that fails to break a retain cycle, I should be able to pass in a NSManagedObjectContext as an argument.
}
[NSURLConnection sendAsynchronousRequest:... queue:... completionHandler:blk];
}
This should work, right? Is there something here that is completely dangerous that suggests I have to use GCD and handling my own queue? I am asking this because I'm kind of having some trouble right now possibly with data in it would appear different threads not updating correctly because of asynchronous calls, though not sure which part of my code is the culprit.
Thanks in advance.
Your block is of the wrong type.
As the documentation for
+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse *, NSData *, NSError *))handler
shows, the type of the completion black is
void (^) (NSURLResponse *, NSData *, NSError *)
not
void (^) (BOOL)
You should change blk to be something like
void (^blk) (NSURLResponse *, NSData *, NSError *) = ^ (NSURLResponse *response, NSData *data, NSError *error) {
//...
};
It would be more stylish to write
[NSURLConnection sendAsynchronousRequest:theRequest queue:theQueue completionHandler:^ (NSURLResponse *response, NSData *data, NSError *error) {
//...
}];
with the completion block in-line with the method.
Regarding the question of performing operations on your NSManagedObjectContext in the completion handler: This is fine so long as the NSOperationQueue passed to sendAsynchronousRequest:queue:completionHandler: is the same as the one in which the managed object context is created. But as the documentation for NSManagedObjectContext states
Core Data uses thread (or serialized queue) confinement to protect managed objects and managed object contexts (see “Concurrency with Core Data”). 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. Instead, you should pass a reference to a persistent store coordinator and have the receiving thread/queue create a new context derived from that.
If the queue that you pass is not the one on which you created the managed object context, you must do one of the following
Call -[NSOperationQueue addOperation:] on the queue where the managed object context was created.
Create a second managed object context (with the same persistent store coordinator) on the queue on which the core data operations are taking place.
Create a second managed object context and a second persistent store coordinator on the queue on which the core data operations are taking place.
Use locking.
The documentation on Concurrency with Core Data makes it clear that you must either use thread confinement (options 1-3 above) or use locking (option 4 above).
This is what the docs have to say about using locks:
If you choose not to use the thread containment pattern—that is, if you try to pass managed objects or contexts between threads, and so on—you must be extremely careful about locking, and as a consequence you are likely to negate any benefit you may otherwise derive from multi-threading.
This is what the docs have to say about having not just per-thread managed object contexts but also per-thread persistent store coordinators:
This approach provides for greater concurrency at the expense of greater complexity (particularly if you need to communicate changes between different contexts) and increased memory usage.
yes this code should work.. note: I'd rename the method then.. uploadIfNeeded maybe -- because it doesnt always blindly upload stuff...
Related
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 used RestKit integrated with CoreData in my project. To perform the network requests and map its JSON response the following service is widely used in the project:
- (RKManagedObjectRequestOperation *)managedObjectRequestOperationWithRequest:(NSURLRequest *)request
managedObjectContext:(NSManagedObjectContext *)managedObjectContext
success:(void (^)(RKObjectRequestOperation *operation, RKMappingResult *mappingResult))success
failure:(void (^)(RKObjectRequestOperation *operation, NSError *error))failure
The documentation of this function has this statement for the managedObjectContext attribute:
The managed object context with which to associate the operation. This
context will be used as the parent context of a new operation local
NSManagedObjectContext with the NSPrivateQueueConcurrencyType
concurrency type. Upon success, the private context will be saved and
changes resulting from the object mapping will be 'pushed' to the
given context.
Is it any reason to use another MOC instead of the main MOC for this request? I can only imagine one case, when a new private context should be used: If at some point the user is able to drop the mapped objects by the given network call. At this case it could be reasonable to keep the newly mapped objects on a separated context, and the changes will be propagated to the main context only if the user make the corresponding action, otherwise the context will be dropped with all its changes.
In my case, the RestKit is used for cache. After a request was performed and the response has mapped, than a fetchedRestultsController's delegate will be called by CD to update the UI. As a result I always specify the mainContext as the managedObjectContext attribute.
In an example project somewhere I found, that this attribute is specified to the MOC of the called thread. If it was the main thread, than the main context will be the input, if it was started from a background thread, than a new child MOC was created for the request. But I think, it is not reasonable, right?
Your question isn't really about RestKit or this method as such, it's about Core Data thread confinement. You have a responsibility to only use a managed object context from the designated thread, and if you ask an API to do something on a context it is again your responsibility to pass the correct one for the thread. So, if you're making a main thread request you should pass the main thread context. RestKit will always create a child context because it creates a background thread to do the heavy lifting.
It is possible, though uncommon, that you would start from a background thread and then you need to be careful about run loops, callback queues and notifications. It can get complicated quickly so this is usually best avoided...
It's generally easier to use the object manager instead of the operations directly as it deals with these choices for you. You're also almost always starting on the main thread and allowing RestKit to handle the background download and mapping for you, then pushing the result back to main.
What are the differences among following types calling Queues,which one is the best?
A)
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
B)
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
[myQueue addOperationWithBlock:^{
// Background work
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// Main thread work (UI usually)
}];
}];
C)Adding NSoperation which has subclassed to NSoperationQueue for example http://www.cimgf.com/2008/02/16/cocoa-tutorial-nsoperation-and-nsoperationqueue/
D)
[[NSOperation mainQueue] addOperation:myOperation];
IS it right approach? because this code adds NSoperation to mainQueue.This is not good for
background task.Usually mainQueue will be used for updating UI Only.
E)If I have missed anything except above call methods for Queue , please mention them also in answer.
None of those examples is best, and they're not even very different from each other. As the name implies, NSOperationQueue is a queue, i.e. a first in first out (FIFO) data structure, that contains operations. You can create your own operation queues, or you can use an existing one.
Example A is a reasonable example of using an existing queue, the main queue. You wouldn't want to put a synchronous network request on the main thread (which is what you get with +mainQueue) because it would block the user interface, but it's not necessarily a bad choice here because the request is asynchronous and the operation queue being passed in is used only to run the completion handler. Indeed, the completion handler might need to manipulate the user interface, and that should be done from the main queue.
Example B illustrates creating a new operation queue and scheduling an operation on that queue. That operation in turn schedules another operation on the main queue. This is a pretty typical scenario -- again, you should only manipulate the UI from the main thread, so it's common to have a background operation create an operation that runs on the main thread in order to update the UI.
Example C is similar to B, except that the operation in question is a subclass rather than one created from a block. NSOperation existed before Grand Central Dispatch and blocks came along, and it used to be that the only way to create an operation that did something interesting was to subclass NSOperation and override -main. The blog post you linked says it was posted Feb. 16, 2008, which would certainly have been from that era. Creating operations from blocks is a newer and often more concise style, but there's nothing wrong with subclassing especially if you might need to perform the same kind of operation in several places. Note also that that article uses -performSelectorOnMainThread:withObject:waitUntilDone:, which is an easier way to run something on the main thread than creating another NSOperation subclass.
Example D is too vague to really comment on -- it merely shows how to add some unspecified operation to the main queue. You're right that you wouldn't want to do that for long-running operations -- those should be scheduled on a background queue instead to avoid blocking the UI. But without knowing what myOperation does, you can't say that it's right or wrong.
So, none of your examples are incorrect, and none is really better than another. To the extent that they're different, it's because they're used in different situations. For example, NSURLConnection takes an NSOperationQueue and a block as parameters because it needs to wait until the connection is done before it schedules an operation created from the completion block. Once the connection is done, though, NSURLConnection will do pretty much what you see in examples B and D.
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.