我有一个方法应该支持从任何队列中调用,并且应该期望。
它在后台线程本身中运行一些代码,然后dispatch_get_main_queue
在向其块参数返回值时使用。
如果它不是在它进入方法时,我不希望它强制它进入主队列。有没有办法获得指向当前调度队列的指针?
我有一个方法应该支持从任何队列中调用,并且应该期望。
它在后台线程本身中运行一些代码,然后dispatch_get_main_queue
在向其块参数返回值时使用。
如果它不是在它进入方法时,我不希望它强制它进入主队列。有没有办法获得指向当前调度队列的指针?
如果您正在使用NSOperationQueue
,它可以为您提供当前的调度队列。
NSOperationQueue 具有类函数[NSOperationQueue currentQueue]
,它将当前队列作为 NSOperationQueue 对象返回。要获取您可以使用的调度队列对象[NSOperationQueue currentQueue].underlyingQueue
,它将您当前的队列作为dispatch_queue_t
.
斯威夫特 3:
if let currentDispatch = OperationQueue.current?.underlyingQueue {
print(currentDispatch)
}
- 适用于主队列!
随着弃用,dispatch_get_current_queue()
实际上无法知道您正在执行哪个队列。如果您仔细阅读GCD 源代码,您最终会发现这是因为“我在哪个队列上执行?”这个问题可能有多个答案。(因为队列最终以全局队列之一为目标,等等)
如果您想保证未来块在特定队列上运行,那么唯一的方法是让您的 API 接受队列作为参数以及完成块。这让调用者可以决定在哪里执行完成。
如果仅仅知道调用者是否在主线程上就足够了,你可以用它+[NSThread isMainThread]
来找出。在一般情况下,在主 GCD 队列上执行的所有块都将在主线程上执行。(此规则的一个例外是,如果您的应用程序使用dispatch_main()
代替主运行循环,您将不得不使用dispatch_get_specific
和朋友来确定您正在主队列上执行 - 这是一种相对罕见的情况。)更常见,请注意,并非所有在主线程上执行的代码都通过 GCD 在主队列上执行;GCD 从属于主线程 runloop。对于您的具体情况,这听起来可能就足够了。
您确实可以选择" dispatch_get_current_queue()
",但是 iOS 6.1 SDK 使用以下免责声明定义了此 API:
" Recommended for debugging and logging purposes only:
"
和
“ This function is deprecated and will be removed in a future release.
”。
这是另一个相关问题,如果您想要面向未来的代码,您可以考虑一些替代方案。
随着您的弃用,dispatch_get_current_queue()
您无法直接获得指向您正在运行的队列的指针,但是您可以通过调用获得当前队列的标签dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)
,这确实为您提供了一些灵活性。
您始终可以通过比较它们的标签来检查您是否在该特定队列中,因此在您的情况下,如果您不想在主队列上强制它,当您输入该方法时,您可以使用以下标志:
let isOnMainQueue = (dispatch_queue_get_label(dispatch_get_main_queue()) == dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))
如果您在全局队列上运行,您将获得与其 QOS 类型相关联的队列标签,该标签可以是以下之一:
com.apple.root.user-interactive-qos //qos_class_t(rawValue: 33)
com.apple.root.user-initiated-qos //qos_class_t(rawValue: 25)
com.apple.root.default-qos //qos_class_t(rawValue: 21)
com.apple.root.utility-qos //qos_class_t(rawValue: 17)
com.apple.root.background-qos //qos_class_t(rawValue: 9)
然后您可以使用dispatch_get_global_queue(qos_class_self(), 0)
which 将返回您正在运行的同一个全局队列。
但我相信 Apple 特别不鼓励我们将逻辑限制在我们被调用的队列中,因此最好将其用于专门的调试目的。
基于奥列格·巴里诺夫的回答
import Foundation
// MARK: private functionality
extension DispatchQueue {
private struct QueueReference { weak var queue: DispatchQueue? }
private static let key: DispatchSpecificKey<QueueReference> = {
let key = DispatchSpecificKey<QueueReference>()
setupSystemQueuesDetection(key: key)
return key
}()
private static func _registerDetection(of queues: [DispatchQueue], key: DispatchSpecificKey<QueueReference>) {
queues.forEach { $0.setSpecific(key: key, value: QueueReference(queue: $0)) }
}
private static func setupSystemQueuesDetection(key: DispatchSpecificKey<QueueReference>) {
let queues: [DispatchQueue] = [
.main,
.global(qos: .background),
.global(qos: .default),
.global(qos: .unspecified),
.global(qos: .userInitiated),
.global(qos: .userInteractive),
.global(qos: .utility)
]
_registerDetection(of: queues, key: key)
}
}
// MARK: public functionality
extension DispatchQueue {
static func registerDetection(of queue: DispatchQueue) {
_registerDetection(of: [queue], key: key)
}
static var currentQueueLabel: String? { current?.label }
static var current: DispatchQueue? { getSpecific(key: key)?.queue }
}
检测系统队列
DispatchQueue.currentQueueLabel
DispatchQueue.current
DispatchQueue.global(qos: .default) == DispatchQueue.current
DispatchQueue.main === DispatchQueue.current
检测自定义队列
let queue = DispatchQueue(label: "queue-sample")
DispatchQueue.registerDetection(of: queue)
if DispatchQueue.current == queue { ... }
func subTest(queue: DispatchQueue) {
queue.async {
print("--------------------------------------------------------")
print("queue label: \(DispatchQueue.currentQueueLabel ?? "nil")")
print("print DispatchQueue.current: \(String(describing: DispatchQueue.current))")
print("print queue == DispatchQueue.current: \(queue == DispatchQueue.current)")
print("print queue === DispatchQueue.current: \(queue === DispatchQueue.current)")
print("DispatchQueue.main == DispatchQueue.current: \(DispatchQueue.main == DispatchQueue.current)\n")
}
}
func test() {
subTest(queue: DispatchQueue.main)
sleep(1)
subTest(queue: DispatchQueue.global(qos: .default))
sleep(1)
subTest(queue: DispatchQueue.global(qos: .utility))
sleep(1)
let queue = DispatchQueue(label: "queue-sample")
DispatchQueue.registerDetection(of: queue)
subTest(queue: queue)
sleep(1)
}
test()
DispatchQueue.global(qos: .default).async {
test()
}
--------------------------------------------------------
queue label: com.apple.root.default-qos
print DispatchQueue.current: Optional(<OS_dispatch_queue_global: com.apple.root.default-qos[0x7fff89eb47c0] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: false
--------------------------------------------------------
queue label: com.apple.root.utility-qos
print DispatchQueue.current: Optional(<OS_dispatch_queue_global: com.apple.root.utility-qos[0x7fff89eb46c0] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: false
--------------------------------------------------------
queue label: queue-sample
print DispatchQueue.current: Optional(<OS_dispatch_queue_serial: queue-sample[0x600000275780] = { xref = 7, ref = 3, sref = 2, target = com.apple.root.default-qos.overcommit[0x7fff89eb4840], width = 0x1, state = 0x0060002500000b01, enqueued, max qos 5, draining on 0xb03, in-barrier}>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: false
--------------------------------------------------------
queue label: com.apple.main-thread
print DispatchQueue.current: Optional(<OS_dispatch_queue_main: com.apple.main-thread[0x7fff89eb43c0] = { xref = -2147483648, ref = -2147483648, sref = 1, target = com.apple.root.default-qos.overcommit[0x7fff89eb4840], width = 0x1, state = 0x001ffe9000000300, dirty, in-flight = 0, thread = 0x303 }>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: true
--------------------------------------------------------
queue label: com.apple.main-thread
print DispatchQueue.current: Optional(<OS_dispatch_queue_main: com.apple.main-thread[0x7fff89eb43c0] = { xref = -2147483648, ref = -2147483648, sref = 1, target = com.apple.root.default-qos.overcommit[0x7fff89eb4840], width = 0x1, state = 0x001ffe9000000300, dirty, in-flight = 0, thread = 0x303 }>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: true
--------------------------------------------------------
queue label: com.apple.root.default-qos
print DispatchQueue.current: Optional(<OS_dispatch_queue_global: com.apple.root.default-qos[0x7fff89eb47c0] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: false
--------------------------------------------------------
queue label: com.apple.root.utility-qos
print DispatchQueue.current: Optional(<OS_dispatch_queue_global: com.apple.root.utility-qos[0x7fff89eb46c0] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: false
--------------------------------------------------------
queue label: queue-sample
print DispatchQueue.current: Optional(<OS_dispatch_queue_serial: queue-sample[0x60000027a280] = { xref = 7, ref = 3, sref = 2, target = com.apple.root.default-qos.overcommit[0x7fff89eb4840], width = 0x1, state = 0x0060002500000b01, enqueued, max qos 5, draining on 0xb03, in-barrier}>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: false
基于来自SQLite.swift的源代码。
如果您想检查您是否在自己的特殊调度队列中:
class Worker {
private static let queueKey = DispatchSpecificKey<Int>()
private lazy var queueContext = unsafeBitCast(self, to: Int.self)
private lazy var queue: DispatchQueue = {
let value = DispatchQueue(label: "com.example.App.Worker")
value.setSpecific(key: Worker.queueKey, value: queueContext)
return value
}()
func test(x: Int) -> Int {
return dispatchSync {
return x > 2 ? test(x: x - 1) * x : x
}
}
private func dispatchSync<T>(_ block: () throws -> T) rethrows -> T {
if DispatchQueue.getSpecific(key: Worker.queueKey) != queueContext {
return try queue.sync(execute: block)
}
return try block()
}
}
let worker = Worker()
worker.test(x: 5)
如果您只对当前 QOS 感兴趣,请检查Thread.current.qualityOfService
.
作为 this 方法的另一种NSOBject
方法performSelector:withObject:afterDelay:在当前线程的运行循环上调度调用。根据文档:
此方法设置一个计时器以在当前线程的运行循环上执行 aSelector 消息。
显然,我建议在延迟为零的情况下使用它,再次根据文档:
指定延迟为 0 不一定会导致选择器立即执行。选择器仍在线程的运行循环中排队并尽快执行。
不幸的是,它只需要一个参数,因此如果您的方法需要更多或更少,则可能需要一些解决方法。
我注意到的另一件事是,此方法不适用于协议,而仅适用于实现。这是由于此方法存在于一个NSObject
类别中,而不是在NSObject
界面中(参见下面的 PS)。这可以通过转换为 轻松修复id
。
PS:NSObject
存在两个不同的,一个协议和一个实现。通知NSObject
声明:
@interface NSObject <NSObject> { ... }
这可能看起来很奇怪,但是一个正在被声明(在 之后@interface
),另一个是之前声明的协议(在<
and之间>
)。当声明一个扩展 NSObject 的协议(即@protocol Foo <NSObject>
)时,该协议继承了后者的方法,但不是前者。最终,该协议由某个继承自实现的类NSObject
实现,因此从实现继承的所有实例NSObject
仍然成立。但我跑题了。
其实还是有办法比较队列的。
设置队列时,请确保添加标签。出于我的目的,我有一个共享队列,用于访问数据库以防止数据库锁定。在我的 DB.m 文件中,我定义了共享队列函数,例如:
const char *kTransactionQueueLabel = "DB_TRANSACTION_DISPATCH_QUEUE";
+ (dispatch_queue_t)sharedDBTransactionQueue {
static dispatch_queue_t sharedDBQueue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedDBQueue = dispatch_queue_create(kTransactionQueueLabel, DISPATCH_QUEUE_SERIAL);
});
return sharedDBQueue;
}
共享数据库事务队列在文件中本地使用,以将所有执行分派到数据库。但是,还有一个公共访问器允许将整个事务分派到数据库。所以在内部,如果从事务队列中调用 DB 访问方法,我们需要在内部调度不同的队列(所有同步调度)。所以在内部,我总是使用下面的 getter 在正确的队列上调度。
/**
* @description Decide which queue to use - if we are already in a transaction, use the internal access queue, otherwise use the shared transaction queue.
*/
- (dispatch_queue_t)getProperQueueForExecution {
const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
dispatch_queue_t sharedAccessQueue = [DB sharedDBTransactionQueue];
if (strcmp(currentLabel, kTransactionQueueLabel) == 0) {
sharedAccessQueue = [DB sharedInternalDBAccessQueue];
}
return sharedAccessQueue;
}
希望这会有所帮助。对不起,这个例子太长了。它的要点是你可以使用
const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
获取当前队列的标签并与定义的标签进行比较。
我有与原始帖子提到的相同的功能要求。您应该能够在任何队列上调用此异步函数,但如果在主队列上调用,则在主队列上回调给用户。我只是这样处理它:
// cache value for if we should callback on main queue
BOOL callbackOnMT = [NSThread isMainThread];
// ...
// ... do async work...
// ...
if (callbackOnMT && ![NSThread isMainThread]){
dispatch_async(dispatch_get_main_queue(), ^{
// callback to user on main queue
// as they called this function on main queue
callbackToUser();
});
}
else{
// callback to user on our current queue
// as they called this function on a non-main queue
callbackToUser();
}
获取当前队列的标签并与使用定义的标签进行比较。
let queueName = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)