Swift中多属性didSet引发无限递归:如何禁用didSet触发
如何抑制Swift属性didSet引发的无限递归?
问题场景
我有一个包含两个使用didSet设置值的属性的类,代码如下:
class MyClass { var myProp1: Bool { didSet { self.myProp2 += blinks ? 1 : -1 } } var myProp2: Int { didSet { self.myProp1 = (midi % 2) != 0 } } }
如预期,这会导致无限递归(myProp1调用myProp2,myProp2再调用myProp1……)。想知道能否在一个didSet中调用另一个属性时,关闭该属性的didSet触发行为?已知init()中didSet不会触发,希望实现类似效果。了解get/set和创建方法的方案,但想询问如何抑制didSet引发的无限递归。
解决方案
解决这种无限递归的核心思路是让属性在特定场景下跳过didSet的执行,下面提供两种贴合需求的实现方式:
方案1:添加内部控制标志
通过一个私有布尔变量标记当前是否处于属性更新流程中,在didSet开头判断标志,若处于更新状态则直接跳过逻辑,避免递归。
class MyClass { private var isUpdating = false // 假设blinks是类内已定义的布尔变量 private var blinks = true var myProp1: Bool { didSet { guard !isUpdating else { return } isUpdating = true myProp2 += blinks ? 1 : -1 isUpdating = false } } var myProp2: Int { didSet { guard !isUpdating else { return } isUpdating = true // 修正原代码笔误,将midi改为myProp2 myProp1 = (myProp2 % 2) != 0 isUpdating = false } } // 必须提供初始化方法为属性赋初始值 init(initialProp1: Bool, initialProp2: Int) { self.myProp1 = initialProp1 self.myProp2 = initialProp2 } }
当在didSet中操作另一个属性时,先将isUpdating设为true,此时另一个属性的didSet会因标志判断直接返回,不会执行递归逻辑;操作完成后再将标志恢复为false,不影响后续正常的属性设置。
方案2:使用私有存储属性绕开didSet
把公开属性对应的底层存储设为私有变量,在自定义setter中直接操作私有变量,由于私有变量没有didSet监听,因此不会触发递归。
class MyClass { private var _myProp1: Bool private var blinks = true var myProp1: Bool { get { _myProp1 } set { _myProp1 = newValue // 直接操作私有存储变量,不会触发myProp2的didSet _myProp2 += blinks ? 1 : -1 } } private var _myProp2: Int var myProp2: Int { get { _myProp2 } set { _myProp2 = newValue // 直接操作私有存储变量,不会触发myProp1的didSet _myProp1 = (_myProp2 % 2) != 0 } } init(initialProp1: Bool, initialProp2: Int) { self._myProp1 = initialProp1 self._myProp2 = initialProp2 } }
这种方式相当于将公开属性转为带自定义逻辑的计算属性,直接操作底层私有变量不会触发任何didSet,从根源上杜绝了递归的可能。
内容的提问来源于stack exchange,提问作者ArturJD96
相关产品推荐
相关产品推荐

