如何在Swift 3中获取当前队列名称

25

在Swift 2.2中,我们有如下函数用于打印带有当前运行线程的日志消息:

func MyLog(_ message: String) {
    if Thread.isMainThread {
        print("[MyLog]", message)
    } else {
        let queuename = String(UTF8String: dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))! // Error: Cannot convert value of type '()' to expected argument type 'DispatchQueue?'
        print("[MyLog] [\(queuename)]", message)
    }
}

这些代码在Swift 3.0中无法编译。现在我们如何获取队列名称?


2
可能是在Swift 3中检查正确的调度队列的重复问题。 - Ozgur Vatansever
2
现在DispatchQueuelabel属性 - eonil
2
@Eonil:那应该发布为一个“答案”。 - Martin R
@MartinR 谢谢。我已经发布了。 - eonil
这个答案对我有用:https://dev59.com/KFoT5IYBdhLWcg3w-TVV#58738439 - Peter Schorn
6个回答

54

正如 Brent Royal-Gordon 在他在lists.swift.org上的邮件中提到的那样,这是当前设计中的一个漏洞,但你可以使用这个可怕的解决方法。

func currentQueueName() -> String? {
    let name = __dispatch_queue_get_label(nil)
    return String(cString: name, encoding: .utf8)
}

刚刚尝试了这个解决方案,但是执行每一行代码都需要额外的时间,大约1-2秒。是这样吗? - Yogi

8

如果您不喜欢不安全的指针和C字符串,还有另一种安全的解决方案:

if let currentQueueLabel = OperationQueue.current?.underlyingQueue?.label {
    print(currentQueueLabel)
    // Do something...
}

我不知道什么情况下 currentQueueLabel 会是 nil


这个解决方案似乎不能正常工作。对于相同的队列,OperationQueue.current?.underlyingQueue?.label 返回“com.apple.main-thread”,而 String(cString: __dispatch_queue_get_label(nil), encoding: .utf8) 返回“myDispatchQueueLabel”。 - dodgio
根据苹果文档,如果您当前不在操作的上下文中,则“current”几乎肯定会返回nil。 - jakehawken
1
@jakehawken,我试过了。我只是在didFinishLaunchingWithOptions中放置了print(OperationQueue.current),而且current不是空的。它看起来像这样:<NSOperationQueue: 0x14cd38dd0>{name = 'NSOperationQueue Main Queue'}。所以,可能一切都在某个操作的上下文中。 - kelin
@kelin,听到这个消息我感到非常惊喜!我之前是根据苹果的文档来做出判断的。但现在很高兴听到这个消息。这很有用! - jakehawken
3
这只适用于为操作队列专门创建的调度队列,并且仅在这些调度队列当前处于活动状态(因为它们所属的操作队列正在执行操作)时才会触发。如果不满足这两个条件,您将获得一个nil标签。例如:您自己的调度队列或您在任何队列中提交的块期间。 - lhunath
显示剩余3条评论

3

现在DispatchQueue具有标签属性

这是您在创建时分配给调度队列的标签。

var label: String { get } 

这个功能可能一直存在,只是没有通过公共API暴露出来。


macOS 10.10+

请仅使用此选项获取可读标签,而不是用于识别每个GCDQ。

如果您想检查代码是否运行在特定的GCDQ上,您可以使用 dispatchPrecondition(...)函数


@yuchen-zhong 这个回答解决了你的问题吗?我知道这可能有帮助,但仍然没有更多的方法来获取当前队列。 - Klaas
@Klaas 根据我所知,“获取当前队列对象”是被禁止和强烈不推荐的做法。我认为这背后有几个潜在的意图... - eonil
我知道,但是楼主要求使用它。有没有什么理由不能将其用于调试目的?(我知道https://developer.apple.com/documentation/dispatch/1780605-dispatchprecondition) - Klaas

1

这种方法可以同时适用于 OperationQueueDispatchQueue

func printCurrnetQueueName()
{
    print(Thread.current.name!)
} 

1

这是一个包装类,提供了一些安全性(修订自这里):

import Foundation

/// DispatchQueue wrapper that acts as a reentrant to a synchronous queue;
/// so callers to the `sync` function will check if they are on the current
/// queue and avoid deadlocking the queue (e.g. by executing another queue
/// dispatch call). Instead, it will just execute the given code in place.
public final class SafeSyncQueue {

    public init(label: String, attributes: DispatchQueue.Attributes) {
        self.queue = DispatchQueue(label: label, attributes: attributes)
        self.queueKey = DispatchSpecificKey<QueueIdentity>()
        self.queue.setSpecific(key: self.queueKey, value: QueueIdentity(label: self.queue.label))
    }

    // MARK: - API

    /// Note: this will execute without the specified flags if it's on the current queue already
    public func sync<T>(flags: DispatchWorkItemFlags? = nil, execute work: () throws -> T) rethrows -> T {
        if self.currentQueueIdentity?.label == self.queue.label {
            return try work()
        } else if let flags = flags {
            return try self.queue.sync(flags: flags, execute: work)
        } else {
            return try self.queue.sync(execute: work)
        }
    }

    // MARK: - Private Structs

    private struct QueueIdentity {
        let label: String
    }

    // MARK: - Private Properties

    private let queue: DispatchQueue
    private let queueKey: DispatchSpecificKey<QueueIdentity>

    private var currentQueueIdentity: QueueIdentity? {
        return DispatchQueue.getSpecific(key: self.queueKey)
    }

}

2
通常情况下,将DispatchSpecificKey创建为实例变量是不安全的。键通过它们在内存中的地址进行比较,如果您创建并释放多个键,则它们可能会共享相同的指针。请参阅我的帖子以了解此问题:http://tom.lokhorst.eu/2018/02/leaky-abstractions-in-swift-with-dispatchqueue - Tom Lokhorst
有趣,谢谢@TomLokhorst!猜测这里最好使用静态常量,因为它们是线程安全的。 - iwasrobbed

0

这对我来说效果最好:

/// The name/description of the current queue (Operation or Dispatch), if that can be found. Else, the name/description of the thread.
public func queueName() -> String {
    if let currentOperationQueue = OperationQueue.current {
        if let currentDispatchQueue = currentOperationQueue.underlyingQueue {
            return "dispatch queue: \(currentDispatchQueue.label.nonEmpty ?? currentDispatchQueue.description)"
        }
        else {
            return "operation queue: \(currentOperationQueue.name?.nonEmpty ?? currentOperationQueue.description)"
        }
    }
    else {
        let currentThread = Thread.current
        return "UNKNOWN QUEUE on thread: \(currentThread.name?.nonEmpty ?? currentThread.description)"
    }
}



public extension String {

    /// Returns this string if it is not empty, else `nil`.
    public var nonEmpty: String? {
        if self.isEmpty {
            return nil
        }
        else {
            return self
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接