扩展不能包含存储的属性,但是为什么可以在扩展内定义静态存储的属性呢?
我也没有找到任何文档提及扩展中允许使用静态属性。
extension String { static let test = "Test" static var test2 = "Test2" }
扩展不能包含存储的 实例 属性。为什么?因为添加实例属性会更改该类型的实例的大小。如果一个模块添加了一个扩展名,使得an Int现在为2个字,会发生什么情况?例如,当它Int从另一个仍为1个字大小的模块中获取一个时,应该怎么办?
Int
扩展中允许使用 静态 存储属性的原因仅仅是因为它们具有静态生存期。它们独立于要扩展的给定类型的任何实例而存在。实际上,它们只不过是全局存储的变量,只是命名空间为一种类型。因此,可以自由添加它们,而不会影响在不了解它们的情况下已经编译的代码。
但是,值得注意的是,当前在定义静态存储属性方面存在三个限制。
static
对于通用占位符的每个单独的专业化,这将需要单独的属性存储。例如,使用:
struct S<T> { static var foo: Int { return 5 } static let bar = "" // error: Static stored properties not supported in generic types }
就像foo对S,S<Int>.foo和S<Float>.foo而 不是 对S自身进行专门化所要求的一样(实际上;S目前甚至还不是一种类型,它都需要T满足)。bar(可能)是相同的。这将被称为,例如S<Int>.bar,没有S.bar。
foo
S
S<Int>.foo
S<Float>.foo
T
bar
S<Int>.bar
S.bar
这是一个重要的细节,因为调用静态成员的元类型将作为隐式self参数传递给接收者。这可以在静态属性初始化程序表达式中访问;因此允许他们调用其他静态方法。
self
因此,能够在泛型类型的 不同 专长上调用相同的静态属性初始化程序,就有可能为每个类型创建不同的属性值(考虑的简单情况static let baz = T.self)。因此,我们需要为它们中的每一个单独存储。
static let baz = T.self
综上所述,没有真正的理由使编译器/运行时无法做到这一点,并且很可能在该语言的未来版本中做到这一点。尽管有一个反对的说法,那就是在某些情况下它可能会产生混乱的行为。
例如,考虑:
import Foundation struct S<T> { static let date = Date() }
如果运行时隐式地产生新的存储date每次它获取上的一个新的专业化的访问S<T>,那么S<Float>.date就不会相等S<Int>.date; 这可能会造成混淆和/或不期望。
date
S<T>
S<Float>.date
S<Int>.date
这主要是从上一点开始的。一个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。这很重要,因为foo吸气剂可以调用静态协议要求self; 但是,这是不可能self的P.self(即协议类型 本身),因为协议不符合自身。
P.foo
S.foo
S1.foo
P.self
对于static存储的属性(例如),也可能会发生同样的情况bar。
class
我不认为类主体中的此类声明会存在任何问题(它等同于class由static存储属性支持的计算属性)。
但是,在扩展中可能 会 出现问题,因为扩展无法将新成员添加到Swift类vtable中(尽管可以将它们添加到Obj-C对应对象中)。因此,在大多数情况下,它们不会动态分配给(实际上是final,因此static)。尽管如此,但扩展名当前允许使用class计算 属性,因此出于一致性考虑,可以允许使用 计算 属性。
final