对于iOS 6中的完成块,dispatch_get_current_queue()的替代方法是什么?

cfi*_*her 98 cocoa-touch objective-c grand-central-dispatch objective-c-blocks ios6

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

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

Cat*_*Man 63

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

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

**编辑**

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

- (void) aMethodWithCompletionBlock:(dispatch_block_t)completionHandler     
{ 
    dispatch_async(self.workQueue, ^{ 
        [self doSomeWork]; 
        dispatch_async(self.callbackQueue, completionHandler); 
    } 
}
Run Code Online (Sandbox Code Playgroud)

  • 完全同意.你可以看到Apple始终遵循这一点; 无论何时你想在主队列上做某事,总是需要调度到主队列,因为apple总是保证你在不同的线程上.大多数情况下,您正在等待一个长时间运行的过程来完成获取/操作数据,然后您可以在完成块中的后台处理它,然后只将UI调用粘贴到主队列上的调度块中.此外,随着开发人员习惯这种模式,遵循Apple的预期总是很好. (7认同)
  • - (void) aMethodWithCompletionBlock:(dispatch_block_t)completionHandler { dispatch_async(self.workQueue, ^{ [self doSomeWork]; dispatch_async(self.callbackQueue, completionHandler); } } (3认同)
  • 在一般情况下,这是不可能的,因为dispatch_sync()和dispatch_set_target_queue()可能(实际上很可能)同时在多个队列上.一般情况的一些子集是可能的. (3认同)

jkh*_*jkh 25

这基本上是您要描述的API的错误方法.如果API接受要运行的块和完成块,则需要满足以下事实:

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

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

这些不仅仅是风格上的点,如果您的API要安全,不受死锁或其他边缘情况的影响,它们将是完全必要的,否则有一天会让您从最近的树上挂起来.:-)

  • 听起来很合理,但由于某些原因,这不是Apple为自己的API采取的方法:大多数采用完成块的方法也不会排队...... (11认同)
  • 是的,如果很明显完成块将在主队列或全局并发队列上运行,那么稍微修改我之前的断言.我会改变我的答案以表明同样的意思. (2认同)

Dan*_*ark 14

其他答案很棒,但对我来说,答案是结构性的.我有一个像Singleton这样的方法:

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

它有两个依赖关系,它们是:

static void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}
Run Code Online (Sandbox Code Playgroud)

typedef void (^simplest_block)(void); // also could use dispatch_block_t
Run Code Online (Sandbox Code Playgroud)

这样我就可以将我的调用集中在另一个线程上.


WDU*_*DUK 12

你应该首先注意你的使用dispatch_get_current_queue.从头文件:

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

代码不得对返回的队列做任何假设,除非它是全局队列之一或代码本身创建的队列.如果该队列不是dispatch_get_current_queue()返回的队列,则代码不得假设同步执行到队列是安全的死锁.

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

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

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

在以前依靠系统跟踪您所在的队列的同时,您将不得不自己完成.

  • 如果我们不能使用`dispatch_get_current_queue()`来找出哪个队列,我们​​如何"保留对最初发布的队列的引用"?有时,需要知道正在运行哪个队列的代码对它没有任何控制或知识.我有很多代码可以(并且应该)在后台队列上执行但偶尔需要更新gui(进度条等),因此需要将dispatch_sync()传递给主队列以进行这些操作.如果已经在主队列中,dispatch_sync()将永远锁定.我需要几个月的时间来重构我的代码. (15认同)
  • 我认为NSURLConnection在它调用的同一个线程上给出了完成回调.它是否会使用相同的API"dispatch_get_current_queue"来存储调用的队列,以便在回调时使用? (3认同)

kel*_*lin 5

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

if let currentDispatch = OperationQueue.current?.underlyingQueue {
    print(currentDispatch)
    // Do stuff
}
Run Code Online (Sandbox Code Playgroud)

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

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