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

使用SwiftUI Observation框架移除数组实例后deinit未调用原因

为什么移除@Observable标记的Streamer实例后deinit未触发?

核心原因

  • Observation框架的订阅持有:当你在SwiftUI视图闭包(比如List的内容闭包、Button的action闭包)里直接引用Streamer实例的属性时,Observation框架会自动为该实例创建订阅者,这个订阅者会持有实例的强引用,直到视图完成更新周期或订阅被取消。哪怕你从数组里移除了实例,只要订阅还在,实例就没法被释放。
  • SwiftUI视图的延迟销毁:List更新时,已移除的行视图不会立刻被销毁,尤其是加了.animation修饰符的情况下,动画执行过程会延长闭包的生命周期,导致Streamer实例的引用被暂时保留。

解决方法

  1. 通过管理器间接操作实例
    不要在视图闭包里直接持有Streamer实例,而是给modelManager添加操作方法,通过实例名称找到对象再修改,这样闭包只会持有model的引用,不会直接绑定Streamer:

    // 在modelManager中新增方法
    func updateStreamer(named name: String) {
        guard let streamer = allStreamers.first(where: { $0.name == name }) else { return }
        streamer.state = streamerState.allCases.randomElement()!
        streamer.title = "title\(Int.random(in: 1...10))"
    }
    
    // 视图中修改Button的action
    Button("change status") {
        model.updateStreamer(named: streamer.name)
    }
    
  2. 强制视图刷新取消订阅
    可以给List添加.id()修饰符,当数组内容变化时强制重建行视图,让SwiftUI及时销毁旧的订阅:

    List(model.allStreamers, id: \.name) { streamer in
        // ... 原有视图内容
    }.id(model.allStreamers.count)
    
  3. 移除不必要的动画
    暂时去掉.animation修饰符测试,如果deinit正常触发,说明动画是导致引用持有的原因之一。可以调整动画的作用范围,避免绑定到单个Streamer实例的属性上。

额外验证

你可以在移除实例后,手动触发一次视图的强制刷新(比如修改一个无关的@State变量),看看deinit是否会触发,这能帮你确认订阅是否是问题所在。

内容的提问来源于stack exchange,提问作者HàMzà VyNøs

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 03:23:18