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

Boo*_*oon 18 swift swift-extensions

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

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

extension String {
  static let test = "Test"
  static var test2 = "Test2"
}
Run Code Online (Sandbox Code Playgroud)

Ham*_*ish 28

扩展不能包含存储的实例属性.为什么?因为添加实例属性会更改该类型实例的大小.如果一个模块添加一个扩展名,使得a 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
}
Run Code Online (Sandbox Code Playgroud)

正如foo被称为对个人专业化S,如S<Int>.fooS<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()
}
Run Code Online (Sandbox Code Playgroud)

如果运行时隐式生成新的存储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 {}
Run Code Online (Sandbox Code Playgroud)

我们无法访问foo协议类型本身,我们不能说P.foo.我们只能说S.fooS1.foo.这很重要,因为foo吸气剂可以满足静态协议要求self; 但是,如果selfP.self(即协议类型本身),这是不可能的,因为协议不符合它们自己.

对于static存储的属性(例如),也可能(可能)遵循bar.

3.您无法定义class存储的属性

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

然而,它在扩展潜在的问题,因为扩展无法添加新成员到斯威夫特类虚函数表(尽管他们可以(如果适用)添加给OBJ-C对应).因此,在大多数情况下,他们不会被动态分派到(这样会有效地final,因此static).虽然如此,class 计算属性目前在扩展中是允许的,因此为了保持一致性可能是允许的.