为什么我可以定义一个依赖于同一类型的另一个静态常量的静态常量,而不是属性?

Why can I define a static constant that depends on another static constant in the same type, but not for properties?

我熟悉在所有属性初始化之前尝试访问 self 时遇到的错误。我一直不明白的一件事是,为什么我可以在相同 type 中初始化依赖于其他 static 属性的 static 常量而不会出现任何错误。

这种行为的原因是什么?为什么定义 static 常量不会给我编译时错误而 属性 常量会?

一些代码:

struct MyStruct {
    static let myStatic = 1
    // No error on this line...
    static let myStaticPlusOne = myStatic + 1

    let myInstance = 1
    // ... But a compile time error on this one! What makes the difference?
    let myInstancePlusOne = myInstance + 1
}

必须是这个结构被加载到内存或其他什么地方的时候,加载静态变量。我看到这个时间与创建 type 的新实例的时间完全相同。我无法清楚地看到这两者之间的区别。两者都有一个 属性 被初始化而另一个没有被初始化的时间。那为什么我仍然可以从另一个静态变量访问我的静态变量?

类型和实例。

  • MyStruct 类型始终存在。 static 属性属于类型。所以他们只是坐在那里,可以做任何他们喜欢的事情或以任何方式联系在一起。好的,是的,它必须在程序启动时就存在,但在后台静态 属性 初始化程序都是 lazy 所以一个人可以依赖另一个人(不是以循环方式)当然)。

  • MyStruct 的实例是必须创建的东西,每次你说 MyStruct(...)。当您这么说时,必须初始化实例属性。实例(不是static)属性属于实例。所以它的初始化器的值不能引用 self 因为 self 正是我们正在创建的东西,即实例。这一行:

    let myInstancePlusOne = myInstance + 1
    

    ...真正的意思是

    let myInstancePlusOne = self.myInstance + 1
    

    ...但这正是你不能说的;在您初始化此 属性 时还没有 self,这是您正在初始化的内容。您可以通过 声明 属性 lazy(对语法进行其他调整)来解决此问题。

有以下两个原因:

  • 每个全局属性懒得电脑。不直观的是它们不需要用 lazy 标记。

  • 不允许循环依赖


马特所说的更简单的例子:

class X {
    static var a = 10
    static var b = 20
}

class Y {
    static var a = 10 * Y.b
    static var b = 20
}

class Z {
    static var a = 10 * Z.b
    static var b = 20 * Z.a // ERROR: Type 'Z' has no member 'a' 
}

class Z 无效,因为你有循环依赖。但是我觉得错误还不清楚。

这意味着编译器将尝试找出依赖关系并从不依赖于任何内容的值开始构建类型,然后继续构建其他类型属性。但如果它不能那样做,它就会出错!

正如您在评论中提到的。 docs 说:

Global constants and variables are always computed lazily, in a similar manner to Lazy Stored Properties. Unlike lazy stored properties, global constants and variables do not need to be marked with the lazy modifier.

Local constants and variables are never computed lazily.


以下将通过编译器,但在访问 属性

时会崩溃
class C {
    lazy var a : Int = {
        let _a = self.b + 30
        return _a
    }()

    lazy var b : Int = {
        let _b = self.a + 20
        return _b
    }()
}

let d = C().a // ERROR!

因此,正如您所看到的,您需要根据存储的值构建一个 instance/type,然后根据它计算其他属性。