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

4
我在尝试使用Swift中的新异步/等待模式时遇到了一些让我感到困惑的事情。
struct ContentView: View {
    var body: some View  {
        Text("Hello World!")
            .task {
                var num = 1

                Task {
                    print(num)
                }

                Task {
                    print(num)
                }
            }
    }

    func printScore() async {
        var score = 1

        Task { print(score) }
        Task { print(score) }
    }
}

enter image description here

有人能否解释一下为什么编译器只在 printScore() 函数中捕获的 var 抱怨,而在使用 ContentView 结构体的 body 计算属性上使用相同的方法时却没有任何抱怨(即第14-24行)?这是我想出来的例子,对编译器的行为感到困惑。我还将编译器设置的“Strict Concurrency Checking”构建设置更改为“Complete”,但仍然看不到编译器抱怨。

不需要使用 Task,任何一个都不需要。你只需要 .task - lorem ipsum
这是真的,但仅仅出于实验性质和例子的乐趣,为什么body计算属性内部没有编译器错误呢? - AKIL KUMAR
1个回答

7

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

@ViewBuilder @MainActor var body: Self.Body { get }

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

相反,如果您在printScore上添加@MainActor,它也将编译不带错误:

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

    Task { print(score) }
    Task { print(score) }
}

score继承了MainActor的指定,并被保护。没有对score的并发访问,因此这是正确的。

实际上,所有的actor都适用于这种方式,但我相信编译器存在一个bug,使得事情不完全按你所期望的方式运行。以下代码可以编译:

actor A {
    var actorVar = 1
    func capture() {
        var localVar = 1
        Task {
            print(actorVar)
            print(localVar)
        }
    }
}

然而,如果您删除对 actorVar 的引用,则传递给 Task 的闭包将不会被放置到 actor 的上下文中,这将使对 localVar 的引用无效。 在我看来,这是一个编译器 Bug


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