In Swift why assigning to a static variable also calls its getter

I understand that in Swift, static vars are implicitly lazy: stack overflow

But I don't understand why this is happening:

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

      

What I can see is that assigning to a static var also calls a getter in a sense. This is not interesting to me. What's going on here? Why isn't the appointment just happening?

+2


source to share


2 answers


This is because static and global stored variables are currently stored (all of this can be changed) if only one compiler gets the compiler - unsafeMutableAddressor

which gets a pointer to the variable store (this can be seen by examining SIL or IR radiation ).

This accessor is simple:

So it makes (more or less) the Objective-C equivalent of a thread-safe lazy initialization scheme:

+(Hat*) hat {

    static Hat* sharedHat = nil;
    static dispatch_once_t oncePredicate;

    dispatch_once(&oncePredicate, ^{
        sharedHat = [[Hat alloc] init];
    });

    return sharedHat;
}

      



The main difference is that Swift returns a pointer to the store sharedHat

(a pointer to a reference), not sharedHat

itself (just a reference to an instance).

Since this is the only and only accessor for static and global stored variables, in order to perform the assignment Swift must be called to obtain a pointer to the store. Therefore, if it has not been initialized yet, then the accessory must first initialize it to its default value (since it has no idea what the caller is going to do with it) before the caller sets it to a different value.

This behavior is indeed somewhat unintuitive and has been filed as a bug . As Jordan Rose says in the report's comments:

This is currently by design, but it might be worth redesigning.

Therefore, this behavior may well change in a future version of the language.

+2


source


Same solution as in Setting initialization to lazy static variable first assign?

Try lazy loading:

struct HatInjector {
    private static var _hat: HatType?
    static var hat: HatType {
        get { return _hat ?? Hat() }
        set(value) { _hat = value }
    }
}

      

Or:

struct HatInjector {
    private static var _hat: HatType?
    static var hat: HatType {
        get {
            if _hat == nil {
                _hat = Hat()
            }
            return _hat!
        }
        set(value) { _hat = value }
    }
}

      

Reason: Static var in your code is optional. So when using it, swift has to ensure that it is non-zero (quickly saved!). Therefore, the compiler request sets the initial value. You cannot define:



static var prop1: MyProtocol

      

This will result in a compiler error. If you define

static var prop1: MyProtocol?

      

it will be valid because it is a shortcut for

static var prop1: MyProtocol? = nil

      

0


source







All Articles