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