104

我有一个接受块和完成块的方法。第一个块应该在后台运行,而完成块应该在调用该方法的任何队列中运行。

对于后者,我总是使用dispatch_get_current_queue(),但它似乎在 iOS 6 或更高版本中已被弃用。我应该改用什么?

4

7 回答 7

65

“在呼叫者所在的任何队列上运行”的模式很有吸引力,但最终不是一个好主意。该队列可以是低优先级队列、主队列或其他具有奇怪属性的队列。

我最喜欢的方法是说“完成块在具有以下属性的实现定义的队列上运行:x、y、z”,如果调用者想要更多控制权,则让块分派到特定队列。要指定的一组典型属性类似于“相对于任何其他应用程序可见队列的串行、不可重入和异步”。

** 编辑 **

Catfish_Man 在下面的评论中举了一个例子,我只是将它添加到他的答案中。

- (void) aMethodWithCompletionBlock:(dispatch_block_t)completionHandler     
{ 
    dispatch_async(self.workQueue, ^{ 
        [self doSomeWork]; 
        dispatch_async(self.callbackQueue, completionHandler); 
    } 
}
于 2012-11-05T19:07:57.790 回答
27

对于您所描述的 API,这从根本上是错误的方法。如果 API 接受块和完成块来运行,则以下事实必须为真:

  1. “要运行的块”应该在内部队列上运行,例如 API 私有的队列,因此完全在该 API 的控制之下。唯一的例外是 API 明确声明该块将在主队列或全局并发队列之一上运行。

  2. 完成块应始终表示为元组(队列,块),除非与#1 相同的假设成立,例如完成块将在已知的全局队列上运行。此外,完成块应该在传入队列上异步调度。

这些不仅仅是风格点,如果您的 API 要避免死锁或其他边缘情况行为,它们是完全必要的,否则有一天会将您从最近的树上吊死。:-)

于 2012-11-06T05:20:57.150 回答
15

其他答案很好,但对我来说,答案是结构性的。我有一个像这样的方法,在单例上:

- (void) dispatchOnHighPriorityNonMainQueue:(simplest_block)block forceAsync:(BOOL)forceAsync {
    if (forceAsync || [NSThread isMainThread])
        dispatch_async_on_high_priority_queue(block);
    else
        block();
}

它有两个依赖项,它们是:

static void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}

typedef void (^simplest_block)(void); // also could use dispatch_block_t

这样,我将调用集中在另一个线程上进行调度。

于 2013-08-12T23:21:34.213 回答
12

首先你应该小心你的使用dispatch_get_current_queue。从头文件:

建议仅用于调试和记录目的:

代码不得对返回的队列做出任何假设,除非它是全局队列之一或代码自己创建的队列。如果该队列不是 dispatch_get_current_queue() 返回的队列,则代码不能假定在队列上同步执行不会出现死锁。

你可以做以下两件事之一:

  1. 保留对您最初发布的队列的引用(如果您通过 创建它dispatch_queue_create),并从那时起使用它。

  2. 通过 使用系统定义的队列dispatch_get_global_queue,并跟踪您正在使用的队列。

实际上,虽然以前依靠系统来跟踪您所在的队列,但您将不得不自己做。

于 2012-11-05T18:18:39.740 回答
5

Apple 已经弃用dispatch_get_current_queue(),但在另一个地方留下了一个漏洞,所以我们仍然能够获得当前的调度队列:

if let currentDispatch = OperationQueue.current?.underlyingQueue {
    print(currentDispatch)
    // Do stuff
}

这至少适用于主队列。请注意,该underlyingQueue属性自 iOS 8 起可用。

如果你需要在原始队列中执行完成块,你也可以OperationQueue直接使用,我的意思是不用GCD。

于 2017-07-31T08:36:04.867 回答
4

对于那些仍然需要进行队列比较的人,您可以按其标签或指定来比较队列。检查这个https://stackoverflow.com/a/23220741/1531141

于 2014-04-22T13:55:44.997 回答
0

这也是我的答案。所以我将谈谈我们的用例。

我们有一个服务层和 UI 层(以及其他层)。服务层在后台运行任务。(数据操作任务、CoreData 任务、网络调用等)。服务层有几个操作队列来满足 UI 层的需求。

UI 层依赖于服务层来完成它的工作,然后运行一个成功完成块。这个块可以有 UIKit 代码。一个简单的用例是从服务器获取所有消息并重新加载集合视图。

在这里,我们保证传递到服务层的块在调用服务的队列上分派。由于 dispatch_get_current_queue 是一个不推荐使用的方法,我们使用 NSOperationQueue.currentQueue 来获取调用者的当前队列。关于此属性的重要说明。

从正在运行的操作的上下文之外调用此方法通常会导致返回 nil。

由于我们总是在已知队列(我们的自定义队列和主队列)上调用我们的服务,这对我们来说效果很好。我们确实有服务A可以调用服务B的情况,而服务B可以调用服务C。由于我们控制了第一个服务调用的来源,我们知道其余的服务将遵循相同的规则。

因此 NSOperationQueue.currentQueue 将始终返回我们的队列或 MainQueue 之一。

于 2018-04-03T22:55:39.537 回答