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

Extension may not contain stored property but why is static allowed

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

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

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

扩展不能包含存储的 实例 属性。为什么?因为添加一个实例 属性 会改变该类型实例的大小。如果一个模块添加了一个扩展,使得 Int 现在是 2 个字长,会发生什么情况?例如,当它从另一个模块中得到一个 Int 时,它们的大小仍然是 1 个字,那么会发生什么?

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

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

1。您不能在通用类型

上定义 static 存储 属性

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

struct S<T> {

    static var foo: Int {
        return 5
    }

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

正如 fooS 的个别特化上被调用一样,例如 S<Int>.fooS<Float>.foo 以及 notS本身(事实上,S目前甚至不是一个类型,它需要满足T); bar 会(很可能)是一样的。例如,它会被称为 S<Int>.bar,而不是 S.bar

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

因此,能够在泛型的 不同 特化上调用相同的静态 属性 初始化程序将有可能为 属性 创建不同的值每个(考虑 static let baz = T.self 的简单情况)。因此,我们需要为它们中的每一个单独存储。

然而,话虽如此,compiler/runtime 并没有真正的理由不能做到这一点,而且在该语言的未来版本中它可能会做到这一点。尽管反对这一点的一个论点是它在某些情况下可能会产生令人困惑的行为。

例如,考虑:

import Foundation

struct S<T> {
    static let date = Date()
}

如果运行时每次在 S<T> 的新特化上访问它时隐式为 date 生成新存储,那么 S<Float>.date 将不等于 S<Int>.date;这可能令人困惑 and/or 不受欢迎。

2。您不能在协议扩展

中定义 static 存储 属性

这主要是上一点的延续。在协议扩展中存储 属性 的 static 需要为该协议的每个符合类型单独存储(但同样;compiler/runtime 没有理由不能这样做)。

这对于协议来说是必要的,因为协议扩展中的 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.fooS1.foo。这很重要,因为 foo 的 getter 可以调用 self 上的静态协议要求;然而,如果 selfP.self(即协议类型 本身 ),这是不可能的,因为 .

static 存储的属性(例如 bar.

也(可能)遵循同样的规则

3。您不能定义 class 存储 属性

我认为在 class 主体中这样的声明不会有任何问题(它只是等同于由一个支持的计算 class 属性 static 存储 属性)。

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