[英]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
.示例代码在
View
的body
属性中使用了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.