In Swift, why does assigning to a static variable also invoke its getter
This is because static and global stored variables are currently (this is all subject to change) only given one accessor by the compiler – unsafeMutableAddressor
, which gets a pointer to the variable's storage (this can be seen by examining the SIL or IR emitted).
This accessor:
Gets a pointer to a compiler-generated global flag determining whether the static variable has been initialised.
Calls
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 a pointer to the static variable's storage, which the caller is then free to read and mutate – as the storage has static lifetime.
So it does more or less the equivalent of the Objective-C thread-safe lazy initialisation pattern:
+(Hat*) hat {
static Hat* sharedHat = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
sharedHat = [[Hat alloc] init];
});
return sharedHat;
}
The main difference being that Swift gives back a pointer to the storage of sharedHat
(a pointer to a reference), rather than sharedHat
itself (just a reference to the instance).
Because this is the one and only accessor for static and global stored variables, in order to perform an assignment, Swift needs to call it in order to get the pointer to the storage. Therefore, if it wasn't initialised already – the accessor needs to first initialise it to its default value (as it has no idea what the caller is going to do with it), before the caller then sets it to another value.
This behaviour is indeed somewhat unintuitive, and has been filed as a bug. As Jordan Rose says in the comments of the report:
This is currently by design, but it might be worth changing the design.
So this behaviour could well change in a future version of the language.
Setting lazy static variable first initializes then assigns?
You need lazy loading. Try this:
struct MyMap {
private static var _prop1: MyProtocol?
static var prop1: MyProtocol {
get { return _prop1 ?? MyClass() }
set(value) { _prop1 = value }
}
}
Or this:
struct MyMap {
private static var _prop1: MyProtocol?
static var prop1: MyProtocol {
get {
if _prop1 == nil {
_prop1 = MyClass()
}
return _prop1!
}
set(value) { _prop1 = value }
}
}
Implicitly lazy static members in Swift
The static
property defines a "type property", one that is instantiated once and only once. As you note, this happens lazily, as statics behave like globals. And as The Swift Programming Language: Properties says:
Global constants and variables are always computed lazily, in a similar manner to Lazy Stored Properties. Unlike lazy stored properties, global constants and variables do not need to be marked with the
lazy
modifier.
This implicitly lazy behavior is because, as the Swift Blog: Files and Initialization says:
it allows custom initializers, startup time in Swift scales cleanly with no global initializers to slow it down, and the order of execution is completely predictable.
They consciously designed it that way to avoid unnecessarily delaying the startup of the app.
If you want to instantiate the static
property at some particular point in your app (rather than deferring it to where it's first used), simply reference this static
property at that earlier point and the object will be initialized at that time. Given the efforts we put into reducing the latency in starting our apps, you generally wouldn't to want this synchronously during the initial launch of the app, but you can do it wherever you want.
Note that unlike lazy
instance properties, the instantiating of globals and static
variables is thread-safe.
Static variables in protocols
A static
variable belongs to the class, not the instance. You can refer to the class by calling dynamicType
:
print(a.dynamicType.x)
Why doesn't Swift use getter and setter for properties as much as Java or C#?
Java historically had different syntax for direct property access versus a method call that gets the value (a "getter"). Since you might someday want to override a property with a method, for consistency it is common to create a method in all cases.
Swift avoids this problem by having the same syntax for direct property access and "getters" (computed properties). This means you can change your mind later without impacting callers, and so there is no reason to create a method "just in case."
A computed property is defined as one with a "getter" (a get
method) in Swift.
Singelton and Static variable access in objective C
Solution:
Using the method + (void)initialize
: Apple Documentation
And we declare the variable as static
City.m
#import "City.h"
static NSArray *cities = nil;
@implementation City
+ (void)initialize
{
if (self == [City self])
{
if (!cities)
{
cities = [self loadCitiesFromPlist];
}
}
}
-(id) initWithDictionary: (NSDictionary *) dictionay
{
self = [super init];
// convert the NSDictionary to City object
return self;
}
// This method load all cities from the file
+(NSArray *) loadCitiesFromPlist
{
NSLog(@"loadCitiesFromPlist");
NSMutableArray *citiesArray = [NSMutableArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"cities" ofType:@"plist"]];
NSMutableArray *citiesObjectsArray = [[NSMutableArray alloc] init];
for (NSDictionary *cityDict in citiesArray)
{
[ citiesObjectsArray addObject: [[City alloc]initWithDictionary:cityDict]] ;
}
return citiesObjectsArray;
}
+(City*) getCityById:(NSString *) cityId
{
NSUInteger barIndex = [cities indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
City* city = (City*) obj;
if(city.cityId == [cityId intValue])
{
*stop = YES;
return YES;
}
return NO;
}];
if (barIndex != NSNotFound)
{
return [cities objectAtIndex:barIndex];
}
else
{
return nil;
}
}
+(NSArray*) getAllCitiesNames
{
NSMutableArray *citiesNames =[[NSMutableArray alloc] init];
for(City* city in cities)
{
[citiesNames addObject:city.name];
}
return citiesNames;
}
@end
More Information in this SO answer
Execute a method when a variable value changes in Swift
You can simply use a property observer for the variable, labelChange
, and call the function that you want to call inside didSet
(or willSet
if you want to call it before it has been set):
class SharingManager {
var labelChange: String = Model().callElements() {
didSet {
updateLabel()
}
}
static let sharedInstance = SharingManager()
}
This is explained in Property Observers.
I'm not sure why this didn't work when you tried it, but if you are having trouble because the function you are trying to call (updateLabel
) is in a different class, you could add a variable in the SharingManager
class to store the function to call when didSet
has been called, which you would set to updateLabel
in this case.
Edited:
So if you want to edit a label from the ViewController, you would want to have that updateLabel() function in the ViewController class to update the label, but store that function in the singleton class so it can know which function to call:
class SharingManager {
static let sharedInstance = SharingManager()
var updateLabel: (() -> Void)?
var labelChange: String = Model().callElements() {
didSet {
updateLabel?()
}
}
}
and then set it in whichever class that you have the function that you want to be called, like (assuming updateLabel is the function that you want to call):
SharingManager.sharedInstance.updateLabel = updateLabel
Of course, you will want to make sure that the view controller that is responsible for that function still exists, so the singleton class can call the function.
If you need to call different functions depending on which view controller is visible, you might want to consider Key-Value Observing to get notifications whenever the value for certain variables change.
Also, you never want to initialize a view controller like that and then immediately set the IBOutlets of the view controller, since IBOutlets don't get initialized until the its view actually get loaded. You need to use an existing view controller object in some way.
Hope this helps.
Create a Setter Only in Swift
Well if I really have to, I would use this.
Swift compiler supports some attributes on getters, so you can use @available(*, unavailable)
:
public subscript(index: Int) -> T {
@available(*, unavailable)
get {
fatalError("You cannot read from this object.")
}
set(v) {
}
}
This will clearly deliver your intention to the code users.
Related Topics
How to Check If 'Any(Not Any)' Is Nil or Not in Swift
Convert Dictionary to Query String in Swift
How to Manually Decode an an Array in Swift 4 Codable
Does Swift Have an Implicit Object Initializer, Like in C#
How to Open Another Window in MACos in Swift with Cocoa
What Is the Backslash(\) Used for in Swiftui
What's the Difference Between a View and a Viewcontroller
Treat *Some* Warnings as Errors in Swift
How to Detect Switch Between MACos Default & Dark Mode Using Swift 3
How to Open Safari Extension Toolbaritem Popover Programmatically
How to Integrate Latest Sdwebimage API in My Swift Based Project
At Runtime, How Does Swift Know Which Implementation to Use
What Are the Precedence Levels of the Swift Operators
Image Upload in Vapor 3 Using Postgresql
Nsnumberformatter:Show 'K' Instead of ',000' in Large Numbers