为什么当视图消失时任务不会自动取消?

Hol*_*ene 3 task async-await swift swiftui

根据 Apple 文档,当视图消失时,任务将自动取消。{}

在视图消失、操作完成之前的某个时刻,SwiftUI 会自动取消任务。

为什么在这种情况下,当 ChildView 消失时,“loadData”方法中的任务没有取消?(当导航回“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")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

即使 ChildView 消失,“loadData”中的任务也会继续执行。当ChildView连续多次初始化时,它也会执行多次。

这可能会导致内存泄漏,尤其是在 ChildView 中使用带有 @Published 'data' 属性而不是 @State 属性的类时。在这种情况下使用 Task() 的正确实现是什么?

Swe*_*per 6

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")
}
Run Code Online (Sandbox Code Playgroud)