使用SwiftUI Observation框架移除数组实例后deinit未调用原因
为什么移除@Observable标记的Streamer实例后deinit未触发?
核心原因
- Observation框架的订阅持有:当你在SwiftUI视图闭包(比如List的内容闭包、Button的action闭包)里直接引用
Streamer实例的属性时,Observation框架会自动为该实例创建订阅者,这个订阅者会持有实例的强引用,直到视图完成更新周期或订阅被取消。哪怕你从数组里移除了实例,只要订阅还在,实例就没法被释放。 - SwiftUI视图的延迟销毁:List更新时,已移除的行视图不会立刻被销毁,尤其是加了
.animation修饰符的情况下,动画执行过程会延长闭包的生命周期,导致Streamer实例的引用被暂时保留。
解决方法
通过管理器间接操作实例
不要在视图闭包里直接持有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) }强制视图刷新取消订阅
可以给List添加.id()修饰符,当数组内容变化时强制重建行视图,让SwiftUI及时销毁旧的订阅:List(model.allStreamers, id: \.name) { streamer in // ... 原有视图内容 }.id(model.allStreamers.count)移除不必要的动画
暂时去掉.animation修饰符测试,如果deinit正常触发,说明动画是导致引用持有的原因之一。可以调整动画的作用范围,避免绑定到单个Streamer实例的属性上。
额外验证
你可以在移除实例后,手动触发一次视图的强制刷新(比如修改一个无关的@State变量),看看deinit是否会触发,这能帮你确认订阅是否是问题所在。
内容的提问来源于stack exchange,提问作者HàMzà VyNøs
相关产品推荐
相关产品推荐

