在不暴露ObservableObject类的前提下,从外部修改其@Published属性的惯用方式及潜在问题咨询
在不暴露ObservableObject类的前提下,从外部修改其@Published属性的惯用方式及潜在问题咨询
我完全能理解你现在的纠结——既要保持两个模块的解耦,又要实现跨模块修改属性,两种方案各有优劣,确实容易拿不准。咱们把这两个方案的细节、潜在问题和适用场景拆解清楚,帮你做判断:
一、协议抽象方案:正统但可避免过度设计
这是面向接口编程的典型实践,确实是解耦场景下的“标准”做法:
- 它能清晰定义对外暴露的能力边界,调用方(B类)只依赖协议,完全不知道具体的A类存在,完美符合你要的解耦要求。
- 你定义协议时用了
nonmutating set非常关键,因为ObservableObject作为类类型,默认的set方法是mutating的,这个修饰符能让协议正确适配类的属性修改逻辑。
至于你担心的“协议数量失控”,其实可以用组合协议来缓解:把相关的属性或方法归到同一个协议里,而不是拆分得太细。比如如果A需要暴露flag和count两个属性,就定义一个StateControllable协议包含这两个属性,而非分别做两个独立协议。
二、inout传参方案:简洁但需注意语义与扩展性
这个方案确实不算“主流常见”,但绝非错误用法,咱们从原理和潜在风险两方面来看:
1. 与@Published的配合安全性
你不用担心inout和@Published的配合问题:
@Published本质是属性包装器,当你用&a.flag传递inout参数时,传递的是包装器底层存储的原始Bool值的引用。修改这个值时,@Published的willSet/didSet会正常触发,objectWillChange的订阅也能收到通知——就像你示例里的代码,两种方式都能正确打印flag值,说明这个场景下是完全安全的。
2. 潜在的局限性
- 语义模糊:调用方(B类)只知道要修改一个Bool值,但不知道这个值属于哪个对象、修改它会触发什么副作用(比如视图更新、其他依赖逻辑),后续业务复杂后,维护者可能会困惑这个值的意义。
- 扩展性差:如果以后需要修改A的多个属性,要么加多个inout参数(代码会变得臃肿),要么封装成结构体(但又会破坏解耦);而协议可以一次性定义多个属性或方法,扩展性好很多。
三、决策建议
- 如果只是修改单个简单属性,且后续大概率不会扩展更多修改需求,
inout方案是简洁可行的,不会有隐藏坑。 - 如果需要修改多个属性,或者后续可能给其他类暴露类似能力,协议方案更合适——通过组合协议避免“协议爆炸”即可。
本质上这是简洁性和扩展性的权衡,没有绝对的对错,完全看你的业务场景。
内容来源于stack exchange
相关产品推荐
相关产品推荐

