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?
source to share
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:
-
Gets a pointer to a compiler-generated global flag that determines whether a static variable has been initialized.
-
Calls
swift_once
with this pointer along with a function that initializes the static variable (this is the initializer expression you give it, i.e.= Hat()
). On Apple platforms,swift_once
it willdispatch_once_f
simply switch to . -
Returns a pointer to the static variable store, which can then be read and mutated since the store has a static lifetime.
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.
source to share
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
source to share