Init methods are funky.
Init methods are not so common because properties can have their defaults set using "=" or using lazy instantiation.
So When Do We Need An Init Method?
We only need init when a value can't be set with an "=", lazily or it's not an optional.
We can have as many init methods in a class or struct as we want, and each can have different arguments.
Callers use your init(s) by just using the name of your type and providing the args we want.
var brain = CalculatorBrain()
var pendingBinaryOperation = PendingBinaryOperation(function: +, firstOperand: 23)
let textNumber = String(45.3)
let emptyString = String()
You get some init methods for "free". For example all base classes automatically get an init with no arguments. A base class has no superclass.
If a struct has no initializers, it will get a default one with all the properties as arguments.
struct PendingBinaryOperation {
var function: (Double, Double) -> Double
var firstOperand: Double
}
// PendingBinaryOperation will get this init automatically under the hood
init(function: (Double, Double) -> Double, firstOperand: Double) { }
// And somewhere else in our code we could use it like this
let pbo = PendingBinaryOperation(function: f, firstOperand: accumulator)
If a struct implements even one init method it no longer gets the "free" one.
What Can We Do Inside An Init?
- We can set any property's value, even those that already had default values.
- Constant properties (i.e. properties declared with let) can also be set (or reset).
- You can call other init methods in you own class or struct using
self.init(<args>)
But there are some rules for calling inits from other inits in a class because we start to run into inheritance issues. So lets break down what we are required to do inside an init for a class.
Inits On A Class
- In a class, by the time an init is done, all properties must have values (optionals can have the value nil)
- There are two types of inits in a class, Convenience and Designated.
Designated Inits
- A designated init must, and can only, call a designated init that is in its immediate superclass.
- It can't call a convenience init of a superclass.
- It can only call the designated init of the class directly above it (it's immediate parent), it can't call a designated init of a class that is 2 or more levels away from it.
- The class must initialize all of it's properties before calling a superclass's init, and you must call a superclass's init before you assign a value to any of it's properties that the class will inherit.
Convenience Inits
- A convenience init must, and can only, call an init in it's own class. We can call the designated, or another convenience init method as long as it's in that class.
- A convenience init must be called, and call any others it will execute before it can set any property values. This is a bit different than working with class inits where we have to initialize all the property values before we call the superclass's init. Here we need to call all of the inits we want to use before any properties are initialized.
- The calling of all other inits must complete before we can access properties or invoke method, or in other words the class must be completely initialized before we begin to work with or on it.
Inheriting Init
- If we do not implement any designated init, we inherit all of the superclass's designated init methods, (only if we do implement any designate inits).
- If we override all of the superclass's designated inits, we inherit all of its convenience inits. This is because when the superclass implements any of its convenience inits it depends on its designated inits being implements.
Required Init
A class can mark on or more of its init methods as required, which will force any subclass to implement it (though they can be inherited per the above rules)
Failable Init
If an init is declared with a "?" after the word init, it returns an optional.
init?(arg1: Type1, ...) {
// might fail and return nil
}
An example of this would be when we see something like Double("hey")
. This call will fail and return an optional with a value of nil
.