What is the use of static keyword if let keyword used to define constants/immutables in swift?
I will break them down for you:
var
: used to create a variablelet
: used to create a constantstatic
: used to create type properties with eitherlet
orvar
. These are shared between all objects of a class.
Now you can combine to get the desired out come:
static let key = "API_KEY"
: type property that is constantstatic var cnt = 0
: type property that is a variablelet id = 0
: constant (can be assigned only once, but can be assigned at run time)var price = 0
: variable
So to sum everything up var and let define mutability while static and lack of define scope. You might use static var
to keep track of how many instances you have created, while you might want to use just var
for a price that is different from object to object. Hope this clears things up a bit.
Example Code:
class MyClass{
static let typeProperty = "API_KEY"
static var instancesOfMyClass = 0
var price = 9.99
let id = 5
}
let obj = MyClass()
obj.price // 9.99
obj.id // 5
MyClass.typeProperty // "API_KEY"
MyClass.instancesOfMyClass // 0
Static properties in enum
Why shouldn't you be able to do this?
Swift enums are first-class types, just like struct
s and class
es. Swift enum
s do not need to have cases, they can be completely empty types, just like how a struct
or class
does not need to have any properties.
enum Empty {} // completely valid
enum
s cannot have _ stored instance properties_, but they can have type properties (which static
properties are) and computed instance properties.
Caseless enums with static properties are often used for storing constant values. For more information on the topic, see Swift constants struct or enum
This might not be documented in the Enumerations section of the Swift docs, but nothing says that this shouldn't be possible either. On the other hand, the docs do state that enums are first-class types and there is a non-exhaustive list of features that enums share with classes and structs.
Is It Inefficient To Access A Static Property Through Computed Properties In Swift?
As with most optimization questions, it depends on your precise code and the version of the compiler, and also we don't have to guess, we can check.
By "inefficient" I'm going to assume you mean "fails to inline the accessor call."
The TL;DR is: In almost all cases the optimizer will inline this either way. There are corner cases where the accessor version is not inlined, for example if the caller is at the top-level (not inside a function) and the class is non-final. (I don't know why that's a corner-case; it may be an optimizer bug.)
I'm neutral on whether this is a good design. I'm fine with it (and occasionally use this pattern myself). But I certainly wouldn't avoid it out of performance concerns. (In cases where one extra function call would be a problem, you're going to need to hand-optimize anyway.)
The details
As with most optimization/performance questions, it will depend on your exact code and the version of the compiler. As I said, there are some corner cases where this doesn't get optimized. I tested with Swift 5.5.2.
First, I created a test program:
// Avoid the complexity around calling print()
// while ensuring that the variable is not optimized away
@inline(never)
func handle(_ x: Int) {
print(x)
}
// Stick it in a function to avoid the special handling of
// global variables
func f() {
let c = OwnerClass()
let x = OwnerClass.constant1
handle(x)
let y = c.constant1
handle(y)
}
// Make sure to call the function so it's not optimized away
f()
Then I checked it with several version of OwnerClass (I use 12345678 to make it easier to find in the output):
// Class
class OwnerClass {
static let constant1 = 12345678
var constant1:Int { get{ return OwnerClass.constant1 }}
}
// Final class
final class OwnerClass {
static let constant1 = 12345678
var constant1:Int { get{ return OwnerClass.constant1 }}
}
// Struct
struct OwnerClass {
static let constant1 = 12345678
var constant1:Int { get{ return OwnerClass.constant1 }}
}
// Instance constant
class OwnerClass {
static let constant1 = 12345678
let constant1:Int = OwnerClass.contant1
}
The only one that ever had trouble (for example, when I didn't wrap it all in a function), was the non-final class with an accessor.
To see what the optimizer does, I compiled with swiftc -emit-sil -O x.swift
. In all cases, this is what f()
compiles to:
// f()
sil hidden @$s1x1fyyF : $@convention(thin) () -> () {
bb0:
%0 = integer_literal $Builtin.Int64, 12345678 // user: %1
%1 = struct $Int (%0 : $Builtin.Int64) // users: %6, %5, %4, %2
debug_value %1 : $Int, let, name "x" // id: %2
// function_ref handle(_:)
%3 = function_ref @$s1x6handleyySiF : $@convention(thin) (Int) -> () // users: %6, %4
%4 = apply %3(%1) : $@convention(thin) (Int) -> ()
debug_value %1 : $Int, let, name "y" // id: %5
%6 = apply %3(%1) : $@convention(thin) (Int) -> ()
%7 = tuple () // user: %8
return %7 : $() // id: %8
} // end sil function '$s1x1fyyF'
The important thing to note is that the constant 12345678 is inlined into the function as %0 (wrapped into %1), and then it's used twice in %4 and %6 to call handle()
. No calls are made to the accessor. OwnerClass isn't even referenced (the creation of c
is optimized away).
How to get all static properties in Swift?
One way is to implement CustomReflectable
:
class AllDevice: CustomReflectable {
static let LIGHT_ONOFF_NH_3A_R11_01_L12 = "LIGHT_ONOFF_NH_3A_R11_01_L12"
static let PLUG_ONOFF_NH_3A_R11_01_B2AC = "PLUG_ONOFF_NH_3A_R11_01_B2AC"
static let PLUG_POWER_NH_3A_R11_01_B2AC="PLUG_POWER_NH_3A_R11_01_B2AC"
static let PLUG_ENERGY_NH_3A_R11_01_B2AC="PLUG_ENERGY_NH_3A_R11_01_B2AC"
static let PLUG_ONOFF_NH_3A_R11_02_B2B8="PLUG_ONOFF_NH_3A_R11_02_B2B8"
static let PLUG_POWER_NH_3A_R11_02_B2B8="PLUG_POWER_NH_3A_R11_02_B2B8"
static let PLUG_ENERGY_NH_3A_R11_02_B2B8="PLUG_ENERGY_NH_3A_R11_02_B2B8"
static let PLUG_ONOFF_NH_3A_R11_03_B2D2="PLUG_ONOFF_NH_3A_R11_03_B2D2"
static let PLUG_POWER_NH_3A_R11_03_B2D2="PLUG_POWER_NH_3A_R11_03_B2D2"
static let PLUG_ENERGY_NH_3A_R11_03_B2D2="PLUG_ENERGY_NH_3A_R11_03_B2D2"
func customMirror() -> Mirror {
return Mirror(self, children: [
"LIGHT_ONOFF_NH_3A_R11_01_L12": self.dynamicType.LIGHT_ONOFF_NH_3A_R11_01_L12,
"PLUG_ONOFF_NH_3A_R11_01_B2AC": self.dynamicType.PLUG_ONOFF_NH_3A_R11_01_B2AC,
"PLUG_POWER_NH_3A_R11_01_B2AC": self.dynamicType.PLUG_POWER_NH_3A_R11_01_B2AC
// etc...
])
}
}
let device = AllDevice()
let m = Mirror(reflecting: device)
// you can access m.children now
As you can see, it's not fun and involve a lot of string literal. If you change value assigned to each device type, you have to update the mirror accordingly.
It is to implement your device type constants as an enum
:
enum Device: String {
case LIGHT_ONOFF_NH_3A_R11_01_L12 = "LIGHT_ONOFF_NH_3A_R11_01_L12"
case PLUG_ONOFF_NH_3A_R11_01_B2AC = "PLUG_ONOFF_NH_3A_R11_01_B2AC"
case PLUG_POWER_NH_3A_R11_01_B2AC = "PLUG_POWER_NH_3A_R11_01_B2AC"
// etc..
static let allDevices: [Device] = [
.LIGHT_ONOFF_NH_3A_R11_01_L12,
.PLUG_ONOFF_NH_3A_R11_01_B2AC,
.PLUG_POWER_NH_3A_R11_01_B2AC
// etc...
]
}
This way the complier can offer you some error checking.
Swift singleton vs. static properties/methods
To me, a simpler alternative would be to convert all properties and methods to static, and drop the sharedInstance property.
These do not do the same thing. The recommended approach is not actually a singleton at all. It's just a well-known instance. The concept of the Singleton pattern is that there must only be one instance. The concert of the shared instance pattern is that there can be more than one instance, but there is one that you probably want, and you would like easy access to it.
The advantage of shared instances is that they are not magical. They're just instances. That means that they can be handed around as values. They can be replaced with other instances that may be configured differently. They are easier to test (because they can be passed into functions).
True singletons are a very rigid pattern that should only be used when it is absolutely necessary that no other instance exist, usually because they interact with some external unique resource in a way that would create conflicts if there were multiples (this is pretty rare). Even in this case, in Swift, you should generally just make init
private to prevent additional instances being created.
If you look around Cocoa, you'll find that shared instances are extremely common for things that would be Singletons in other frameworks, and this has been very powerful. For instance, there is a well known NotificationCenter
called default
, and it's probably the only one you've ever used. But it's completely valid to create a private NotificationCenter
that's independent (I've actually done this in production code).
The fact that UIDevice.current
is how you access the device, rather than static methods, leaves open the possibility of new APIs that can handle multiple devices (it also helps with unit testing). In the earliest versions of iOS, the only UIScreen
was .main
, and it might have made sense to make it a singleton. But because Apple didn't, when mirroring was added in 4.3, it was simple to talk about the second screen (UIScreen.mirrored
). You should generally be very slow to assume that there can only be one of something.
Swift static property initilizers are lazy why I could declared it as a constant
Neuburg M. is drawing a distinction between static properties and instance properties. You are pretending to ignore that distinction. But you cannot ignore it; they are totally different things, used for different purposes.
In this code:
class Person { // let's declare a static property
static let firstNaID = "First Name"
}
... firstNaID
is already lazy. But now try to do this:
class Person { // let's declare an instance property
lazy let firstNaID : String = "First Name" // error
}
You can't; as things stand (up thru Swift 3.1), you have to say lazy var
instead — and when you do, you get a lazy instance property.
Your static let
declaration thus doesn't accomplish what lazy let
wanted to accomplish, because a static property is not an instance property.
Static properties in Swift
With this code:
class var items: [AnyObject] {
return [AnyObject]()
}
you are not creating a stored property - instead it's a computed property, and the worst part is that every time you access to it, a new instance of [AnyObject]
is created, so whatever you add to it, it's lost as soon as its reference goes out of scope.
As for the error, the static computed property returns an immutable copy of the array that you create in its body, so you cannot use any of the array method declared as mutating
- and removeAll
is one of them. The reason why it is immutable is because you have defined a getter, but not a setter.
Currently Swift classes don't support static properties, but structs do - the workaround I often use is to define an inner struct:
class SomeClass {
struct Static {
static var items = [AnyObject]()
}
}
SomeClass.Static.items.append("test")
If you want to get rid of the Static
struct every time you refer to the items
property, just define a wrapper computed property:
class var items: [AnyObject] {
get { return Static.items }
set { Static.items = newValue }
}
so that the property can be accessed more simply as:
SomeClass.items.append("test")
Related Topics
Sharing Code Between Original iOS App and App Extension
Uistackview Distribution Fill Equally
Creating Delegates on the Spot with Blocks
iOS How to Detect When App Was Removed from Process
Xcode Myprojectname-Bridging-Header.H Does Not Exist
Xcode 10 (iOS 12) Does Not Contain Libstdc++6.0.9
How to Compile for Arm Rather Than Thumb in Xcode 4
Custom Rounding Corners on Uiview
Storing Asynchronous Cloud Firestore Query Results in Swift
Uitableviewcell with Autolayout Left Margin Different on iPhone and iPad
Auto Layout in Uicollectionviewcell Not Working
Secure Keys in iOS App Scenario, Is It Safe
How to Determine Height of Uicollectionview with Flowlayout
Access Container View Controller from Parent iOS
In iOS 12, When Does the Uicollectionview Layout Cells, Use Autolayout in Nib