简体   繁体   English

SwiftUI的任务修饰符示例代码令人困惑

[英]The example code of task modifier of SwiftUI is confusing

Here is the code in Apple developer document。这是苹果开发者文档中的代码。

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"
            }
        }
}

Why don't it update message in the UI thread as code below为什么不更新 UI 线程中的message ,如下面的代码

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

Does the code in task block alway run in the UI thread?任务块中的代码是否总是在 UI 线程中运行?

Great question, It looks like a bug.好问题,它看起来像一个错误。 but in fact Apple's sample code is safe.但实际上苹果的示例代码是安全的。 But it is a safe for a sneaky reason.但出于一个偷偷摸摸的原因,它是一个保险箱。

Open a Terminal window and run this:打开终端 window 并运行:

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

The find command may take a while to finish, but it will eventually print a path like this: find命令可能需要一段时间才能完成,但它最终会打印出这样的路径:

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

Take a look at that swiftinterface file with less and search for func task .less查看那个swiftinterface文件并搜索func task You'll find the true definition of the task modifier.您将找到task修饰符的真正定义。 I'll reproduce it here and line-wrap it to make it easier to read:我将在这里复制它并换行以使其更易于阅读:

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

Notice that the action argument has the @_inheritActorContext attribute.请注意, action参数具有@_inheritActorContext属性。 That is a private attribute, but the Underscored Attributes Reference in the Swift repository explains what it does:这是一个私有属性,但 Swift 存储库中的Underscored Attributes Reference解释了它的作用:

Marks that a @Sendable async closure argument should inherit the actor context (ie what actor it should be run on) based on the declaration site of the closure.标记@Sendable async闭包参数应该根据闭包的声明站点继承参与者上下文(即它应该在哪个参与者上运行)。 This is different from the typical behavior, where the closure may be runnable anywhere unless its type specifically declares that it will run on a specific actor.这与典型的行为不同,其中闭包可以在任何地方运行,除非它的类型明确声明它将在特定的参与者上运行。

So the task modifier's action closure inherits the actor context surrounding the use of the task modifier.所以task修饰符的action闭包继承了围绕task修饰符使用的actor上下文。 The sample code uses the task modifier inside the body property of a View .示例代码在Viewbody属性中使用了task修饰符。 You can also find the true declaration of the body property in that swiftinterface file:您还可以在该swiftinterface文件中找到body属性的真实声明:

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

The body method has the MainActor attribute, which means it belongs to the MainActor context. body方法具有MainActor属性,这意味着它属于MainActor上下文。 MainActor runs on the main thread/queue. MainActor在主线程/队列上运行。 So using task inside body means the task closure also runs on the main thread/queue.所以在body内使用task意味着task闭包也在主线程/队列上运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM