Initialisation - RaduG/swift_learning GitHub Wiki
General rules
- Initialisers can overload on types, argument labels etc.
- Any
class
orstruct
that has default values for all its properties automatically gets an initialiser with no arguments. - Any
struct
that does not explicitly implement any initialisers automatically gets a member-wise initialiser. If only some of the properties have default values, only those without may be passed to this constructor if the default values are fine for the others.
Designated initialisers
Designated initialisers are initialisers that must not leave any properties unset. They must call one of their immediate superclass's initialisers after setting all properties they themselves declare/override are set.
class AutomaticCar: Car {
let n_gears: Int
override var currentSpeed: Double {
didSet {
if Int(currentSpeed) == 0 {
self.gear = 0
} else {
self.gear = min(Int(currentSpeed / 10) + 1, n_gears)
}
}
}
init(topSpeed: Double, gears: Int) {
self.n_gears = gears
super.init(topSpeed: topSpeed)
}
}
Convenience initialisers
Convenience initialisers must call another initialiser in the same class and must ultimately call a designated initialiser. They are marked by the convenience
keyword.
class AutomaticCar: Car {
let n_gears: Int
override var currentSpeed: Double {
didSet {
if Int(currentSpeed) == 0 {
self.gear = 0
} else {
self.gear = min(Int(currentSpeed / 10) + 1, n_gears)
}
}
}
init(topSpeed: Double, gears: Int) {
self.n_gears = gears
super.init(topSpeed: topSpeed)
}
convenience init(nationalSpeedLimit: Double, gears: Int) {
self.init(topSpeed: nationalSpeedLimit + 10.0, gears: gears)
}
}
Initialiser inheritance
Subclasses do not inherit initialisers by default. However, there are circumstances when superclass initialisers are automatically inherited:
- If a subclass does not define any initialisers, then it will automatically inherit all the designated initialisers of its superclass
- If a subclass overrides all initialisers of its superclass, then it will automatically inherit all the convenience initialisers of its superclass
Failable initialisers
Failable initialisers can be used to sometimes fail initialisation. A failable initialiser cannot have the same signature as a normal initialiser. Use init?
to declare a failable initialiser and return nil
to fail initialisation.
class AutomaticCar: Car {
let n_gears: Int
override var currentSpeed: Double {
didSet {
if Int(currentSpeed) == 0 {
self.gear = 0
} else {
self.gear = min(Int(currentSpeed / 10) + 1, n_gears)
}
}
}
init?(topSpeed: Double, gears: Int) {
guard gears > 0 else {
return nil
}
self.n_gears = gears
super.init(topSpeed: topSpeed)
}
convenience init?(nationalSpeedLimit: Double, gears: Int) {
self.init(topSpeed: nationalSpeedLimit + 10.0, gears: gears)
}
}
You can override a failable initialiser with a nonfailable initialiser but not the other way around.
init!
is an alternative to init?
which implicitly unwraps the instance:
class AutomaticCar: Car {
let n_gears: Int
override var currentSpeed: Double {
didSet {
if Int(currentSpeed) == 0 {
self.gear = 0
} else {
self.gear = min(Int(currentSpeed / 10) + 1, n_gears)
}
}
}
init!(topSpeed: Double, gears: Int) {
guard gears > 0 else {
return nil
}
self.n_gears = gears
super.init(topSpeed: topSpeed)
}
convenience init(nationalSpeedLimit: Double, gears: Int) {
self.init(topSpeed: nationalSpeedLimit + 10.0, gears: gears)
}
}
// this will raise a runtime error
let car = AutomaticCar(topSpeed: 100, gears: 0)
Required initialisers
Required initialisers are used to require all subclasses to implement the initialiser with that signature (unless automatically inherited!). Subclasses must also declare those as required. The override keyword is not necessary when implementing required initialisers. Use the required
keyword.
Dynamic default value
The swift pattern for providing computed default values for properties (e.g. random number) is to define their value as a closure and call it.
class Person {
var age: Int = {
Int.random(in: 1...100)
}()
init() {}
init(age: Int) {
self.age = age
}
}
let p1 = Person()
print(p1.age)
let p2 = Person()
print(p2.age)