扩展可能不包含存储的属性,但为什么允许静态

时间:2017-08-02 17:24:08

标签: swift swift-extensions

扩展不能包含存储属性,但为什么可以在扩展中定义静态存储属性?

我也没有找到任何文档提到扩展中允许使用静态属性。

extension String {
  static let test = "Test"
  static var test2 = "Test2"
}

1 个答案:

答案 0 :(得分:22)

扩展程序不能包含存储的实例属性。为什么?因为添加实例属性会更改该类型实例的大小。如果一个模块添加一个扩展名,使Int现在长2个字,会发生什么?例如,当它从另一个模块获得Int时,它应该会发生什么呢?

扩展中允许 static 存储属性的原因仅仅是因为它们具有静态生命周期;它们独立于您正在扩展的给定类型的任何实例而存在。实际上它们只不过是全局存储变量,只是命名空间到类型。因此,可以自由添加它们,而不会影响已经编译过的代码而不了解它们。

但值得注意的是,目前存在三个限制静态存储属性的限制。

1。您无法在泛型类型

上定义static存储的属性

这将需要为通用占位符的每个单独专业化单独存储属性。例如,使用:

struct S<T> {

    static var foo: Int {
        return 5
    }

    static let bar = "" // error: Static stored properties not supported in generic types
}

foo的{​​{1}}的个别专业化被调用,例如SS<Int>.foo以及S<Float>.foo本身的 (事实上​​,S目前甚至不是一种类型,它要求S满足; T(可能)会相同。例如,它将被称为bar,而不是S<Int>.bar

这是一个重要的细节,因为调用静态成员的元类型作为隐式S.bar参数传递给接收者。这可以在静态属性初始化表达式中访问;因此允许他们调用其他静态方法。

因此,能够在泛型类型的不同特化上调用相同的静态属性初始化器,可能会为每个特性创建不同的属性值(考虑self的简单情况) 。因此,我们需要为每个存储单独存储。

然而,总而言之,编译器/运行时无法做到这一点的原因并不存在,并且它可能在该语言的未来版本中完成。虽然反对这一点的一个论点是,在某些情况下它可能会产生令人困惑的行为。

例如,考虑:

static let baz = T.self

如果运行时每次在import Foundation struct S<T> { static let date = Date() } 的新专业化访问时隐式生成date的新存储,则S<T>将不等于S<Float>.date;这可能会令人困惑和/或不受欢迎。

2。您无法在协议扩展

中定义S<Int>.date存储的属性

这主要是从前一点开始的。协议扩展中的static存储属性需要为该协议的每个符合类型单独存储(但同样;编译器/运行时没有理由不这样做。)

这对于协议是必要的,因为协议扩展中的static成员是协议类型本身上的不是成员。他们是符合协议的具体类型的成员。

例如,如果我们有:

static

我们无法访问协议类型本身protocol P {} extension P { static var foo: Int { return 5 } static let bar = "" // error: Static stored properties not supported in generic types // (not really a great diagnostic) } struct S : P {} struct S1 : P {} ,我们不能说foo。我们只能说P.fooS.foo。这很重要,因为S1.foo的getter可以调用foo上的静态协议要求;但如果selfself(即协议类型本身),则protocols don't conform to themselves无法实现这一点。

P.self存储的static属性(可能)也是如此。

3。您无法定义bar存储的属性

我不相信在类体本身中这样的声明会有任何问题(它只会等同于class存储属性支持的计算class属性。

在扩展中可能存在问题,因为扩展无法将新成员添加到Swift类vtable中(尽管如果适用,它们可以添加到Obj-C对应项)。因此,在大多数情况下,它们不会被动态分派(因此实际上是static,因此final)。虽然如此,static 计算属性目前在扩展中是允许的,因此为了保持一致性可能是允许的。