I was creating my own custom tableViewCell and then I got an error saying:
'required' initializer 'init(coder:)' must be provided by subclass of 'UITableViewCell'
I looked it up and obviously it's a must to implement that as well. But this led to my confusion about required vs. designated initializers
Apple Docs says:
Required Initializers:
Write the required modifier before the definition of a class initializer to indicate that every subclass of the class must implement that initializer:
Designated initializers
Designated initializers are the primary initializers for a class. A designated initializer fully initializes all properties introduced by that class and calls an appropriate superclass initializer to continue the initialization process up the superclass chain.
Are the following statements correct:
Having that said I still don't fully understand their functional differences.
A designated initializer, or designator, points out a particular element to be initialized. A designator list is a comma-separated list of one or more designators. A designator list followed by an equal sign constitutes a designation.
A designated initializer fully initializes all properties introduced by that class and calls an appropriate superclass initializer to continue the initialization process up to the superclass chain. Convenience initializers are secondary, supporting initializers for a class.
A convenience initializer is a secondary initializer that must call a designated initializer of the same class. It is useful when you want to provide default values or other custom setup. A class does not require convenience initializers.
Required initialisers and designated initialisers are not really related, though the associated keywords required
and convenience
are both used to specify restrictions on subclasses.
A required initialiser makes a guarantee that you can initialise a type, or any of its sub-types, with that initialiser. If you have an initialiser in a protocol and you conform something to that protocol, you have to use required
(if it's a class) because that protocol guarantees that the initialiser is present on that class, and any of its subclasses. When you use required
on an initialiser of a class, that signals that all of its subclasses can also be initialised using that method. This means you also need to add that initialiser to any of its subclasses.
protocol TestProtocol {
init()
}
class TestClass: TestProtocol {
required init() {
}
}
Here, the required
keyword must be present because any subclasses of TestClass
must also provide init()
(because they also conform to TestProtocol
).
Having a required initialiser allows you to initialise a class without knowing what it is at compile time, which is useful for a variety of reasons:
let classType: TestProtocol.Type = TestClass.self
let object = classType.init()
If your class conformed to multiple protocols, each with a different initialiser for example, each of those initialisers must also be required:
protocol OtherProtocol {
init(thing: Int)
}
class OtherClass: TestClass, OtherProtocol {
let thing: Int
required init() { // Required from superclass/its protocol
self.thing = 0
}
required init(thing: Int) { // Required from new protocol
self.thing = thing
}
}
Note that adding super.init()
isn't required in this special case, because Swift will automatically include the call if it takes no parameters.
In all the above examples, the initialisers are designated because they do not include the convenience
keyword.
Even if you didn't have any protocols, you can still make use of required
by initialising a type of a class which isn't known at compile time:
class BaseClass {
let value: Int
required init(value: Int) {
self.value = value
}
}
class SubClass: BaseClass {
required init(value: Int) { // Required from superclass
super.init(value: value) // Must call desginated initialiser of superclass
}
}
let someBaseClassType: BaseClass.Type = SubClass.self
let someBaseClassInstance = someBaseClassType.init(value: 1)
A designated initialiser is one which isn't a convenience initialiser (i.e, marked with convenience
). A designated initialiser must make sure that all properties of the class have a value before the initialiser finishes (or a super initialiser is called). Convenience initialisers only don't have this requirement because they must themselves call a designated initialiser.
class OtherSubClass: BaseClass {
convenience required init(value: Int) {
self.init() // Must call designated initialiser of this class
}
init() {
super.init(value: 0) // Must call designated initialiser of superclass
}
}
(This is fairly contrived example.)
In my experience, convenience initialisers are rarely useful and I tend to find the problems they solve can be solved using optional arguments on designated initialisers instead. One also needs to consider the fact that initialisers can't call convenience initialisers on their superclass, so make sure you don't have any convenience initialisers which provide functionality that your designated initialisers don't if you intend your class to be subclassed!
Structs and enums don't use the required
or convenience
keywords because these keywords are both used to indicate initialisation rules for subclasses, which only class
es support: The required
keyword indicates that subclasses must provide that initialiser, and the convenience
keyword indicates that subclasses cannot call that initialiser. Despite not having the keywords, they must still provide initialisers defined in any protocols they conform to, and you can write 'convenient' initialisers which call self.init
, just without the convenience
keyword.
To respond to your statements:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With