我有一门ObservableObject
课和一个快捷界面.当点击一个按钮时,我会创建一个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
}
}
}
}
结果:
按钮停止移动,然后在populate
结束时突然弹回来.
奇怪的是,如果我把Task
移动到populate
本身,go 掉async
,旋转动画就不会断断续续,所以我认为循环实际上是在后台执行的.然而,我现在得到了Publishing changes from background threads is not allowed
的警告.
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()
}
结果:
如何确保我的代码在后台线程上执行?我想这可能与MainActor
有关,但我不确定.