gpt4 book ai didi

swift - 为什么在更改现有值的成员时会运行属性观察器?

转载 作者:搜寻专家 更新时间:2023-10-31 22:04:39 25 4
gpt4 key购买 nike

请考虑这个 Swift 代码。我有一个包装另一个类实例的类。当我在保留值上设置属性时,包装类的属性观察器将运行。

protocol MyProtocol {
var msgStr: String? { get set }
}

class MyClass: MyProtocol {
var msgStr: String? {
didSet {
print("In MyClass didSet")
}
}
}

class MyWrapperClass {
var myValue: MyProtocol! {
didSet {
print("In MyWrapperClass didSet")
}
}
}

let wrapperObj = MyWrapperClass()
wrapperObj.myValue = MyClass() // Line1
wrapperObj.myValue.msgStr = "Some other string" // Line2

以上代码的输出是:

In MyWrapperClass didSet
In MyClass didSet
In MyWrapperClass didSet

我知道 didSet 在变量值改变时被调用。

因此,当“第 1 行”处的​​上述代码执行时,我了解到打印了“In MyWrapperClass didSet”,这很好。

接下来,当 Line2 执行时,我希望正确打印“In MyClass didSet”,但我不确定为什么打印“In MyWrapperClass didSet”,因为属性 myValue 未更改.谁能解释一下为什么?

最佳答案

Swift 需要将 myValue.msgStr 的变异视为具有值语义;这意味着需要触发 myValue 上的属性观察器。这是因为:

  1. myValue 是协议(protocol)类型的属性(恰好也是可选的)。该协议(protocol)不是类绑定(bind)的,因此符合类型可以是值类型和引用类型。

  2. myStr 属性要求有一个隐式的 mutating setter 因为 (1) 和它没有被标记为 nonmutating。因此,协议(protocol)类型的值很可能在根据其 myStr 要求发生变化时发生变化。

考虑该协议(protocol)可能已被值类型采用:

struct S : MyProtocol {
var msgStr: String?
}

在这种情况下,msgStr 的突变在语义上等同于将 S 值与 msgStr 的突变值重新分配回 myValue(参见 this Q&A for more info)。

或者可以将默认实现重新分配给 self:

protocol MyProtocol {
init()
var msgStr: String? { get set }
}

extension MyProtocol {
var msgStr: String? {
get { return nil }
set { self = type(of: self).init() }
}
}

class MyClass : MyProtocol {
required init() {}
}

class MyWrapperClass {

// consider writing an initialiser rather than using an IUO as a workaround.
var myValue: MyProtocol! {
didSet {
print("In MyWrapperClass didSet")
}
}
}

在这种情况下,myValue.myStr 的突变将一个全新实例重新分配给 myValue

如果 MyProtocol 是类绑定(bind)的:

protocol MyProtocol : class {
var msgStr: String? { get set }
}

或者如果 msgStr 要求指定 setter 必须是非可变的:

protocol MyProtocol {
var msgStr: String? { get nonmutating set }
}

然后 Swift 会将 myValue.msgStr 的突变视为具有引用语义;也就是说,myValue 上的属性观察器不会被触发。

这是因为 Swift 知道属性值不能改变:

  1. 在第一种情况下,只有类可以符合,并且类的属性 setter 不能改变 self(因为这是对实例的不可变引用)。

  2. 在第二种情况下,msgStr 要求只能由类中的属性(并且此类属性不会改变引用)或类中的计算属性来满足setter 不可变的值类型(因此必须具有引用语义)。

或者,如果 myValue 刚刚被输入为 MyClass!,你也会得到引用语义,因为 Swift 知道你正在处理一个类:

class MyClass {
var msgStr: String? {
didSet {
print("In MyClass didSet")
}
}
}

class MyWrapperClass {
var myValue: MyClass! {
didSet {
print("In MyWrapperClass didSet")
}
}
}

let wrapperObj = MyWrapperClass()
wrapperObj.myValue = MyClass() // Line1
wrapperObj.myValue.msgStr = "Some other string" // Line2

// In MyWrapperClass didSet
// In MyClass didSet

关于swift - 为什么在更改现有值的成员时会运行属性观察器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48842279/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com