扩展不能包含存储属性,但为什么可以在扩展中定义静态存储属性?
我也没有找到任何文档提到扩展中允许使用静态属性。
extension String {
static let test = "Test"
static var test2 = "Test2"
}
答案 0 :(得分:22)
扩展程序不能包含存储的实例属性。为什么?因为添加实例属性会更改该类型实例的大小。如果一个模块添加一个扩展名,使Int
现在长2个字,会发生什么?例如,当它从另一个模块获得Int
时,它应该会发生什么呢?
扩展中允许 static 存储属性的原因仅仅是因为它们具有静态生命周期;它们独立于您正在扩展的给定类型的任何实例而存在。实际上它们只不过是全局存储变量,只是命名空间到类型。因此,可以自由添加它们,而不会影响已经编译过的代码而不了解它们。
但值得注意的是,目前存在三个限制静态存储属性的限制。
static
存储的属性
这将需要为通用占位符的每个单独专业化单独存储属性。例如,使用:
struct S<T> {
static var foo: Int {
return 5
}
static let bar = "" // error: Static stored properties not supported in generic types
}
foo
的{{1}}的个别专业化被调用,例如S
和S<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
;这可能会令人困惑和/或不受欢迎。
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.foo
或S.foo
。这很重要,因为S1.foo
的getter可以调用foo
上的静态协议要求;但如果self
为self
(即协议类型本身),则protocols don't conform to themselves无法实现这一点。
对P.self
存储的static
属性(可能)也是如此。
bar
存储的属性我不相信在类体本身中这样的声明会有任何问题(它只会等同于class
存储属性支持的计算class
属性。
但 在扩展中可能存在问题,因为扩展无法将新成员添加到Swift类vtable中(尽管如果适用,它们可以添加到Obj-C对应项)。因此,在大多数情况下,它们不会被动态分派(因此实际上是static
,因此final
)。虽然如此,static
计算属性目前在扩展中是允许的,因此为了保持一致性可能是允许的。