Let's think again the reason of why Swift init method is designed like this before we continue to dig deeper into it.
In fact, the core considering is for safety. In Objective-C, init
method is not safe at all: there is no guarantee that neither the init
method will be called only once when the object getting initiated, nor the variables in the instance would be ready to use after that method. It may even cause other issues if you are using the property in the init method. Although Apple suggests not using property in init method, it is not a compelling rule, so some developers always do such thing.
Turn to Swift, there is a very strict init method. The designated init method is strengthened. An init method without any modifiers is a designated init method, in which all non-optional properties should be assigned. And the super
version of init method is also a must (either explicitly or implicitly) in the child class. No matter how an instance gets initiated, all needed properties will be assigned, to ensure the instance is fully initiated when the init method finishes.
class ClassA {
let numA: Int
init(num: Int) {
numA = num
}
}
class ClassB: ClassA {
let numB: Int
override init(num: Int) {
numB = num + 1
super.init(num: num)
}
}
In the example above, we can use let
to assign a constant variable. This is only permitted in init
method and after an instance initiated, the constant cannot be changed anymore. This is very useful when creating a multithread program. Constant variables will be always safe to use once assigned in init method, without any concern about annoying multithread issues.
There is a different type to init
method. We can add convenience
keyword before init
to make it a convenience init method. It is a "second-class citizen" in Swift world, since all convenience
must call the designated init method in the same type to finish initiating. Moreover, convenience
cannot be neither overridden in subclass nor called from subclass by super
.
class ClassA {
let numA: Int
init(num: Int) {
numA = num
}
convenience init(bigNum: Bool) {
self.init(num: bigNum ? 10000 : 1)
}
}
class ClassB: ClassA {
let numB: Int
override init(num: Int) {
numB = num + 1
super.init(num: num)
}
}
In a child class, once we implement a needed init
method for convenience
init method of the parent class, we will be able to use convenience
init of the parent class. In the code above, we override the init(num: Int)
. Because of that, we can use convenience init(bigNum: Bool)
to initiate the subclass even there is no init method which accepts a bigNum
in ClassB
:
let anObj = ClassB(bigNum: true)
// anObj.numA = 10000, anObj.numB = 10001
In summary, an init method should always stick to these two principles:
- The init path should fully initiate the object. All properties should be configured properly. This could be guaranteed by calling the designated init method of the current type;
- Designated init method in subclass must call a designated init method in super, which could guarantee the super class gets initiated properly.
Sometimes, we also want the subclass to implement some designated init methods of the super class. By adding required
to these methods in the super class, we can force subclass to override them. The advantage of this would be the convenience
init method which depends on the designated method that could be used even in subclass. A good example is the init(bigNum: Bool)
above. If we want this method to be used for any subclass at any time, we would mark the depended init(num: Int)
as required
. When we call init(bigNum: Bool)
in subclass, there is always a path for proper initiating.
class ClassA {
let numA: Int
required init(num: Int) {
numA = num
}
convenience init(bigNum: Bool) {
self.init(num: bigNum ? 10000 : 1)
}
}
class ClassB: ClassA {
let numB: Int
required init(num: Int) {
numB = num + 1
super.init(num: num)
}
}
Last but not least, not only for designated init method, even for convenience init method we can add required
to ask subclass to implement. It is advantageous when the subclass not using the convenience init methods in superclass.