为什么要有构造器:为类中自身和继承来的存储属性赋初值。
一、两种构造器-指定构造器和便利构造器
指定构造器:类中必备的构造器。为全部的属性赋初值。(有些子类可能不须要显示声明,由于默认从基类继承了)
便利构造器:类中的辅助构造器,通过调用指定构造器为属性赋初值。(仅在必要的时候声明)
举例
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
便利构造器通过conveniencekeyword声明,能够看到。便利构造器是通过调用指定构造器来进行构造的。这也就是一个关键的概念:横向代理。
何为代理:就是让别人帮你干活
二、构造过程中的规则
(一)构造器链就是调用构造器的顺序
规则例如以下:
1.1、指定构造器必须调用其父类的指定构造器
1.2、便利构造器必须调用同一类中的指定构造器
1.3、便利构造器必须最后以调用一个指定构造器而结束
总得来说一句话:便利构造器横向代理,指定构造器向上代理。
举个样例:
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)//这里是规则1.1
}
convenience init(conSubInput:String){
self.init(subInput:conSubInput,baseInput:"")//这里是规则1.2
}
convenience init(){
self.init(conSubInput:"")//这里是规则1.3,由于调用了另外一个便利构造器,而另外一个便利构造器以调用指定构造器结束
}
}
(二)关于构造器的继承与重载
swift中,子类不会默认继承父类的构造器。
构造器的重载遵循构造器链的规则(1.1-1.3)
构造器的继承规则例如以下:
2.1、假设子类中未定义不论什么指定构造器,将会自己主动继承全部父类的指定构造器
2.2、假设子类中提供了全部父类指定构造器,无论是通过规则2.1继承来的,还是自己定义实现的,它将继承全部父类的便利构造器。
注意:
子类能够通过部分满足规则2.2的方式,使用子类便利构造器来实现父类的指定构造器。
样例一:
class Base{
var baseVar:String
init(baseInput:String){
baseVar = baseInput
}
convenience init(){
self.init(baseInput:"basevar")
}
}
class Sub:Base{
var subVar:String = "subvar";
}
这里子类未定义不论什么构造器,所以满足规则2.1,2.1,将继承全部父类的指定构造器和便利构造器
所以能够这么调用
var instance1 = Sub()
var instance2 = Sub(baseInput:"newBaseVar")
样例二
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)
}
}
这里,子类仅仅是实现了父类的一个构造器,所以并未继承便利构造器,也没有继承另外一个指定构造器
仅仅能够这么创造实例
var instance = Sub(subInput:"subvar",baseInput:"basevar")
(三)基于上述两个规则,构造过程分为两个部分
阶段一
- 某个指定的构造器或者便利构造器被调用;
- 完毕新实例的内存分配(此时内存尚未初始化)。
- 指定构造器确保其引入的全部存储属性已经赋值(存储属性极其所属内存完毕初始化)。
- 指定构造器调用父类构造器(父类构造器属性初始化)。
- 这个调用父类的构造器沿着构造器链一直向上。直到最顶部。(确保全部的继承的基类过程都已经初始化)。
阶段二
- 从顶部一直向下。每一个构造器链中类指定的构造器都有机会进一步定制实例。构造器此时能够訪问self,改动它的属性而且调用实例方法等等。
- 终于,随意构造器的便利构造器将有机会定制实例和使用self。
可能这个规则有点抽象。举个样例就明确了
class Base{
var baseVar:String
init(baseInput:String){
baseVar = baseInput
}
}
class Sub:Base{
var subVar:String;
func subPrint(){
println("如今能够调用实例方法了")
}
init(subInput:String,baseInput:String){
subVar = subInput
super.init(baseInput:baseInput)
//这里就完毕了阶段一
self.subVar = subInput + "123"//此时能够调用self
subPrint()//此时也能够调用实例方法了
}
}
总得来说:当类的实例的内存被初始化完毕,也就是调用super.init()之后,就完毕了阶段一了。
三、编译器的安全检查
检查一
指定构造器必须在它所在类的属性先初始化完毕后才干把构造任务向上代理给父类中的构造器。简单来说。就是先初始化自己的存储属性,在调用父类的super.init向上初始化
检查二
指定构造器必须先向上调用父类构造器。在为继承来的属性赋初值。
这个非常简答,如果继承来个x,你先为x赋值为1了,而在调用父类构造器。父类构造器会为x赋另外一个初值来保证初始化过程完毕,那么你赋值的1就被覆盖了
检查三
便利构造器先调用同类中其它构造器,再为随意属性赋初值。和检查二类似。也是防止被覆盖
检查四
构造器在第一阶段完毕之前。不能饮用self,不能调用不论什么实例属性,不能调用实例方法
四、总结一下
指定构造器的过程是这种
1、为自己的属性赋初值
2、调用基类构造器(super.init)
3、然后就能够调用self,和实例方法,存储属性。定制新的值了。
然后,我们看下官方文档里给出的一个比較好的样例
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
}
}
这个构造器链的关系如图
解释
- 基类Food定义了一个指定构造函数和一个便利构造器
- 子类RecipeIngredient实现了基类Food全部的指定构造器。所以它继承了基类的便利构造器
- 子类ShoppingListItem未定义构造器,所以继承了基类RecipeIngredient的全部构造器。Swift入门系列15-继承中的构造规则(难点)
转载于:
关于继承中的构造规则是一个难点。
假设有问题,请留言问我。
我的Swift新手教程专栏
为什么要有构造器:为类中自身和继承来的存储属性赋初值。
一、两种构造器-指定构造器和便利构造器
指定构造器:类中必备的构造器。为全部的属性赋初值。(有些子类可能不须要显示声明,由于默认从基类继承了)
便利构造器:类中的辅助构造器,通过调用指定构造器为属性赋初值。(仅在必要的时候声明)
举例
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
便利构造器通过conveniencekeyword声明,能够看到。便利构造器是通过调用指定构造器来进行构造的。这也就是一个关键的概念:横向代理。
何为代理:就是让别人帮你干活
二、构造过程中的规则
(一)构造器链就是调用构造器的顺序
规则例如以下:
1.1、指定构造器必须调用其父类的指定构造器
1.2、便利构造器必须调用同一类中的指定构造器
1.3、便利构造器必须最后以调用一个指定构造器而结束
总得来说一句话:便利构造器横向代理,指定构造器向上代理。
举个样例:
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)//这里是规则1.1
}
convenience init(conSubInput:String){
self.init(subInput:conSubInput,baseInput:"")//这里是规则1.2
}
convenience init(){
self.init(conSubInput:"")//这里是规则1.3,由于调用了另外一个便利构造器,而另外一个便利构造器以调用指定构造器结束
}
}
(二)关于构造器的继承与重载
swift中,子类不会默认继承父类的构造器。
构造器的重载遵循构造器链的规则(1.1-1.3)
构造器的继承规则例如以下:
2.1、假设子类中未定义不论什么指定构造器,将会自己主动继承全部父类的指定构造器
2.2、假设子类中提供了全部父类指定构造器,无论是通过规则2.1继承来的,还是自己定义实现的,它将继承全部父类的便利构造器。
注意:
子类能够通过部分满足规则2.2的方式,使用子类便利构造器来实现父类的指定构造器。
样例一:
class Base{
var baseVar:String
init(baseInput:String){
baseVar = baseInput
}
convenience init(){
self.init(baseInput:"basevar")
}
}
class Sub:Base{
var subVar:String = "subvar";
}
这里子类未定义不论什么构造器,所以满足规则2.1,2.1,将继承全部父类的指定构造器和便利构造器
所以能够这么调用
var instance1 = Sub()
var instance2 = Sub(baseInput:"newBaseVar")
样例二
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)
}
}
这里,子类仅仅是实现了父类的一个构造器,所以并未继承便利构造器,也没有继承另外一个指定构造器
仅仅能够这么创造实例
var instance = Sub(subInput:"subvar",baseInput:"basevar")
(三)基于上述两个规则,构造过程分为两个部分
阶段一
- 某个指定的构造器或者便利构造器被调用;
- 完毕新实例的内存分配(此时内存尚未初始化)。
- 指定构造器确保其引入的全部存储属性已经赋值(存储属性极其所属内存完毕初始化)。
- 指定构造器调用父类构造器(父类构造器属性初始化)。
- 这个调用父类的构造器沿着构造器链一直向上。直到最顶部。(确保全部的继承的基类过程都已经初始化)。
阶段二
- 从顶部一直向下。每一个构造器链中类指定的构造器都有机会进一步定制实例。构造器此时能够訪问self,改动它的属性而且调用实例方法等等。
- 终于,随意构造器的便利构造器将有机会定制实例和使用self。
可能这个规则有点抽象。举个样例就明确了
class Base{
var baseVar:String
init(baseInput:String){
baseVar = baseInput
}
}
class Sub:Base{
var subVar:String;
func subPrint(){
println("如今能够调用实例方法了")
}
init(subInput:String,baseInput:String){
subVar = subInput
super.init(baseInput:baseInput)
//这里就完毕了阶段一
self.subVar = subInput + "123"//此时能够调用self
subPrint()//此时也能够调用实例方法了
}
}
总得来说:当类的实例的内存被初始化完毕,也就是调用super.init()之后,就完毕了阶段一了。
三、编译器的安全检查
检查一
指定构造器必须在它所在类的属性先初始化完毕后才干把构造任务向上代理给父类中的构造器。简单来说。就是先初始化自己的存储属性,在调用父类的super.init向上初始化
检查二
指定构造器必须先向上调用父类构造器。在为继承来的属性赋初值。
这个非常简答,如果继承来个x,你先为x赋值为1了,而在调用父类构造器。父类构造器会为x赋另外一个初值来保证初始化过程完毕,那么你赋值的1就被覆盖了
检查三
便利构造器先调用同类中其它构造器,再为随意属性赋初值。和检查二类似。也是防止被覆盖
检查四
构造器在第一阶段完毕之前。不能饮用self,不能调用不论什么实例属性,不能调用实例方法
四、总结一下
指定构造器的过程是这种
1、为自己的属性赋初值
2、调用基类构造器(super.init)
3、然后就能够调用self,和实例方法,存储属性。定制新的值了。
然后,我们看下官方文档里给出的一个比較好的样例
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
}
}
这个构造器链的关系如图
解释
- 基类Food定义了一个指定构造函数和一个便利构造器
- 子类RecipeIngredient实现了基类Food全部的指定构造器。所以它继承了基类的便利构造器
- 子类ShoppingListItem未定义构造器,所以继承了基类RecipeIngredient的全部构造器。Swift入门系列15-继承中的构造规则(难点)