【问题标题】:Accessing a lazy property on a struct mutates the struct访问结构上的惰性属性会改变结构
【发布时间】:2021-06-22 07:01:20
【问题描述】:

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

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

【问题讨论】:

  • 请将代码发布为 text。不要发布代码图片。

标签: swift xcode struct lazy-initialization


【解决方案1】:

你看到的是被观察的属性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 被定义为变异,它可能会很复杂设置。

【讨论】:

    【解决方案2】:

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

    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.4 在Xcode 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] 只执行了一次,但在访问items 时会重复调用MyClass.myStructPropery.modify。

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

    【讨论】:

    猜你喜欢
    • 2020-03-15
    • 1970-01-01
    • 2021-04-01
    • 2019-11-26
    • 1970-01-01
    • 2011-12-11
    • 1970-01-01
    • 2015-06-09
    • 1970-01-01
    相关资源
    最近更新 更多