在并发执行的代码中引用捕获的 var

AKI*_*MAR 4 ios async-await swift swiftui swift-concurrency

我正在快速尝试新的异步/等待模式,但遇到了一些让我感到困惑的事情。

\n
struct ContentView: View {\n    var body: some View  {\n        Text("Hello World!")\n            .task {\n                var num = 1\n\n                Task {\n                    print(num)\n                }\n\n                Task {\n                    print(num)\n                }\n            }\n    }\n\n    func printScore() async {\n        var score = 1\n\n        Task { print(score) }\n        Task { print(score) }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在此输入图像描述

\n

有人可以澄清一下上面的屏幕截图,为什么编译器只抱怨捕获的var内部函数,而在使用结构体的计算属性上的修饰符printScore() 执行相同操作时不抱怨(即第 14-24 行)?taskbodyContentView

\n

这是我提出的示例,并对编译器行为感到困惑。我还将编译器设置“严格并发检查\xe2\x80\x9d 构建设置”更改为\xe2\x80\x9cComplete\xe2\x80\x9d,但仍然不这样做没有看到编译器抱怨。

\n

Rob*_*ier 7

这是一种特殊的力量@MainActor。在视图中,body被标记为 MainActor:

@ViewBuilder @MainActor var body: Self.Body { get }
Run Code Online (Sandbox Code Playgroud)

任务继承其调用者的上下文,因此它们也是 MainActor。如果替换TaskTask.detachedin body,您将看到相同的错误,因为这会将任务移出 MainActor 上下文。

相反,如果添加@MainActorprintScore也将编译无错误:

@MainActor func printScore() async {
    var score = 1

    Task { print(score) }
    Task { print(score) }
}
Run Code Online (Sandbox Code Playgroud)

score继承 MainActor 名称并受到保护。没有并发访问score,因此这是正确的。

这实际上适用于所有参与者,但我相信编译器有一个错误,使事情的行为不太符合您的预期。以下代码编译:

actor A {
    var actorVar = 1
    func capture() {
        var localVar = 1
        Task {
            print(actorVar)
            print(localVar)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果删除对 的引用actorVar,则传递给 Task 的闭包将不会放入参与者的上下文中,这将使引用localVar无效。IMO,这是一个编译器错误