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

Accessing a lazy property on a struct mutates the struct

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

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 中的错误还是我做错了什么?

我开始使用以下设置进行调试 -

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

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

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

let numbers = [1,2,3]

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

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

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

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

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

时被重复调用

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

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

惰性变量实际上只设置了一次,即使它发出信号表明它已经发生变异,并且 属性 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 一旦设置就不会被定义为变异。