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

avwztpqn  于 2023-02-20  发布在  iOS
关注(0)|答案(1)|浏览(131)

我在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) }
    }
}

有人能解释一下为什么编译器只抱怨在printScore()函数中捕获了var,而在ContentView结构体的body计算属性上使用task修饰符执行相同操作时却没有抱怨(即第14-24行)?
这是我想到的一个例子,我对编译器的行为感到困惑。我还将编译器设置“严格并发检查”的构建设置更改为“完成”,但仍然没有看到编译器抱怨。

mzsu5hc0

mzsu5hc01#

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

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

任务继承其调用者的上下文,因此这些任务也是MainActor。如果在body中将Task替换为Task.detached,您将看到相同的错误,因为这会将任务移出MainActor上下文。
相反,如果您将@MainActor添加到printScore,它也将编译无误:

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

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

score继承了MainActor指定,并且受到保护。没有对score的并发访问,因此这是正确的。
这实际上适用于所有参与者,但我认为编译器有一个bug,它使事情不像你期望的那样运行。

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

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

相关问题