我有两个单身人士课程。我在Playground中实现了代码。代码如下
//: Playground - noun: a place where people can play
import UIKit
class A {
private init() {
print("1")
setB()
print("2")
}
static let a = A()
func setB() {
_ = B.b
}
}
class B {
private init() {
setA()
}
static let b = B()
func setA() {
print("3")
_ = A.a // Doesn't execute beyond this
print("4")
}
}
_ = A.a
我在外面创建Class A
个对象,然后创建Class B
的对象。由于A
是单例,因此已经创建了对象。然后在B
的内部构造函数中,我正在访问A
的对象。但代码并没有超出该行。上述代码的输出是
1
3
任何人都可以帮我理解代码有什么问题吗?我想知道为什么它会停止执行。没有语法错误/警告。
答案 0 :(得分:3)
由于A是单身,因此已经创建了对象。
不正确。默认情况下,静态属性是惰性的。这意味着A.a
不B.b
在首次使用之前都不会被实例化。这只是一个细节,因为任何初始化A.a
的尝试都会导致尝试在初始化完成之前重新访问静态属性(导致很可能是线程死锁)。
当您调用_ = A.a
时,您将启动一个调用链,该调用链在完成初始化之前尝试获取A.a
的引用。
// instantiate A.a
_ = A.a
// -> calls init() of A
// -> calls setB() of A
// -> instantiates B.b
// -> calls init() of B
// -> calls setA() of B
// -> tries to grab a reference to A.a, but the
// initialization of A.a has not yet finished!
// (as you can see by the lack of printing "2").
我不知道这意味着什么(未定义的行为?),但可能,因为A.a
的初始化尚未完成,A.a
在setA()
中的访问权限B
会认为是时候实例化A.a
,重新启动调用链,再次尝试在初始化之前访问A.a
;导致一个递归的调用链循环(它将“冻结”并最终导致游乐场崩溃)。
另一个可能更合理的情况,正如下面我的@MartinR所指出的那样,我们实际上并没有进入递归调用链而是一个线程死锁(由于静态属性的线程安全性),所以我们尝试在完全初始化之前访问A.a
。
在任何情况下,核心问题都是在完全初始化之前尝试访问A.a
。