You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ViewModel中取消长期Task?解决内存泄漏问题

修复SwiftUI ViewModel内存泄漏与Task取消问题

你的推测完全正确——执行observeNotification的Task持有了TestViewModel的强引用,而NotificationCenter的异步序列会持续监听方向变化通知,导致Task无法自动结束,ViewModel也就无法被销毁,进而出现检测逻辑持续运行的问题。

步骤1:在ViewModel中管理Task引用并实现取消逻辑

给TestViewModel添加Task属性保存异步任务引用,同时提供取消方法,主动终止任务以释放引用:

@MainActor
class TestViewModel: ObservableObject {
    @Published var isPortrait = false
    private var orientationTask: Task<Void, Never>? // 保存Task引用
    
    init() {
        print("TestViewModel init.")
        setup()
    }
    
    deinit {
        print("TestViewModel deinit.")
    }
    
    func setup() {
        orientationTask = Task {
            await observeNotification()
        }
    }
    
    // 新增:主动取消任务的方法
    func cancelObservation() {
        orientationTask?.cancel()
        orientationTask = nil
    }
    
    private func observeNotification() async {
        let sequence = NotificationCenter.default.notifications(named: UIDevice.orientationDidChangeNotification)
            .map { _ in await UIDevice.current.orientation }
        do {
            for try await value in sequence { // 用try await捕获取消信号
                print("orientationDidChangeNotification changed, orientation = \(value).")
                isPortrait = value == .portrait
            }
        } catch is CancellationError {
            print("Orientation observation task cancelled.")
        } catch {
            // 处理其他潜在错误
        }
    }
}

步骤2:在View消失时触发Task取消

在TestView的onDisappear回调中调用ViewModel的取消方法,确保Sheet关闭时主动终止异步任务:

struct TestView: View {
    @StateObject private var vm = TestViewModel()
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        VStack {
            Text("isPortrait = \(vm.isPortrait.description)")
            Button("Dismiss") {
                dismiss()
            }
        }
        .onDisappear {
            print("TestView onDisappear.")
            vm.cancelObservation() // 触发任务取消
        }
    }
}

效果验证

修改后关闭TestView时,会依次打印:

  1. TestView onDisappear.
  2. Orientation observation task cancelled.
  3. TestViewModel deinit.
    方向检测逻辑会停止运行,ViewModel成功释放,内存泄漏问题解决。

关键原理

  • Task.cancel()会触发CancellationError,异步序列的for try await循环捕获错误后终止,Task不再持有ViewModel的强引用。
  • 主动取消Task后,NotificationCenter的异步监听会自动停止,避免资源浪费。

内容的提问来源于stack exchange,提问作者foolbear

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 00:09:22