Why use required Initializers in Swift classes?
It's actually just a way of satisfying the compiler to assure it that if this class were to have any subclasses, they would inherit or implement this same initializer. There is doubt on this point, because of the rule that if a subclass has a designated initializer of its own, no initializers from the superclass are inherited. Thus it is possible for a superclass to have an initializer and the subclass not to have it. required
overcomes that possibility.
One situation where the compiler needs to be satisfied in this way involves protocols, and works like this:
protocol Flier {
init()
}
class Bird: Flier {
init() {} // compile error
}
The problem is that if Bird had a subclass, that subclass would have to implement or inherit init
, and you have not guaranteed that. Marking Bird's init
as required
does guarantee it.
Alternatively, you could mark Bird as final
, thus guaranteeing the converse, namely that it will never have a subclass.
Another situation is where you have a factory method that can make a class or its subclass by calling the same initializer:
class Dog {
var name: String
init(name: String) {
self.name = name
}
}
class NoisyDog: Dog {
}
func dogMakerAndNamer(whattype: Dog.Type) -> Dog {
let d = whattype.init(name: "Fido") // compile error
return d
}
dogMakerAndNamer
is calling the init(name:)
initializer on Dog or a Dog subclass. But how can the compiler be sure that a subclass will have an init(name:)
initializer? The required
designation calms the compiler's fears.
What's the difference between a required initializer and a designated initializer?
Required initialisers and designated initialisers are not really related, though the associated keywords required
and convenience
are both used to specify restrictions on subclasses.
Required Initialisers
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)
Designated Initialisers
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:
- Required initialisers don't have to be designated.
- Designated initialisers don't have to be required.
- Classes can have multiple required and designated initialisers.
What are convenience required initializers in Swift?
A convenience required initializer is an initializer that is enforced onto all subclasses but is not the designated initializer. This means that said initializer will eventually call a designated initializer in its initialization chain.
Designated Initialisers
A designated initialiser is the canonical initializer for a class and the one which all required and convenience initialisers should call. The Docs say:
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.
Convenience Initialisers
A convenience initialiser is an initializer that sets up certain configuration information on a class...conveniently. Documentation:
Convenience initializers are secondary, supporting initializers for a
class. You can define a convenience initializer to call a designated
initializer from the same class as the convenience initializer with
some of the designated initializer’s parameters set to default values.
You can also define a convenience initializer to create an instance of
that class for a specific use case or input value type.You do not have to provide convenience initializers if your class does
not require them. Create convenience initializers whenever a shortcut
to a common initialization pattern will save time or make
initialization of the class clearer in intent
Required Initialisers
Required initializers can be thought of as a binding contract between a parents interface and subsequent subclasses. Its your means of enforcing that all your children are aware of and implement a certain set of initialisers.
Write the required modifier before the definition of a class
initializer to indicate that every subclass of the class must
implement that initializer:
difference between override init and required init? - swift
override init(frame: CGRect)
is used when you create the view (in this case the button) programmatically.
required init?(coder: NSCoder)
is used when the view is created from storyboard/xib.
since the latter is required you must implement its body. However if you are not going to create the button manually the first one is not required and can be omitted
setup is called in both because however you choose to create the button you want to setup its custom behavior so it will work as you intended it to
Swift - A little confusion between Required Initializer vs. Delegating initializer
A required
initializer means that every subclass must also implement the same initializer. Since you made the init(_:)
initializer required in the Dog
class, every subclass (and their subclasses) of Dog
must also provide the same required init(_:)
initializer.
From the looks of things, you don't need to make the init(_:)
initializer in the Dog
class required. Remove that keyword and then you can remove the init(_:)
initializer from the NewDog
class.
Bonus - both init
methods in your NewDog
class are designated initializers since neither is a convenience initializer.
You need to learn the differences and uses of default, convenience, and required initializers. This is all clearly covered in the Intializers section of The Swift Programming Language book.
Swift Required initializers in EVReflection
It's indeed caused by the extra init
init(blah: String) {
}
That one could also be changed to:
convenience init(blah: String) {
self.init()
}
Then it won't complain about adding the required initializer.
With the convenience you specify that you will call a required initializer from there. Without that the compiler can't be sure that you do.
See also https://docs.swift.org/swift-book/LanguageGuide/Initialization.html
What is the difference between convenience init vs init in swift, explicit examples better
Standard init
:
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.
convenience init
:
Convenience initializers are secondary, supporting initializers for a
class. You can define a convenience initializer to call a designated
initializer from the same class as the convenience initializer with some
of the designated initializer’s parameters set to default values. You can
also define a convenience initializer to create an instance of that class
for a specific use case or input value type.
per the Swift Documentation
In a nutshell, this means that you can use a convenience initializer to make calling a designated initializer faster and more "convenient". So convenience initializers require the use of self.init
instead of the super.init
you might see in an override of a designated initializer.
pseudocode example:
init(param1, param2, param3, ... , paramN) {
// code
}
// can call this initializer and only enter one parameter,
// set the rest as defaults
convenience init(myParamN) {
self.init(defaultParam1, defaultParam2, defaultParam3, ... , myParamN)
}
I use these a lot when creating custom views and such that have long initializers with mainly defaults. The docs do a better job explaining than I can, check them out!
Preferred way to initialize a class in Swift
“If a property always takes the same initial value, provide a default value rather than setting a value within an initializer. The end result is the same, but the default value ties the property’s initialization more closely to its declaration. It makes for shorter, clearer initializers and enables you to infer the type of the property from its default value. The default value also makes it easier for you to take advantage of default initializers and initializer inheritance, as described later in this chapter.”
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l
Why must constructing an object of class type 'someClass' with a metatype value use a 'required' initializer?
Consider the case where we also have a subclass:
class SomeClass {
}
class SomeSubclass : SomeClass {
}
If you store the class type in a variable:
var anotherClass = SomeClass.self
The variable anotherClass
is of type SomeClass.Type
.
You can later assign this variable to a subclass:
anotherClass = SomeSubclass.self
This is valid because SomeSubclass.Type
is a SomeClass.Type
. At this point, anotherClass()
would fail if the initializer is not implemented in the subclass. This is what the compiler is protecting against.
In your sample code, this is impossible: you used let
instead of var
so changing the type is impossible. It may be that the safety checks just aren't nuanced enough to notice this.
Related Topics
Whats the Swift Animate Withduration Syntax
Select() from Linq in Swift 3.0
What Are the Differences Between Throws and Rethrows in Swift
In Swift, How to Convert a String to an Enum
Hide/Show Tab Bar When Push/Back. Swift
Perform Assignment Only If Right Side Is Not Nil
How to Animate a Model's Rotation in Realitykit
Simple Swift Fibonacci Program Crashing (Project Euler 2)
Swift: Extending Functionality of Print() Function
Can You Override Between Extensions in Swift or Not? (Compiler Seems Confused!)
Swiftui - Passing Data from Swiftuiview to Scenekit
Why Use Required Initializers in Swift Classes
How to Find the Number of Days in Given Month and Year Using Swift
Use Queue and Semaphore for Concurrency and Property Wrapper
Handling Multiple Gesturerecognizers