Reputation: 6870
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
Upvotes: 1
Views: 458
Reputation: 122449
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.
Upvotes: 1
Reputation: 52538
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.
Upvotes: 1
Reputation: 8202
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.
Upvotes: 0
Reputation: 16946
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
.
Upvotes: 1