我有一个 ObservableObject
类和一个 SwiftUI 视图。当点击按钮时,我创建了一个 Task
并在其中调用 populate
(一个异步函数)。我认为这会在后台线程上执行 populate
,但实际上整个 UI 都会冻结。这是我的代码:
class ViewModel: ObservableObject {
@Published var items = [String]()
func populate() async {
var items = [String]()
for i in 0 ..< 4_000_000 { /// this usually takes a couple seconds
items.append("\(i)")
}
self.items = items
}
}
struct ContentView: View {
@StateObject var model = ViewModel()
@State var rotation = CGFloat(0)
var body: some View {
Button {
Task {
await model.populate()
}
} label: {
Color.blue
.frame(width: 300, height: 80)
.overlay(
Text("\(model.items.count)")
.foregroundColor(.white)
)
.rotationEffect(.degrees(rotation))
}
.onAppear { /// should be a continuous rotation effect
withAnimation(.easeInOut(duration: 2).repeatForever()) {
rotation = 90
}
}
}
}
结果:
![当按下按钮时,旋转动画会冻结](https://istack.dev59.com/TnDMi.gif)
按钮停止移动,然后在populate
完成后突然弹回。
奇怪的是,如果我将Task
移到populate
内部并去掉async
,则旋转动画不会出现卡顿,因此我认为循环实际上是在后台执行的。但是现在我会收到“不允许从后台线程发布更改”的警告。
func populate() {
Task {
var items = [String]()
for i in 0 ..< 4_000_000 {
items.append("\(i)")
}
self.items = items /// Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
}
}
/// ...
Button {
model.populate()
}
结果:
![即使按钮被按下,旋转动画仍然会继续](https://istack.dev59.com/kqkuK.gif)
我该如何确保我的代码在后台线程上执行?我认为这可能与MainActor
有关,但我不确定。
Task
不是异步的吗?当我在Task
中使用await
的时候,当前线程不应该是后台线程吗?我无法理解第一种和第二种选项之间的区别。无论如何,在 SwiftUI 中,您只需要设置属性,UI 就会自动更新。 - ahezeMainActor.run { self.items = items }
将更新移动到主线程吗? - lazarevzubovBOYCOTT on russia!Don't buy, sell, support - HELP TO STOP WAR!
与“可能的解决方案”有什么关系?同样适用于您个人资料中的BOYCOTT on russia - terrorist must be punished!
。在SO上,激进言辞和/或政治宣传是否不合适? - meaning-matters