在 Swift 中,为什么分配给静态变量也会调用它的 getter
In Swift, why does assigning to a static variable also invoke its getter
我知道在 Swift 中,静态变量是隐式惰性的:
但我不清楚为什么会这样:
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
我看到的是对静态变量的赋值,在某种意义上也调用了 getter。这对我来说不直观。这里发生了什么?为什么不只发生赋值?
与
中相同的解决方案
尝试延迟加载:
struct HatInjector {
private static var _hat: HatType?
static var hat: HatType {
get { return _hat ?? Hat() }
set(value) { _hat = value }
}
}
或者:
struct HatInjector {
private static var _hat: HatType?
static var hat: HatType {
get {
if _hat == nil {
_hat = Hat()
}
return _hat!
}
set(value) { _hat = value }
}
}
原因:
代码中的静态变量不是可选的。因此在使用它时 swift 必须确保它不是 nil(swift 是保存!)。因此编译器要求你设置一个初始值。您不能定义:
static var prop1: MyProtocol
这将导致编译器错误。如果你定义
static var prop1: MyProtocol?
它将有效,因为它是
的快捷方式
static var prop1: MyProtocol? = nil
这是因为静态和全局存储变量目前(这是所有可能会发生变化)编译器只给了一个访问器——unsafeMutableAddressor
,它得到一个指向变量存储的指针(可见by examining the SIL or IR emitted)。
这个存取器:
获取指向编译器生成的全局标志的指针,确定静态变量是否已初始化。
调用 swift_once
with this pointer, along with a function that initialises the static variable (this is the initialiser expression you give it, i.e = Hat()
). On Apple platforms, swift_once
simply forwards onto dispatch_once_f
.
Returns 一个指向静态变量存储的指针,调用者可以自由读取和修改——因为存储具有静态生命周期。
所以它或多或少相当于 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需要调用它才能获得指向存储的指针。因此,如果它还没有初始化——访问者需要先将它初始化为默认值(因为它不知道调用者要用它做什么),然后再调用者 then 将其设置为另一个值。
这种行为确实有些不直观,一直filed as a bug。正如 Jordan Rose 在报告的评论中所说:
This is currently by design, but it might be worth changing the design.
所以这种行为很可能会在语言的未来版本中改变。
我知道在 Swift 中,静态变量是隐式惰性的:
但我不清楚为什么会这样:
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
我看到的是对静态变量的赋值,在某种意义上也调用了 getter。这对我来说不直观。这里发生了什么?为什么不只发生赋值?
与
尝试延迟加载:
struct HatInjector {
private static var _hat: HatType?
static var hat: HatType {
get { return _hat ?? Hat() }
set(value) { _hat = value }
}
}
或者:
struct HatInjector {
private static var _hat: HatType?
static var hat: HatType {
get {
if _hat == nil {
_hat = Hat()
}
return _hat!
}
set(value) { _hat = value }
}
}
原因: 代码中的静态变量不是可选的。因此在使用它时 swift 必须确保它不是 nil(swift 是保存!)。因此编译器要求你设置一个初始值。您不能定义:
static var prop1: MyProtocol
这将导致编译器错误。如果你定义
static var prop1: MyProtocol?
它将有效,因为它是
的快捷方式static var prop1: MyProtocol? = nil
这是因为静态和全局存储变量目前(这是所有可能会发生变化)编译器只给了一个访问器——unsafeMutableAddressor
,它得到一个指向变量存储的指针(可见by examining the SIL or IR emitted)。
这个存取器:
获取指向编译器生成的全局标志的指针,确定静态变量是否已初始化。
调用
swift_once
with this pointer, along with a function that initialises the static variable (this is the initialiser expression you give it, i.e= Hat()
). On Apple platforms,swift_once
simply forwards ontodispatch_once_f
.Returns 一个指向静态变量存储的指针,调用者可以自由读取和修改——因为存储具有静态生命周期。
所以它或多或少相当于 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需要调用它才能获得指向存储的指针。因此,如果它还没有初始化——访问者需要先将它初始化为默认值(因为它不知道调用者要用它做什么),然后再调用者 then 将其设置为另一个值。
这种行为确实有些不直观,一直filed as a bug。正如 Jordan Rose 在报告的评论中所说:
This is currently by design, but it might be worth changing the design.
所以这种行为很可能会在语言的未来版本中改变。