In this second series, I want to talk about struct, which is a rather new concept for Java programmer.
Value type
First of all, remember struct is value type (in comparison with reference type as class) so that on assignment instances are COPIED. It has the following impact:
The instance will be allocated in stack (instead of heap), which means it could be fast and has trouble-free for memory management.
If the variable is declared as
let instance = ...
, then there is no way to modify the value, including its properties. The fix is to usevar instance = ...
.The above case is for the case that we modify the struct instance by changing the property. If the change is via a method, then the method needs to be marked as
mutating
(so that the compiler knows it can’t be called for a constant struct instance).Other instances, which were copied from the original instance, have no impact if the original instance get changed. The same behavior as integer, boolean and string in Java. That’s also the reason that in Swift, Int, Bool and String (and other primitives) are defined as struct.
Note that value type is copied into a cloned instance, which can be NOT obvious in case like
for
loop. This is a tricky one stackoverflow
Property observer
It’s simple to monitor property’s change with willSet
and didSet
. The former can visit the value via newValue
, while the later via oldValue
.
struct Person {
let name: String
var birthYear: Int {
willSet {
guard newValue <= Person.currentYear() else {
print("Invalid birthYear before set: \(newValue)")
return
}
}
didSet {
guard birthYear <= Person.currentYear() else {
print("Invalid birthYear after set: \(birthYear)")
birthYear = oldValue
return
}
print(magicNo * birthYear)
}
}
One thing that puzzles me is that how to prevent an invalid value in
willSet
Lazy property
Lazy property won’t need to be initialized until the first time it’s accessed. However, compiler will enforce to include it in Person’s init unless we give an explicit init like that.
lazy var magicNo: Int = {
print("calculating magicNo")
return 13
}()
init (name: String, birth: Int) { ... }
Note that the lazy block only gets executed once throughout the lifecycle of a
Person
instance.
Computerized property
In spite of this, there is no need of these two blocks for computerized properties since it can be obviously written into its get and set. In the following piece of code, birthYear
has property observers, while age
is a computerized property (from birthYear
).
var age: Int {
get {
return Person.calculateAge(year: birthYear)
}
set {
birthYear = Person.currentYear() - newValue
}
}
static func calculateAge(year: Int) -> Int {
return currentYear() - year
}
static func currentYear() -> Int {
return Calendar(identifier: Calendar.Identifier.gregorian)
.component(Calendar.Component.year, from: Date())
}
}
This wraps all about struct. In the next session, I’ll talk about class.