用存储的属性覆盖计算属性

时间:2017-10-10 22:29:07

标签: swift properties

假设我有一个类A和B,如下所示:

class A {
    var x: Int { return 9 }
    func p() {  print(x) }
}

class B: A {
    ...
}

let v: A = B()
v.p()

然后我以三种不同的方式覆盖B中的x:

class B: A {

    // OPTION 1: FINE - prints 10
    override var x: Int { return 10 }

    // OPTION 2: ERROR - cannot override with a stored property 'x'
    override var x = 10

    // OPTION 3: FINE - prints 10
    override lazy var x = 10

}

有人可以解释一下使用存储属性覆盖计算属性的错误,为什么当我声明它是懒惰时突然修复了所有内容?

1 个答案:

答案 0 :(得分:2)

没有真正的技术原因导致您无法使用存储属性覆盖属性;它有getter和setter(访问器),它可以覆盖要覆盖的属性的访问器。它应该等同于覆盖计算属性,然后转发到存储的属性。

但是,我确实预见到允许这样一个轻微的复杂性:属性观察者覆盖。

考虑以下示例:

class C {
  var f: Int {
    get { return 5 }
    set { print("C says f was set") }
  }
}

class D : C {
  override var f: Int {
    didSet {
      print("D says f was set")
    }
  }
}

let d = D()
print(d.f)
d.f = 7
print(d.f)

// 5
// C says f was set
// D says f was set
// 5

我们可以在f中覆盖didSet' D。在这种情况下,override var f: Int基本上被视为具有getter和setter的计算属性,该getter和setter转发到super,并且另外调用setter中的didSet实现。

此处D未引入实际的存储空间。那么为什么这会有问题呢?那么,我们如何告诉编译器我们实际上想要f的存储?添加初始化表达式(= 10)可以传达该表达式,但并非所有存储的属性都具有默认值;大多数都是从班级初始化的。指定的初始化者。我们可能需要某种属性,但对于这种有限的用例,它似乎不是对语言特别有用的改变。

lazy案例很明确,因为我们无法向他们添加属性观察者。

尽管如此,你提出的具体案例是明确的;因为基本属性只有一个getter,所以没有属性观察者可以覆盖。我建议你file a bug(希望)看看Swift团队对此有何看法。

您始终可以使用计算属性覆盖获得相同的结果,然后通过以下方式转发到存储的属性:

class C {
  var f: Int {
    return 9
  }
}

class D : C {

  private var _f: Int = 10

  override var f: Int {
    get { return _f }
    set { _f  = newValue }
  }
}

let d = D()
print(d.f) // 10
d.f = 7
print(d.f) // 7