[英]Why can't I remove elements from an Array in its willSet event?
The logic is to clear an Array when it has a specified amount of elements. 逻辑是在数组具有指定数量的元素时清除它。 I could put the check outside of the Array but I was trying to see what if do it in Array's willSet event.
我可以将检查放在Array之外,但是我试图在Array的willSet事件中查看该怎么办。 The result is elements in Array stay still.
结果是数组中的元素保持静止。
Here is the code 这是代码
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 was expected to have only one elements while actual result was four. 预期MyArr仅包含一个元素,而实际结果为四个。
The behavior has nothing to do with value type / reference type 该行为与值类型/引用类型无关
Please note the warning 请注意警告
Attempting to store to property 'MyArr' within its own willSet, which is about to be overwritten by the new value
尝试将属性“ MyArr”存储在其自己的willSet中,该属性将被新值覆盖
which means that modifying the object in willSet
has no effect. 这意味着在
willSet
中修改对象无效。
Citing the Language Guide - Properties - Property Observers [ emphasis mine]: 引用语言指南-属性-属性观察者 [ 重点 ]:
Property Observers
物业观察员
Property observers observe and respond to changes in a property's value.
财产观察员观察并响应财产价值的变化。
...
...
You have the option to define either or both of these observers on a property:
您可以选择在属性上定义这些观察者之一或全部:
willSet
is called just before the value is stored .将在存储值之前调用
willSet
。didSet
is called immediately after the new value is stored .存储新值后,将立即调用
didSet
。
As you are experimenting with the willSet
property observer, any mutation of the property you are observing within the willSet
block precedes the actual storing of the newValue
which follows immediately after the willSet
block. 当您使用
willSet
属性观察器进行实验时,在willSet
块中观察的任何属性突变willSet
在newValue
的实际存储之前发生,而newValue
的实际存储紧随willSet
块之后。 This means you are essentially attempting to mutate "the old copy" of myArr
prior to it being replaced with its new value. 这意味着您实际上是在尝试将
myArr
“旧副本”突变为新值之前。
Arguably this could be discussed as something illegal, as any mutation of myArr
should lead to the invocation of any property observers, thus mutation of a property within a property observer (mutation of the reference for reference types or the value for value types) could arguably lead to recursive calls to the property observer. 可以说,这可能是一些非法的讨论,作为任何突变
myArr
按理说应该导致任何财产观察员的调用,因此性能观察者(引用类型的引用或值类型的值的突变)内的财产的突变可能导致对属性观察者的递归调用。 This is not the case, however, and for the willSet
case, specifically, instead a warning is emitted, as pointed out in @vadian's answer . 但是,情况并非如此,对于
willSet
情况,尤其是,发出警告,如@vadian的答案中指出的那样。 The fact that mutation of the property itself within a property observer will not trigger property observers is not really well-documented, but an example in the Language Guide - Properties - Type Properties points it out [ emphasis mine]: 在属性观察器中对属性本身进行更改不会触发属性观察器的事实并没有得到很好的记录,但是《语言指南-属性-类型属性》中的示例指出了这一点[ 强调我的]:
Querying and Setting Type Properties
查询和设置类型属性
...
...
The
currentLevel
property has adidSet
property observer to check the value ofcurrentLevel
whenever it is set.currentLevel
属性具有didSet
属性观察器,currentLevel
在设置currentLevel
时检查其值。 ......
NOTE
注意
In the first of these two checks, the
didSet
observer setscurrentLevel
to a different value.在这两个检查的第一个中,
didSet
观察器将currentLevel
设置为不同的值。 This does not, however, cause the observer to be called again.但是,这不会导致再次调用观察者。
This is also a special case we can somewhat expect, as eg didSet
is an excellent place to incorporate eg bounds checks that clamps a given property value to some bounds; 这也是我们可以预料到的一种特殊情况,例如
didSet
是合并例如将给定属性值限制在某些范围内的范围检查的绝佳场所。 ie, over-writing the new value by a bounded one in case the former is out of bounds. 也就是说,如果前者超出范围,则用有界值覆盖新值。
Now, if you change to mutate the property to after the new value has been stored, the mutation will take affect and, as covered above, not trigger any additional calls to the property observers. 现在,如果在存储新值之后更改为将属性突变为,则该突变将生效,并且如上所述,不会触发对属性观察器的任何其他调用。 Applied to your example:
应用于您的示例:
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's comment on didSet
vs willSet
is interesting. aAlan对评论
didSet
VS willSet
很有趣。 I tried the same code but with didSet
and it did remove the elements from the array which seemed odd initially. 我尝试了相同的代码,但是使用
didSet
,它确实从数组中删除了看起来很奇怪的元素。 I think it's by design. 我认为这是设计使然。 My reasoning is:
我的理由是:
willSet
and made changes then everything would get overwritten. willSet
确实获得了对实际项目的引用并进行了更改,则所有内容都将被覆盖。 It makes all your changes nullified. willSet
well then you're going to trigger the property observer again and again and will create a feedback loop which will crash the compiler, hence the compiler behind the scene willSet
设置它,那么将一次又一次触发属性观察器,并创建一个反馈循环,这将使编译器崩溃,因此,幕后的编译器将didSet
. didSet
并非如此。 You wait...read the value and then make your decision.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.