根据 Apple 文档,当视图消失时任务将自动取消。
SwiftUI 会在任务结束后的某个时刻自动取消任务 视图在操作完成之前消失。
为什么在这种情况下,当ChildView消失时,'loadData'方法中的Task没有被取消? (当导航回“ParentView”时)
struct ChildView: View {
@State private var data: [Double] = []
private func loadData() async {
// Why isn't this Task automatically cancelled when the ChildView disappears?
// The Task is still executing in the background.
Task(priority: .background) {
// Simulating heavy data processing.
self.data = (1...3_000_000).map { _ in Double.random(in: -10...30) }
print("Task ended")
}
}
var body: some View {
Text("This is a child view")
.task { await loadData() }
}
}
struct ParentView: View {
var body: some View {
NavigationStack {
NavigationLink(destination: { ChildView() }) {
Text("Show child view")
}
}
}
}
即使 ChildView 消失,“loadData”中的任务也会继续执行。当ChildView连续多次初始化时也会执行多次。
这可能会导致内存泄漏,尤其是在 ChildView 中使用具有 @Published 'data' 属性而不是 @State 属性的类时。在这种情况下使用 Task() 的正确实现是什么?
SwiftUI 确实取消了由
task
视图修饰符隐式创建的任务,但该任务不执行“繁重的数据处理”。该任务仅创建一个子任务来运行loadData
。该子任务几乎立即完成。
这是因为
loadData
所做的只是通过使用 Task { ... }
创建一个顶级任务,而不执行其他任何操作。当您的视图消失时,
loadData
任务就已经完成了。然而,顶级任务执行所有“繁重的数据处理”,并且因为它是顶级任务(即不是 loadData
任务的子任务),所以当 loadData
被取消时,它不会被取消.
您不应该在此处创建顶级任务。把繁重的数据处理直接放在
loadData
。
此外,任务取消是合作性的 -
loadData
还应该检查 Task.isCancelled
并停止正在执行的操作。
private func loadData() async {
for _ in 1...3_000_000 {
if Task.isCancelled { // for example
break
}
self.data.append(Double.random(in: -10..<30))
}
print("Task ended")
}