[英]Why can't I remove elements from an Array in its willSet event?
逻辑是在数组具有指定数量的元素时清除它。 我可以将检查放在Array之外,但是我试图在Array的willSet事件中查看该怎么办。 结果是数组中的元素保持静止。
这是代码
var MyArr=[String]() {
willSet{
print("now count is:\(MyArr.count)")
if MyArr.count>2 {
print("now remove all!")
MyArr.removeAll()
}
}
}
MyArr.append("hello")
MyArr.append(",world")
MyArr.append("!")
MyArr.append("too much.")
print("The conent is \(MyArr)")
预期MyArr仅包含一个元素,而实际结果为四个。
该行为与值类型/引用类型无关
请注意警告
尝试将属性“ MyArr”存储在其自己的willSet中,该属性将被新值覆盖
这意味着在willSet
中修改对象无效。
引用语言指南-属性-属性观察者 [ 重点 ]:
物业观察员
财产观察员观察并响应财产价值的变化。
...
您可以选择在属性上定义这些观察者之一或全部:
- 将在存储值之前调用
willSet
。- 存储新值后,将立即调用
didSet
。
当您使用willSet
属性观察器进行实验时,在willSet
块中观察的任何属性突变willSet
在newValue
的实际存储之前发生,而newValue
的实际存储紧随willSet
块之后。 这意味着您实际上是在尝试将myArr
“旧副本”突变为新值之前。
可以说,这可能是一些非法的讨论,作为任何突变myArr
按理说应该导致任何财产观察员的调用,因此性能观察者(引用类型的引用或值类型的值的突变)内的财产的突变可能导致对属性观察者的递归调用。 但是,情况并非如此,对于willSet
情况,尤其是,发出警告,如@vadian的答案中指出的那样。 在属性观察器中对属性本身进行更改不会触发属性观察器的事实并没有得到很好的记录,但是《语言指南-属性-类型属性》中的示例指出了这一点[ 强调我的]:
查询和设置类型属性
...
currentLevel
属性具有didSet
属性观察器,currentLevel
在设置currentLevel
时检查其值。 ...注意
在这两个检查的第一个中,
didSet
观察器将currentLevel
设置为不同的值。 但是,这不会导致再次调用观察者。
这也是我们可以预料到的一种特殊情况,例如didSet
是合并例如将给定属性值限制在某些范围内的范围检查的绝佳场所。 也就是说,如果前者超出范围,则用有界值覆盖新值。
现在,如果在存储新值之后更改为将属性突变为,则该突变将生效,并且如上所述,不会触发对属性观察器的任何其他调用。 应用于您的示例:
var myArr = [String]() {
didSet {
print("now count is: \(myArr.count)")
if myArr.count > 2 {
print("now remove all!")
myArr.removeAll()
}
}
}
myArr.append("hello")
myArr.append(",world")
myArr.append("!")
myArr.append("too much.")
print("The content is \(myArr)") // The content is ["too much."]
aAlan对评论didSet
VS willSet
很有趣。 我尝试了相同的代码,但是使用didSet
,它确实从数组中删除了看起来很奇怪的元素。 我认为这是设计使然。 我的理由是:
willSet
确实获得了对实际项目的引用并进行了更改,则所有内容都将被覆盖。 它使所有更改无效。 因为您甚至在阅读即将发生的事情之前就已经在这样做。 另外(重复dfri所说的内容),如果再次在willSet
设置它,那么将一次又一次触发属性观察器,并创建一个反馈循环,这将使编译器崩溃,因此,幕后的编译器将didSet
并非如此。 您等待...读取值,然后做出决定。 您知道该属性的值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.