请考虑这个 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
上的属性观察器。这是因为:
myValue
是协议(protocol)类型的属性(恰好也是可选的)。该协议(protocol)不是类绑定(bind)的,因此符合类型可以是值类型和引用类型。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 知道属性值不能改变:
在第一种情况下,只有类可以符合,并且类的属性 setter 不能改变
self
(因为这是对实例的不可变引用)。在第二种情况下,
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/