3

这是苹果开发者文档中的代码。</p>

let url = URL(string: "https://example.com")!
@State private var message = "Loading..."

var body: some View {
    Text(message)
        .task {
            do {
                var receivedLines = [String]()
                for try await line in url.lines {
                    receivedLines.append(line)
                    message = "Received \(receivedLines.count) lines"
                }
            } catch {
                message = "Failed to load"
            }
        }
}

为什么不在messageUI 线程中更新如下代码

DispatchQueue.main.async {
    message = "Received \(receivedLines.count) lines"
}

任务块中的代码是否总是在 UI 线程中运行?

这是我的测试代码。有时似乎任务没有继承其调用者的参与者上下文。

func wait() async {
    await Task.sleep(1000000000)
}

Thread.current.name = "Main thread"
print("Thread in top-level is \(Thread.current.name)")

Task {
    print("Thread in task before wait is \(Thread.current.name)")
    if Thread.current.name!.isEmpty {
        Thread.current.name = "Task thread"
        print("Change thread name \(Thread.current.name)")
    }
    await wait()
    print("Thread in task after wait is \(Thread.current.name)")

}

Thread.sleep(until: .now + 2)

// print as follow

// Thread in top-level is Optional("Main thread")
// Thread in task before wait is Optional("")
// Change thread name Optional("Task thread")
// Thread in task after wait is Optional("")

任务中的线程前后不同wait()

任务中的线程不是Main thread

4

1 回答 1

2

好问题!它看起来像一个错误,但实际上 Apple 的示例代码是安全的。但出于一个偷偷摸摸的原因,它是一个保险箱。

打开终端窗口并运行:

cd /Applications/Xcode.app
find . -path */iPhoneOS.platform/*/SwiftUI.swiftmodule/arm64.swiftinterface

find命令可能需要一段时间才能完成,但最终会打印出如下路径:

./Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/SwiftUI.framework/Modules/SwiftUI.swiftmodule/arm64.swiftinterface

查看该swiftinterface文件less并搜索func task. 你会发现修饰符的真正定义。task我将在这里复制它并换行以使其更易于阅读:

  @inlinable
  public func task(
    priority: _Concurrency.TaskPriority = .userInitiated,
    @_inheritActorContext
    _ action: @escaping @Sendable () async -> Swift.Void
  ) -> some SwiftUI.View {
    modifier(_TaskModifier(priority: priority, action: action))
  }

请注意,action参数具有@_inheritActorContext属性。这是一个私有属性,但Underscored Attributes ReferenceSwift 存储库中的 解释了它的作用:

标记@Sendable async闭包参数应该基于闭包的声明站点继承actor上下文(即它应该在哪个actor上运行)。这与典型的行为不同,其中闭包可以在任何地方运行,除非它的类型明确声明它将在特定的actor上运行。

所以task修饰符的action闭包继承了修饰符使用周围的演员上下文task。示例代码在 a 的属性task内使用修饰符。您还可以在该文件中找到该属性的真实声明:bodyViewbodyswiftinterface

  @SwiftUI.ViewBuilder @_Concurrency.MainActor(unsafe) var body: Self.Body { get }

body方法具有MainActor属性,这意味着它属于MainActor上下文。MainActor在主线程/队列上运行。所以使用taskinsidebody意味着task闭包也在主线程/队列上运行。

于 2021-12-10T20:47:40.937 回答