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

SwiftUI列表行删除时Row View/ViewModel未触发deinit问题

SwiftUI List行视图与ViewModel生命周期异常问题解析

核心问题根源

你遇到的现象本质是错误使用@State管理引用类型ViewModel,加上SwiftUI List的视图复用机制共同导致的:

  1. @State是为值类型设计的属性包装器,用来存储视图内部的可变值类型状态。把引用类型(RowViewModel)放在@State里,SwiftUI无法正确追踪其生命周期,会导致对象被意外持有,无法及时释放。
  2. List内部有视图复用池,启动时的两次初始化是List在布局阶段创建临时视图进行预渲染,随后销毁临时实例,保留实际显示的视图——这属于SwiftUI的正常内部机制,但因为@State的错误使用,放大了这个现象。
  3. 删除行时,由于@State持有旧的ViewModel实例,加上List的视图重建逻辑,旧实例无法被ARC及时回收,直到App进程终止才会触发deinit。

修复方案

只需要把RowView中管理ViewModel的属性包装器从@State改成@StateObject(专门用于管理Observable引用类型的生命周期),同时优化删除逻辑:

修改后的完整代码:

struct ContentView: View {
    @State var list: [String] = ["A", "B", "C"]

    var body: some View {
        List {
            ForEach(list, id: \.self) { item in
                RowView(item: item)
            }
            .onDelete(perform: { indexSet in
                list.remove(atOffsets: indexSet) // 更简洁的删除写法
            })
        }
    }
}

struct RowView: View {
    @StateObject var vm: RowViewModel // 替换@State为@StateObject

    init(item: String) {
        _vm = StateObject(wrappedValue: RowViewModel(item: item)) // 显式初始化StateObject
    }

    var body: some View {
        Text(vm.item)
    }
}

@Observable
class RowViewModel {
    public let item: String

    init(item: String) {
        print("RowVM:Init \(item)")
        self.item = item
    }

    deinit {
        print("RowVM:Deinit \(item)")
    }
}

验证结果

  • 启动App时,控制台输出(每个ViewModel仅初始化一次):
    RowVM:Init A
    RowVM:Init B
    RowVM:Init C
    
  • 滑动删除行C时,控制台立即输出:
    RowVM:Deinit C
    
  • 删除行B时,输出:
    RowVM:Deinit B
    
  • 停止App时,剩余ViewModel正常释放:
    RowVM:Deinit A
    

补充说明

  • @StateObject会在视图首次初始化时创建Observable对象,并在视图被销毁时自动释放对象,完美匹配ViewModel的生命周期需求。
  • 如果需要从父视图传递ViewModel,应该使用@ObservedObject,但这里RowView是独立创建ViewModel,@StateObject是正确选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 19:25:13