SwiftUI/Combine中嵌套ObservableObject的自定义发布者未触发问题咨询
问题根因
@Published的发布逻辑仅监听属性本身的赋值动作:只有当你把新值直接赋值给被@Published标记的属性时,才会触发对应的$前缀发布者和objectWillChange事件。- 你当前的
$model是ContentViewModel中@Published var model对应的发布者,只有当model实例被整体替换时才会抛事件,不会监听model内部的nestedIsToggled变化。 - 你修改
model.nestedIsToggled时,model实例本身没有被重新赋值,因此$model不会发出新值,后续的map操作无法拿到更新后的布尔值,自定义发布者自然不会触发。 - 你手动转发
model.objectWillChange到父类objectWillChange的逻辑仅用于触发SwiftUI视图刷新,和你自定义的Combine发布者是完全独立的两套逻辑,不会互相影响。
修正方案
- 要监听嵌套模型的内部属性变化,需要直接引用
Model实例的nestedIsToggled对应的$前缀发布者;如果后续可能存在model实例被替换的场景,需要用flatMap串联两层发布者,自动适配实例切换的情况。 - 兼容
model实例替换的完整修正实现:
private var nestedToggleChangedPublisher: AnyPublisher<Bool, Never> { $model // 当model实例变化时,自动切换到新实例的nestedIsToggled发布者 .flatMap { $0.$nestedIsToggled } .removeDuplicates() // 可选:避免重复抛出相同值 .eraseToAnyPublisher() }
- 如果确认
model实例不会被替换,也可以简化为直接监听内部属性:
private var nestedToggleChangedPublisher: AnyPublisher<Bool, Never> { model.$nestedIsToggled .eraseToAnyPublisher() }
补充优化建议
- 你当前手动转发
objectWillChange的逻辑可以保留,这是SwiftUI下转发嵌套ObservableObject变更的标准兼容方案。 - 如果想要统一逻辑,也可以直接把嵌套属性的变更合并到父类的
objectWillChange中,不需要单独手动转发:
init() { $model .flatMap { $0.objectWillChange } .sink { [weak self] _ in self?.objectWillChange.send() } .store(in: &anyCancellables) // 其余原有订阅逻辑保留即可 }
内容的提问来源于stack exchange,提问作者adelmachris
相关产品推荐
相关产品推荐

