1

Does Option #1 below run some sort of implied queue? It does not seem to run on the main queue because when I tried to update UI stuff there it complained until I moved to Option #3 so I’m assuming that blocks have their own queue or thread? Before it complained I was under the impression if I didn’t start a dispatch queue that things would just run like normal, which in my mind would be on the main queue. Here’s some example code to illustrate:

// UserViewController.h
@interface UserViewController : NSObject
@property(nonatomic, strong) Server *server;
@property(nonatomic, strong) User *user;
@end

// UserViewController.m - Controller that sets a block for use in another class
@implementation UserViewController
- (void)doSomething {
  // I'd like to call other methods and set @properties from the controller and I've heard
  // __weak is the correct keyword to use (rather than __block or __strong).
  __weak UserViewController *weakController = self;

  // Option #0 - Outside of block
  weakController.user = [[RHZUser alloc] init];

  server.callbackBlock = ^(NSURLResponse *response, NSData *data, NSError *error) {

    // Option #1 - Outside of dispatch queues.  Is this in some sort of default queue?
    weakController.user = [[RHZUser alloc] init];

    dispatch_queue_t backgroundQueue
      = dispatch_queue_create("com.example.backgroundQueue", nil);

    dispatch_async(backgroundQueue, ^{

      // Option #2 - This is on the serial queue I created
      weakController.user = [[RHZUser alloc] init];

      dispatch_async(dispatch_get_main_queue(), ^{

        // Option #3 - The main queue where all my UI is
        weakController.user = [[RHZUser alloc] init];

      } // dispatch_async
    } // dispatch_async
  }; // self.callbackBlock
}
@end

// Server.m - Class that uses the block defined in the controller
@implementation Server
- makeAServerCall {
  [NSURLConnection sendAsynchronousRequest:
    [NSMutableURLRequest requestWithURL:restServiceURL]
                                  queue:[[NSOperationQueue alloc] init]
                      completionHandler:self.callbackBlock];
}
@end
TenaciousJay
  • 6,750
  • 3
  • 45
  • 48

4 Answers4

1

A block is a piece of code that, when executed, runs on a particular queue. Setting a block on some object does not make it run, nor does it get attached to one particular queue.

In the case of option 1, you set a block property on an instance of Server. This does not mean it's run, all it does is make that code accessible to anyone that has access to that block. Because the name of the property is callbackBlock, I'll assume that the Server instance executes the block when it finishes something.

That's when the block gets tied to a queue. Server's implementation decides whether it runs on the main queue or not, and should document (probably in its .h) whether it does or not. If it's not documented, but I absolutely need it run on the main queue, I always play it safe and make sure it gets called on the main thread by wrapping it in a dispatch_async.

EDIT:

Assuming your Server implementation is the same as Server's, you create a new queue using alloc/init and pass that to NSURLConnection. From the NSURLConnection documentation:

queue

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

So, the behavior is indeed documented and if you want the handler to be called on the main queue, simply pass dispatch_get_main_queue.

Community
  • 1
  • 1
Scott Berrevoets
  • 16,921
  • 6
  • 59
  • 80
  • Ha, I totally spaced where I alloc/init an NSOperationQueue that I pass to NSURLConnection (spent too much time staring at UserViewController.m). Totally makes sense that Option #1 runs on whatever queue is passed to NSURLConnection. I'm not sure I need it to run on dispatch_get_main_queue, but +1 for the suggestion. (newacct also mentioned NSOperationQueue, thanks newacct!) – TenaciousJay May 06 '14 at 17:53
1

You can just call a block. Same as a call using a function pointer; actually the same syntax as using a function pointer. That block will just run on the same thread as the caller.

dispatch_block_t myBlock = ^ { NSLog (@"This is a block!"); };
myBlock (); 

prints "This is a block!" on the same thread that the code is running on. callbacks will run on whatever thread they have been called on. So your "Option 1" block is performed on whatever queue the caller of that block decided to perform it on. Usually this should be documented by the method using the callback. There are some methods using callbacks that allow you to pass in a queue, and they will dispatch the callback on that queue.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
1

Blocks are just things that you can call like a function. They do not care about or do anything with threads and queues per se. Your question would be exactly the same if you had asked "Using Grand Central Dispatch on iOS, what queue (if any) do regular C functions run on if they are not in a dispatch queue?" or "Using Grand Central Dispatch on iOS, what queue (if any) do regular Objective-C methods run on if they are not in a dispatch queue?" The answer to that would be the answer to the title of this question.

So what's the answer to those questions? Well, a function (or a method, or a block) runs when you call it. It's that simple. So by definition it runs on whatever thread or queue you were in when you call it. So what thread then code in your block runs in depends on what thread the code that calls it is in. So how is the block called? The block is passed to +[NSURLConnection sendAsynchronousRequest:queue:completionHandler:];, and it is that code that somehow calls it. We don't have the code of that library, but its documentation says that the completion handler is executed in the NSOperationQueue passed in as the second argument. You pass a new NSOperationQueue object as the second argument.

An NSOperationQueue maintains an internal thread or dispatch queue on which the operations are run. You don't have access to and should not care about this internal queue; you just know that the operations are executed on something that is separate from the other queues and threads, so is definitely not the main thread.

newacct
  • 119,665
  • 29
  • 163
  • 224
0

Its possible that Server has incorrectly (in my opinion) implemented their call back blocks on a queue that is not the main queue. You can check if option #1 is definitely not on the main queue by checking [NSThread isMainThread];. You usually only change UIKit elements on the main thread (there are some exceptions - as always!).

Usually web service callback (or completion) blocks are sent back to the main thread as is the case for AFNetworking for example.

Rich
  • 8,108
  • 5
  • 46
  • 59
  • Any particular reason this was downvoted? I'd be interested to know why - have I missed or got something incorrect? – Rich Apr 24 '14 at 01:11
  • Thanks, I'll try to throw that code in and see if it's on the main thread or not. (P.S. Down vote wasn't from me.) – TenaciousJay Apr 24 '14 at 01:23
  • @TenaciousJay yeah I didn't think it was you...! Its always the way - you have found a solution to your problem anyway but just want to know why its happening so my answer is just words more than a solution! :P – Rich Apr 24 '14 at 01:27
  • Where is `doSomething` called from? – Rich Apr 24 '14 at 14:05
  • K, I checked. Option #0 and #3 was true for [NSThread isMainThread]. So Option #1 (the code in the block) is definitely running on a different thread, but not sure if that's in some sort of default queue or not. (Note: Option #0 was not originally in my question, I updated my answer to include it for clarity.) – TenaciousJay Apr 24 '14 at 14:07
  • doSomething is really a button, so the signature is - (IBAction)doSomething and gets called when a user clicks on a login button. I was just trying to simplify things so this didn't become a discussion on how to use IBAction too. – TenaciousJay Apr 24 '14 at 14:10
  • Ah, you updated the answer/comment - I was a bit confused for a second! – Rich Apr 24 '14 at 14:14
  • Sorry, the Server class should not have the prefix. I updated my question so you can update your answer to match it. Thanks. – TenaciousJay Apr 24 '14 at 15:21