You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 03:05:27