访问结构上的惰性属性会改变结构

问题描述

我在一个结构体中有一个惰性属性,每次我访问它时,它都会改变结构体。

var numbers = [1,2,3]

struct MyStruct {
    
    lazy var items = numbers
}

class MyClass {
    var myStructPropery: MyStruct = MyStruct() {
        didSet {
            print(myStructPropery)
        }
    }
}

var myClass = MyClass()
myClass.myStructPropery.items
myClass.myStructPropery.items
myClass.myStructPropery.items

结果:

每次都会调用Print(didSet)。

理想的行为应该只是第一次突变(因为惰性变量就是这样表现的)。这是 Swift 中的错误还是我做错了什么?

解决方法

你看到的是,被观察的属性 items 已经通知它的观察者它因为被访问而发生了变异,而惰性变量根据定义是变异的,因为它将在以后设置。因此,didSet 被调用来处理这个问题。

lazy 变量实际上只设置一次,即使它表示它已经发生了变化,并且属性 myStructPropery 在第一次设置变量时只发生了一次变化,但之后是同一个实例。

这是我们如何验证这一点,首先更改惰性 var 声明,使其更像我们通常如何声明这样的变量

lazy var items: [Int] = { numbers }() 

然后添加打印语句

lazy var items: [Int] = {
    print("inside lazy")
    return numbers
}()

如果我们现在运行测试代码

var myClass = MyClass()
myClass.myStructProperty.items
myClass.myStructProperty.items
myClass.myStructProperty.items 

我们看到“inside lazy”只打印一次。要验证属性 myStructProperty 未更改,我们可以使结构符合 Equatable 并在 didSet

内执行简单检查
didSet {
    if oldValue != myStructProperty {
        print(myStructProperty)
    }
}

现在运行测试,我们看到 didSet 中的打印永远不会执行,因此 myStructProperty 永远不会改变。

我不知道这种行为是否是一个错误,但就我个人而言,如果属性观察者在访问惰性属性后停止观察它,或者惰性变量一旦被访问就不会被定义为变异,这可能会很复杂设置。

,

我通过以下设置开始调试 -

var numbers = [1,2,3]

struct MyStruct {
    lazy var items = numbers
}

class MyClass {
    var myStructPropery: MyStruct = MyStruct() {
        didSet {
            // Changed this to make sure we are not invoking getter here
            print("myStructPropery setter called")
        }
    }
}

let myClass = MyClass()
myClass.myStructPropery.items
myClass.myStructPropery.items
myClass.myStructPropery.items

我可以使用 Xcode 12.5Swift 5.4 上重现该问题。

尝试 1:将 var numbers 变成 let numbers - 不起作用。

let numbers = [1,3]

尝试 2:在不使用额外变量的情况下内联分配值 - 不起作用。

struct MyStruct {
    lazy var items = [1,3]
}

尝试 3:使用完整的 getter 语法内联分配值 - 不起作用。

struct MyStruct {
    lazy var items: [Int] = {
        return [1,3]
    }()
}

在这一点上,我们没有选择可以尝试。尽管我们可以清楚地看到最后一次尝试中的 return [1,3] 只执行了一次,但在访问 MyClass.myStructPropery.modify 时会重复调用 items

也许 Swift Forums 是讨论这个更好的地方。