Swift类的构造过程
Swift类的存储型属性必须在构造过程完成后有值,Swift为类提供了两种构造器——指定构造器、便利构造器
指定构造器
init(参数表){
构造过程
}
指定构造器先完成自身类属性值的初始化,再调用父类指定构造器完成父类属性值初始化,一个类可以有多个指定构造器,但至少要有一个(继承的,默认的都算)。在指定构造器中不可以调用本类其他构造器,只能向上代理
便利构造器
convenience init(参数表){
构造过程
}
便利构造器必须调用本类其他构造器完成初始化,而且最终必须导致本类一个指定构造器被调用,便利构造器不可以直接调用父类构造器,只能横向代理
总结为三条规则:
1、指定构造器必须调用其直接父类(如果有)的指定构造器(先初始化自己的属性,然后调用父类的构造器初始化父类的属性)
2、便利构造器必须调用本类中其他构造器
3、便利构造器最终需要导致一个指定构造器调用
指定构造器必须总是向上代理,便利构造器必须总是横向代理
构造过程两段式构造过程
第一阶段:
1、构造器被调用
2、分配内存,未初始化
3、指定构造器为本类存储属性赋初值,本类存储属性完成初始化
4、向上调用父类指定构造器,重复类似3,4过程,直到顶部类
5、自下而上所有存储属性完成初始化,第一阶段完成
第一阶段
第二阶段:
1、从顶部往下,每一层的指定构造器都有机会进一步定制本类实例,此时构造器可以访问self,修改属性,调用实例方法等,也就是说在未完全初始化时,实例是不可以使用的
2、最终,任意构造器链中的便利构造器都可以定制实例和使用self
第二阶段
编译器进行四种安全检查保证上述构造准确进行
- 指定构造器先初始化本类的存储属性后,才可以向上代理给父类的指定构造器
- 指定构造器必须要在调用父类的指定构造器后,才可以为继承来的属性赋新值,不然就被父类初始值覆盖
- 便利构造器必须先调用本类其他构造器,才能为属性赋新值,不然会被初始值覆盖
- 构造器在第一阶段完成前,不能调用实例方法,不能读取实例属性值,不能引用self作为一个值
默认构造器
默认构造器就是不带参数的构造器,当类中没有提供任何构造器时(继承来的也算),属性都有默认值时, 编译器会提供一个默认构造器
class ABC{
var a:Int = 1
var b:Int = 2
var c:Int = 3
}
var abc = ABC()//编译器会提供一个默认的构造器
print(abc.a,abc.b,abc.c)
class A {
init() {
}
}
没有问题,默认就是这样,我们写了和默认编译器一样的东西,也就是说我们可以自己定义指定型的默认构造器
class A {
convenience init() {
self.init()
}
}
编译报init()重定义,便利构造器不会阻止编译器自动生成默认构造器,和默认构造器重名,所以报错
class A {
var a:Int
init(a:Int) {
self.a = a
}
convenience init() {
self.init()
}
}
这段代码编译不报错,运行阶段调用init()
进入死循环,我们有init(a:int)
,所以编译器不会自动生成默认构造器,然后我们写convenience init()
,也不会报错,奇怪的是构造过程并不满足三条规则中的规则三,因为已经没有了指定的默认构造器,然而编译器不报错,运行却进入死循环,妥妥的编译器bug
子类中访问父类的属性,用self
和super
都行