我理解在Swift中,静态变量是隐式的:https://stackoverflow.com/a/34667272/1672161
但我不清楚为什么会这样:
protocol HatType {}
class Hat: HatType {
init() { print("real hat") }
}
class MockHat: HatType {
init() { print("mock hat") }
}
struct HatInjector {
static var hat: HatType = Hat()
}
HatInjector.hat = MockHat()
// Output:
// real hat
// mock hat
我所看到的是,对静态var的赋值也在某种意义上调用了getter。这对我来说并不直观。这里发生了什么?为什么这项任务不会发生?
答案 0 :(得分:6)
这是因为静态和全局存储变量当前(这是所有可能会发生变化)仅由编译器提供一个访问器 - unsafeMutableAddressor
,它获取指向变量的指针&# 39;存储(可以看到by examining the SIL or IR emitted)。
此访问者:
获取指向编译器生成的全局标志的指针,该标志确定静态变量是否已初始化。
使用此指针调用swift_once
,以及初始化静态变量的函数(这是您提供的初始化表达式,即= Hat()
)。在Apple平台上,swift_once
只是forwards onto dispatch_once_f
。
返回指向静态变量存储的指针,然后调用者可以自由读取和变异 - 因为存储具有静态生存期。
因此它或多或少地等同于Objective-C线程安全的延迟初始化模式:
+(Hat*) hat {
static Hat* sharedHat = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
sharedHat = [[Hat alloc] init];
});
return sharedHat;
}
主要区别在于Swift返回指向sharedHat
(指向引用的指针)存储的指针,而不是sharedHat
本身(仅指向实例)。
因为这是静态和全局存储变量的唯一和访问器,为了执行赋值,Swift需要调用它以获取指向存储的指针。因此,如果它尚未初始化 - 访问者需要首先将其初始化为其默认值(因为它不知道调用者将使用它做什么),然后调用者然后将其设置为另一个值。
这种行为确实有点不直观,并且一直是filed as a bug。正如乔丹·罗斯在报告的评论中所说:
这是目前的设计,但可能值得改变设计。
所以这种行为很可能会在未来的语言版本中发生变化。
答案 1 :(得分:0)
与Setting lazy static variable first initializes then assigns?
中的解决方案相同尝试延迟加载:
onclick
或者:
struct HatInjector {
private static var _hat: HatType?
static var hat: HatType {
get { return _hat ?? Hat() }
set(value) { _hat = value }
}
}
原因: 代码中的静态var不是可选的。因此,当使用它时,swift必须确保它不是零(快速保存!)。因此,编译器请求您设置初始值。你无法定义:
struct HatInjector {
private static var _hat: HatType?
static var hat: HatType {
get {
if _hat == nil {
_hat = Hat()
}
return _hat!
}
set(value) { _hat = value }
}
}
这将导致编译器错误。如果你定义
static var prop1: MyProtocol
它将是有效的,因为它是
的快捷方式static var prop1: MyProtocol?