为什么不能在其willSet事件中从数组中删除元素?

时间:2018-07-23 14:46:18

标签: arrays swift value-type didset property-observer

逻辑是在数组具有指定数量的元素时清除它。我可以将检查放在Array之外,但是我试图在Array的willSet事件中查看该怎么办。结果是Array中的元素保持静止。

这是代码

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仅包含一个元素,而实际结果为四个。

3 个答案:

答案 0 :(得分:2)

该行为与值类型/引用类型无关

请注意警告

  

尝试将属性“ MyArr”存储在其自己的willSet中,该属性将被新值覆盖

这意味着修改willSet中的对象无效。

答案 1 :(得分:2)

引用the Language Guide - Properties - Property Observers [强调我的]:

  

财产观察员

     

财产观察员观察并响应财产变化   值。

     

...

     

您可以选择定义一个或两个这些观察者   属性:

     
      
  • willSet在存储值之前被称为
  •   
  • didSet在存储新值后立即被调用
  •   

在尝试使用willSet属性观察器时,您在willSet块中观察到的属性的任何变异先于存储{{1} },紧跟在newValue块之后。这意味着您实际上是在尝试将willSet的“旧副本”突变为新值之前。

可以说这可能是非法的,因为myArr的任何突变都应导致调用任何属性观察者,从而导致属性观察者内部的属性发生变异(引用的变异或对于值类型为)可能会导致对属性观察器的递归调用。但是,情况并非如此,特别是对于myArr情况,如@vadian's answer中指出的那样,发出警告。在财产观察者中改变财产本身不会触发财产观察者这一事实并没有得到很好的证明,但是the Language Guide - Properties - Type Properties中的示例指出了这一点[强调我的]:

  

查询和设置类型属性

     

...

     

willSet属性有一个currentLevel属性观察者可以检查   设置didSet的值。 ...

     

注意

     

在这两个检查的第一个中,currentLevel观察者设置   didSet更改为其他值。 但这不会导致   再次调用观察者。

这也是我们可以预期的一种特殊情况,例如currentLevel是合并例如边界检查将给定的属性值限制在一定范围内;也就是说,如果新值超出范围,则用有界值覆盖新值。

现在,如果在存储新值后更改为将属性更改为 ,则该更改将生效,并且如上所述,不会触发对属性观察器的任何其他调用。应用于您的示例:

didSet

答案 2 :(得分:1)

aAlan对didSetwillSet的评论很有趣。我尝试了相同的代码,但使用了didSet,并且它从数组中删除了最初看起来很奇怪的元素。我认为这是设计使然。我的理由是:

  • 如果您确实确实在willSet中获得了对实际项目本身的引用并进行了更改,则所有内容都会被覆盖。它使所有更改无效。因为您正在之前进行此操作,所以您甚至都阅读了即将发生的事情。另外(重复dfri所说的内容),如果再次在willSet内设置它,则将再次触发属性观察器 ,并创建一个反馈循环,该循环将导致编译器,因此幕后的编译器创建一个避免所有这些情况的副本... 只会抑制此突变,即不允许这样做。它不会创建副本...简单地说就是忽略它。通过抛出模糊的警告(因为它并没有真正告诉您它将忽略更改)来做到这一点:

enter image description here

  • didSet来说不是一样。您等待...读取值,然后做出决定。您知道该属性的值。