8

Are Objective-C blocks always executed in a separate thread?

Specifically, I'm asking about the sendAsynchronousRequest:queue:completionHandler method of the NSURLConnection class. This is the scenario:

Main thread (1st thread) calls the sendAsynchronousRequest method the sendAsynchronousRequest is executed on a 2nd thread, managed by the NSOperationQueue when method is completed and calls commpletionHandler, which thread is it executed on? 2nd thread? yet another 3rd thread? or the 1st thread?

Thanks!

Rob
  • 415,655
  • 72
  • 787
  • 1,044
ikevin8me
  • 4,253
  • 5
  • 44
  • 84
  • I'm still a bit confused (after 3 answers). I'm asking about the execution of the block itself. Will it be executed on the same thread as the URL request? – ikevin8me Oct 02 '12 at 06:16
  • The answers talk about "queue" - this refers to the NSOperationQueue. So, it means this queue will detach yet another separate concurrent thread to execute the block? – ikevin8me Oct 02 '12 at 06:17
  • @ikevnjp: Not sure what you mean by "yet another", but the block will be executed on whichever thread the queue decides to use. You shouldn't assume it will create a new thread for each request. – Jon Skeet Oct 02 '12 at 06:24
  • Thanks for your reply. "on whichever thread the queue decides to use" means it can be the same thread used to perform the URL request OR an differently thread. – ikevin8me Oct 02 '12 at 06:28
  • Oh... now I'm reading the documentation and thinking carefully... The [Apple] docs says "queue -The operation queue to which the handler block is dispatched when the request completes or failed." So, this actually mean that supplying a 'queue' as a parameter is ONLY for the execution of completionHandler block. That means, if I call sendAsynchronousRequest from the main thread, the URL request (the operation to fetch data from a server) will be performed on the same [main] thread. Is my understanding correct? – ikevin8me Oct 02 '12 at 06:32
  • No, your understanding is wrong. The URL request is guaranteed to be called on a separate thread to the calling thread, otherwise it wouldn't be asynchronous. Look at the documentation for the _synchronous_ method - this still uses a separate thread, but blocks the calling thread until complete. – jrturton Oct 02 '12 at 06:38
  • Thanks, saw that! It mentions about a "asynchronous loading system" in the sendSynchronousRequest method. – ikevin8me Oct 02 '12 at 06:40
  • so, now the conlusion is: after the sendAsynchronousRequests is done and it calls the completionHandler, it uses a thread from the queue pool (NSOperationQueue). This thread can be whatever decided by the queue. Therefore, I suppose the "asynchronous loading system" is a queue pool by itself, and the supplied 'queue' (NSOperationQueue) is a different queue pool. Is my understanding correct now? – ikevin8me Oct 02 '12 at 06:43
  • I think so. I'm not sure, for your purposes, if you really need to distinguish between a queue and a thread, the key principle is that the URL request is performed asynchronously to the calling code, and the completion block is performed on the queue passed in as the method parameter. – jrturton Oct 02 '12 at 06:46
  • @jrturton Thanks, your description is the same as my understanding. – ikevin8me Oct 02 '12 at 07:05
  • I've put my last comment into my answer so it is more complete. – jrturton Oct 02 '12 at 10:00

4 Answers4

7

It executes it on whatever operation queue you specify as the queue argument:

Loads the data for a URL request and executes a handler block on an operation queue when the request completes or fails.

The queue parameter is documented as:

The operation queue to which the handler block is dispatched when the request completes or failed.

So it's really up to the NSOperationQueue exactly how many threads are used. I'd expect pooling behaviour - so while there can be multiple threads, I wouldn't expect a different thread per handler, necessarily.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I think answer is that queue (NSOperationQueue) will call the completionHandler block on yet another thread, right? (ie. the execution of the completionHandler will not be on the same thread used for the URL request) – ikevin8me Oct 02 '12 at 06:25
  • @ikevinjp: Again with the "yet another thread" without any context... it's a meaningless phrase. It probably won't be on the same thread as the calling code, that's correct. (I suppose it's just possible, if the calling code is executing in an `NSOperation` on the same queue.) You basically shouldn't make any assumptions about which thread it will execute on. – Jon Skeet Oct 02 '12 at 06:30
7

A block is just a closure, like you have them in python or functional languages. They don't "run on a thread" they run where they are called.

int main(void)
{
    void (^f)(void) { printf("hello world!\n"); }
    f();
    return 0;
}

Does just what you think it does, no dispatch queues, no threads, no nothing.

Though, once you have blocks with all their nice capture semantics, it's very tempting to have APIs to schedule their execution everywhere. But basically, a block, is just the same as a function pointer and an ad-hoc struct containing all the variable captured, passed as an argument to the callback (it's even how it's implemented in the compiler).

4

Blocks are executed wherever they are told. Wrapping code in a block does not affect the thread or queue it will be run on. In your particular case, as documented, the completion block is executed on the queue that is passed in in the queue parameter.

I'm not sure, for your purposes, if you really need to distinguish between a queue and a thread, the key principle is that the URL request is performed asynchronously to the calling code, and the completion block is performed on the queue passed in as the method parameter.

jrturton
  • 118,105
  • 32
  • 252
  • 268
4

As others have said, it will run on whatever queue you have specified. If this is a background queue, and you want to execute some code on the main thread, you can iclude a GCD block accessing the main queue. Your completion block would look something like this:

^(NSURLResponse *response, NSData *data, NSError*error){

   // do whatever in the background

   dispatch_async(dispatch_get_main_queue(), ^{
   // this block will run on the main thread
   });
}
Dima
  • 23,484
  • 6
  • 56
  • 83