Swift中类遵循Publisher协议后属性访问器调用异常消失问题
嘿,这个问题确实挺让人摸不着头脑的——明明只是加了个协议遵循,怎么属性的getter和setter就突然“消失”了?我来帮你拆解一下背后的原因:
问题本质:编译器的激进优化在搞鬼
这个现象其实是Swift编译器的性能优化策略导致的。当你的Root类遵循Publisher协议后,编译器会认为这个类的实例大概率会被频繁使用(毕竟Publisher是Combine框架里用来发布事件的核心类型,通常会被订阅、多次访问),所以会启用更激进的优化规则。
具体到你的代码里,Root的nested属性是个计算属性,但它的实现非常简单:getter直接返回私有存储属性_nested,setter也只是给_nested赋值,除了打印日志外没有其他复杂逻辑。编译器检测到这一点后,就会直接把对nested的访问扁平化——跳过你写的getter和setter,直接读写底层的_nested存储属性,自然就不会触发打印日志了。
为什么只有遵循Publisher才会触发?
其实不是只有Publisher会触发这种优化,任何能让编译器认为“这个类会被高频访问”的条件都可能触发。只不过Publisher作为Combine的核心协议,编译器对这类类型的优化策略会更激进一些,优先把简单的转发型计算属性直接替换为存储属性访问,来提升性能。
怎么验证/解决?
如果你想确认是优化的问题,可以试试在nested的getter或setter里加一些编译器无法优化掉的逻辑,比如生成一个UUID,或者调用一个非内联的自定义函数:
var nested: Nested { get { print("Accessing nested value") // 加入无法被优化的副作用操作 _ = UUID() return _nested } set { print("Will Set nested value") defer { print("Did set nested value") } self._nested = newValue } }
这样编译器就无法确定这段代码的副作用,不会跳过访问器,日志就能正常打印了。
另外,如果你只是在调试阶段需要看到日志,可以暂时把Xcode的优化级别改成-O0(Debug模式默认就是这个),但Release模式下为了性能,这种优化还是会生效的。
总结
这不是Swift的bug,而是编译器为了提升性能做的合理优化——只是它刚好把你用来调试的打印逻辑给“优化没了”。只要你的访问器里有实际的业务逻辑(不是简单转发),或者加入一些无法被消除的副作用,就能避免这种情况。
内容来源于stack exchange

