Static Properties in Swift

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 variable
  • let : used to create a constant
  • static : used to create type properties with either let or var. 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 constant
  • static var cnt = 0 : type property that is a variable
  • let 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 varfor 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 structs and classes. Swift enums 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

enums 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



Leave a reply



Submit