在Swift中,为什么分配给静态变量也会调用它的getter

时间:2017-04-12 15:49:59

标签: swift variables static lazy-evaluation

我理解在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。这对我来说并不直观。这里发生了什么?为什么这项任务不会发生?

2 个答案:

答案 0 :(得分:6)

这是因为静态和全局存储变量当前(这是所有可能会发生变化)仅由编译器提供一个访问器 - unsafeMutableAddressor,它获取指向变量的指针&# 39;存储(可以看到by examining the SIL or IR emitted)。

此访问者:

  1. 获取指向编译器生成的全局标志的指针,该标志确定静态变量是否已初始化。

  2. 使用此指针调用swift_once,以及初始化静态变量的函数(这是您提供的初始化表达式,即= Hat())。在Apple平台上,swift_once只是forwards onto dispatch_once_f

  3. 返回指向静态变量存储的指针,然后调用者可以自由读取和变异 - 因为存储具有静态生存期。

  4. 因此它或多或少地等同于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?