DispatchQueue的闭包是 @Sendable的,它如何访问由主Actor隔离的值?

移动开发 2026-07-09

先尝试这段代码:

func viewDidLoad() {
  DispatchQueue.global().async {
    self.view.backgroundColor = red
  }
}

这在Swift 6中显然会失败,因为闭包是 @Sendable,也就是说它可以在后台线程中被调用,因此无法访问被主actor隔离的值(self.view

有趣的部分在这里:

func viewDidLoad() {
  DispatchQueue.main.async {
    self.view.backgroundColor = red
  }
}

这段代码可以正常编译。

然而,若你查看API,DispatchQueue.global() 和DispatchQueue.main是相同类型(DispatchQueue),它们都接受一个 @Sendable 闭包。主队列会不会神奇地把 @Sendable 闭包变成一个 @MainActor 闭包?

解决方案

DispatchQueue.main.async 只是被编译器硬编码的一个特例。这个检查由函数 isMainDispatchQueueMember 来完成,它只是查看AST,看看是否匹配“DispatchQueue.main”。如果匹配,编译器最终会把闭包的类型调整为对主actor进行隔离,从而使闭包具备可发送性。

你可以从 这一行 开始,跟踪编译器的源代码,看看它最终如何达到 applyUnsafeConcurrencyToParameterType

因为这只是一个AST检查,以不同但等效的方式写 DispatchQueue.main 都会导致检查失败,并产生你预期的警告/错误。

let queue = DispatchQueue.main
queue.async {
    mainActorFunc() // warning
}

typealias DP = DispatchQueue
DP.main.async {
    mainActorFunc() // warning
}

另一方面,如果你定义一个名为 DispatchQueue 的自定义类型,并为它添加一个 main 属性和一个 async 方法,那么就不会有任何警告!

struct DispatchQueue {
    static let main = DispatchQueue()
    func async(block: @Sendable () -> Void) {

    }
}

DispatchQueue.main.async {
    mainActorFunc() // totally fine by the compiler
}

另请参阅:https://oleb.net/2024/dispatchqueue-mainactor/

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章