Swift difficulties-detailed explanation of the constructor rule instances in inheritance, swift difficulties
Constructing Rules in inheritance is a challenge. If you have any questions, please leave a message to ask me.
My Swift getting started tutorial Column
Why is there a constructor: assign an initial value to the class itself and the inherited storage attribute.
One or two constructors-specify the constructor and constructor
Specify the constructor: A required constructor in the class that assigns initial values to all attributes. (Some subclasses may not need to display declarations because they inherit from the base class by default)
Constructor: Auxiliary constructor in the class, which assigns an initial value to the attribute by calling the specified constructor. (Declared only when necessary)
Example
class Food { var name: String init(name: String) { self.name = name } convenience init() { self.init(name: "[Unnamed]") }}
The convenience keyword declares that the convenience constructor is constructed by calling the specified convenience. This is also a key concept: horizontal proxy.
What is proxy: Let others help you
Ii. Rules in the construction process
(1) The constructor chain is the order in which the constructor is called.
The rules are as follows:
1.1 The specified constructor must call the specified constructor of its parent class.
1.2 The constructor must call the specified constructor in the same class.
1.3 The convenience constructor must end with calling a specified constructor.
In a word, the constructor is used to facilitate the horizontal proxy and specify the constructor to forward the proxy.
For example:
Class Base {var baseVar: String init (baseInput: String) {baseVar = baseInput} convenience init () {self. init (baseInput: "")} class Sub: Base {var subVar: String; init (subInput: String, baseInput: String) {subVar = subInput super. init (baseInput: baseInput) // here is Rule 1.1} convenience init (conSubInput: String) {self. init (subInput: conSubInput, baseInput: "") // here is Rule 1.2} convenience init () {self. init (conSubInput: "") // here is Rule 1.3, because another constructor is called, and another constructor ends by calling the specified constructor }}
(2) Inheritance and overloading of Constructors
In swift, child classes do not inherit the constructor of the parent class by default.
The reload of the constructor follows the constructor chain rules (1.1-1.3)
The constructor inherits the following rules:
2.1 If no specified constructor is defined in the subclass, it will automatically inherit the specified constructor of all parent classes.
2.2 If the subclass provides all parent class specified constructors, whether inherited by rule 2.1 or implemented by custom, it inherits the convenience constructors of all parent classes.
Note: subclass can partially meet rule 2.2 and use the subclass constructor to implement the specified constructor of the parent class.
Example 1:
class Base{ var baseVar:String init(baseInput:String){ baseVar = baseInput } convenience init(){ self.init(baseInput:"basevar") }}class Sub:Base{ var subVar:String = "subvar";}
The subclass does not define any constructor, so rule 2.1 and 2.1 will inherit the specified constructor and constructor of all parent classes.
So you can call
var instance1 = Sub()var instance2 = Sub(baseInput:"newBaseVar")
Example 2
class Base{ var baseVar:String init(baseInput:String){ baseVar = baseInput } init(firstPart:String,secondPart:String){ baseVar = firstPart + secondPart } convenience init(){ self.init(baseInput:"basevar") }}class Sub:Base{ var subVar:String; init(subInput:String,baseInput:String){ subVar = subInput super.init(baseInput) }}
Here, the subclass only implements a constructor of the parent class, so it does not inherit the constructor or inherit another specified constructor.
This is the only way to create instances.
var instance = Sub(subInput:"subvar",baseInput:"basevar")
(3) Based on the above two rules, the construction process is divided into two parts:
Phase 1
- A specified constructor or constructor is called;
- Complete the memory allocation for the new instance (the memory has not been initialized yet );
- Specify the constructor to ensure that all the storage attributes it introduces have been assigned a value (the memory that the storage attribute belongs to has been initialized );
- Specify the constructor to call the parent Constructor (parent constructor attribute initialization );
- The constructor that calls the parent class keeps up along the constructor chain until the top. (Ensure that all the inherited base class processes have been initialized ).
Phase 2
- From the top down, the constructor specified by the class in each constructor chain has the opportunity to further customize the instance. At this time, the constructor can access self, modify its attributes, and call the instance method.
- In the end, the constructor of Any constructor will have the opportunity to customize instances and use self.
This rule may be a bit abstract. For example, you will understand it.
Class Base {var baseVar: String init (baseInput: String) {baseVar = baseInput} class Sub: Base {var subVar: String; func subPrint () {println ("You can call the instance method now")} init (subInput: String, baseInput: String) {subVar = subInput super. init (baseInput: baseInput) // This completes Phase 1 self. subVar = subInput + "123" // at this time, you can call self subPrint () // you can also call the instance method }}
In general, when the memory of the class instance is initialized, that is, after super. init () is called, Phase 1 is completed.
Iii. compiler security check
Check 1
The specified constructor must initialize its class attributes before it can forward the constructor to the constructor in the parent class. To put it simply, you need to first initialize your own storage attributes and initialize them upwards when calling the super. init of the parent class.
Check 2
Specify that the constructor must first call the parent class constructor and assign an initial value to the inherited attribute. This is a simple answer. Assume that x is inherited. you assign a value of 1 to x first, while calling the parent class constructor, the parent class constructor will assign another initial value to x to ensure that the initialization process is complete, so the value of 1 will be overwritten.
Check 3
The constructor first calls other constructor of the same type, and then assigns an initial value to any attribute. Similar to check 2, it also prevents overwriting.
Check 4
The constructor cannot consume self, call any instance attributes, or call the instance method before the first stage is complete.
Iv. Summary
The process for specifying the constructor is as follows:
1. Assign initial values to your attributes
2. Call the base class Constructor (super. init)
3. Then you can call self, instance method, and store attributes. The new value is customized.
Next, let's take a look at a good example in the official document.
class Food { var name: String init(name: String) { self.name = name } convenience init() { self.init(name: "[Unnamed]") }}class RecipeIngredient: Food { var quantity: Int init(name: String, quantity: Int) { self.quantity = quantity super.init(name: name) } override convenience init(name: String) { self.init(name: name, quantity: 1) }}class ShoppingListItem: RecipeIngredient { var purchased = false var description: String { var output = "\(quantity) x \(name.lowercaseString)" { output += purchased ? " YES" : " NO" return output }}
The relation of the constructor chain
Explanation
- A base-class Food defines a specified constructor and a convenience constructor.
- The subclass RecipeIngredient implements all the specified constructors of the base class Food, so it inherits the convenience constructor of the base class
- The sub-class ShoppingListItem does not define the constructor, so it inherits all constructors of the base class RecipeIngredient. Swift getting started Series 15-constructor rules in inheritance (difficulties)