在iOS上使用Grand Central Dispatch,如果常规Objective-C块不在调度队列中,那么它们在哪



下面的选项#1是否运行某种隐含队列?它似乎没有在主队列上运行,因为当我试图在那里更新UI时,它一直在抱怨,直到我转到选项#3,所以我假设块有自己的队列或线程?在它抱怨之前,我有一种印象,如果我不启动一个调度队列,事情就会像正常一样运行,在我看来,它会在主队列上。下面是一些示例代码:

// 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

块是一段执行时在特定队列上运行的代码。在某个对象上设置块不会使其运行,也不会附加到某个特定队列。

在选项1的情况下,可以在Server的实例上设置块特性。这并不意味着它已经运行,它所做的只是让任何有权访问该块的人都可以访问该代码。因为属性的名称是callbackBlock,所以我假设Server实例在完成某个操作时执行该块。

这是块绑定到队列时的情况。Server的实现决定了它是否在主队列上运行,并且应该记录(可能在其.h中)它是否运行。如果它没有文档化,但我绝对需要它在主队列上运行,我总是保持安全,并通过将它封装在dispatch_async中来确保它在主线程上被调用。

编辑:

假设Server的实现与Server的实现相同,则使用alloc/init创建一个新队列,并将其传递给NSURLConnection。来自NSURLConnection文档:

队列

当请求完成或失败时,处理程序块被调度到的操作队列。

因此,行为确实是有文档记录的,如果您希望在主队列上调用处理程序,只需传递dispatch_get_main_queue即可。

您可以直接调用一个块。与使用函数指针的调用相同;实际上与使用函数指针的语法相同。该块将只在与调用方相同的线程上运行。

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

在代码运行的同一线程上打印"这是一个块!"。回调将在调用它们的任何线程上运行。因此,您的"选项1"块将在该块的调用方决定执行它的任何队列上执行。通常,这应该通过使用回调的方法来记录。有一些方法使用回调,允许您传入队列,它们将在该队列上调度回调。

块只是可以像函数一样调用的东西。它们不关心线程和队列本身,也不做任何事情。如果你问过"在iOS上使用Grand Central Dispatch,如果常规C函数不在调度队列中,它们会在哪个队列(如果有的话)上运行?"或"在iOS中使用Grand Central Dispatch。如果常规Objective-C方法不在调度列队中,它们在哪个队列中运行?"你的问题会完全一样。答案就是这个问题标题的答案。

那么这些问题的答案是什么呢?好吧,一个函数(或方法,或块)在你调用它的时候就会运行。就这么简单。因此,根据定义,它运行在您调用它时所在的任何线程或队列上。因此,块中的代码运行在哪个线程中取决于调用它的代码所在的线程。那么,块是如何调用的呢?该块被传递给+[NSURLConnection sendAsynchronousRequest:queue:completionHandler:];,正是该代码以某种方式调用了它。我们没有该库的代码,但它的文档说,完成处理程序是在作为第二个参数传递的NSOperationQueue中执行的。您传递一个新的NSOperationQueue对象作为第二个参数。

NSOperationQueue维护运行操作的内部线程或调度队列。您没有访问权限,也不应该关心此内部队列;您只知道这些操作是在与其他队列和线程分离的东西上执行的,因此绝对不是主线程。

Server可能(在我看来)在不是主队列的队列上错误地实现了它们的回调块。您可以通过检查[NSThread isMainThread];来检查选项#1是否肯定不在主队列中。您通常只更改主线程上的UIKit元素(也有一些例外——一如既往!)。

通常,web服务回调(或完成)块被发送回主线程,例如AFNetworking就是这样。

最新更新