在上一节课的末尾,我们最终明确了一个对象的创建过程,那么,说起来创建一个对象的样子很像是调用了一个方法,可这个方法到底是什么呢?
初始化器
没错的,当我们初始化了一个类为对象的时候,我们确实调用了一个方法——初始化器。
初始化器其实就是一个特殊规定了的方法,它能够为类进行初始化。
想象一下,如果没有初始化器,那么我们调用的类就一定是千篇一律的,每一次都要先创建对象,再修改它的属性?
好吧,我承认目前来讲我们都是这么做的。
总之,我们有办法让初始化对象的同时就根据不同的属性来初始化对象,这就依赖了初始化器。
默认的初始化器
我们之前的类都没有提供初始化器,但因为我们写的是一个基类,所以编译器会自动帮我们写一个,这个初始化器只是按照我们声明的方式给所有的属性赋值罢了,所以我们创建对象的时候才会使用空的括号——因为没有接收参数的初始化器!
下面我们来看看默认初始化器的样子(实际上没有样子,但我们可以写出一个和默认初始化器完全相同的初始化器来?)
1 2 3 4 5 |
class Weapon { var aaa = 0 init() {} func fire() {} } |
功能
好了,解答了一直以来我们心中的疑惑,那么这时候再来重新认识一下构造器吧:它能够让你在初始化一个对象的同时就传入一些参数用以帮助你初始化一个对象。
举个栗子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Gun { var ammo:Int var isSafe:Bool init (ammo a:Int,isSafe s:Bool ) { ammo = a isSafe = s } func fire() { print("bang!") } } var gun = Gun(ammo: 53, isSafe: true) |
看,这下我们可以直接声明不同状态的对象了!
……等等!这样虽好,可是如果我不知道弹药数量呢?不能有个默认值吗?比如一般手枪弹药数量都是8发这种?
总要有默认的东西
当然,我们也可以在类里边写上多个初始化器,这样初始化的方法就有多种了——这是被允许的。
当然,如果你不想这么做,也可以用便捷初始化器来重复使用同一个初始化器,我们看看它是怎么运作的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Gun { var ammo:Int var isSafe:Bool init (ammo a:Int,isSafe s:Bool ) { ammo = a isSafe = s } convenience init (isSafe s:Bool) { self.init(ammo: 8,isSafe: s) } func fire() { print("bang!") } } var gun = Gun(isSafe: true) |
相对于初始化器,便捷初始化器要加 convenience 前缀,而且最终它要调用初始化器,我就不多说了,看代码也能明白它是怎么运作的了。
重载初始化器
说完了便捷初始化器,那么我们再回过头看看如果要重载多个初始化器,有没有什么要求:
重载初始化器不能是相同的,即标签、参数、顺序必须有一样是不同的,这样才能通过编译。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class Gun { var ammo:Int var isSafe:Bool init (ammo a:Int,isSafe s:Bool ) { ammo = a isSafe = s } init (ammo1 a:Int,isSafe2 s:Bool) { ammo = a isSafe = s } convenience init (isSafe s:Bool) { self.init(ammo: 8,isSafe: s) } func fire() { print("bang!") } } |
这样的两个初始化器虽然相同但是标签不同,这是可以的。
初始化器与继承
子类的对象不可能在其父类没有初始化的情况下给创造出来——这是个悖论。所以说,如果你的类是一个子类,那么写初始化器就要先把父类的初始化器调用一遍——要是有参数还得给传参数以便人家能够初始化。
如果你的父类也是继承自其他类,那他的初始化器肯定也是这样的,所以最终初始化器会调用到一开始的 AnyObject 上,这样,从继承树最顶端开始一级一级往下初始化,最终完成你的这个子类的初始化工作。
这个过程就是大家口中经常提到的“构造函数链”——好吧,在 Swift 当中,我们把它叫做“初始化器”,所以这个专业术语就是“初始化链”这样。
调用父类的初始化器
我们使用 super.init() 的方式来调用父类的初始化器,如果初始化器类型相同,你也得把它给重写掉——使用 override 前缀。
所以,如果你的类是从父类继承而来,那写初始化器的时候记得先调用父类的初始化器。
如果父类初始化器有参数呢?
那我们就需要写一个接收这些参数的初始化器,然后在初始化器里边将参数传给父类的初始化器:
1 2 3 4 5 6 7 |
class SmallGun: Gun { override init (ammo1 ammo:Int,isSafe2 s:Bool) { let a = ammo + 8 super.init(ammo1: a, isSafe2: s) } } |
转载请保留出处和原文链接:https://www.logcg.com/archives/1134.html
Comments