便捷初始化
之前的初始化器被称为指定初始化器(是必须的,没有系统也会生成个默认的),和便捷初始化器
便捷初始化器的用处
指定初始化器就不赘述了,便捷初始化器在目前我得到的资料里面确实是配合指定初始化器使用的,另外便捷初始化器并不能在结构体里面使用
class Person {
var a:Int,b:Int
init(a:Int,b:Int) {
self.a = a
self.b = b
}
convenience init(a:Int)
{
self.init(a:a,b:10)
}
}
比如这种初始化器,有两种场景一种a,b都得有值,一种只需要赋值一个,这种情况其实有两种选择,1.写多个指定初始化器,2.写一个指定初始化器和多个便捷初始化器
一般来说推荐的写法是一个指定初始化器+多个便捷初始化器,这样在继承里面结构会更加清晰(在继承里面说明),在本地也能确保调用的是同一个初始化器,一些统一的设置也可以一起写
class Person {
var a:Int,b:Int
init(a:Int,b:Int) {
self.a = a
self.b = b
}
convenience init(a:Int)
{
self.init(a:a,b:10)
}
convenience init(b:Int)
{
self.init(a:10,b:b)
}
convenience init()
{
self.init(a:10,b:10)
}
}
比如上面这种特别复杂的情况,如果需要给这个类设置个什么,写指令初始化器就得在这四个里面全部写上设置(因为指令初始化器只能调父类的指令初始化器,不能调用同类的)
指令初始化器和便捷初始化器在继承里面的使用
在继承来说,一般只能是指令初始化器调用,便捷初始化器是不可以的
所以一般在继承的时候推荐的是
同一个类,多个便捷初始化器,一个指定初始化器,除非是final类,不建议写多个指定初始化器,否则会有几十种初始化组合,直接把让绕晕,从模块设计来说是不好的,后面也不好维护,像这种模式,便捷初始化器是只能找本类的初始化器,然后指令初始化器找父类的指令初始化器,这样在继承上走的是一条线,在本地则可以多样化
初始化器之间的相互调用规则
便捷初始化器只能调用本类的便捷初始化器和指令初始化器
指令初始化器只能调用父类的指令初始化器
两段初始化
在刚才写代码的时候老遇到
self.c = c
super.init(a: a, b: b)
这种代码的先后顺序写错,会导致报错之类的问题,原因就是两段初始化
第一阶段:初始化所有存储属性
1.外层调用初始化器 -------- 就是外层调用初始化器
2.分配存储空间但未初始化 ---------- 外层调用之后就进入初始化阶段,先是malloc,这里只分配了空间也并未实际的初始化
3.指定初始化器,保证所有存储属性都能初始化 --------- 就是指定一个初始化器能初始化所有存储属性
4.调用父类初始化器,不断的往上,形成初始化器链 ---------- 假如本类的初始化器不足以初始化所有存储属性,就去调用父类的初始化器,具体表现就是self.c = c必须写在super.init()前面
所以第一阶段的目的就是保证所有的存储属性都能初始化
class Person {
var a:Int,b:Int
init(a:Int,b:Int) {
self.a = a
self.b = b
// end
}
convenience init(a:Int)
{
self.init(a:a,b:10)
}
convenience init(b:Int)
{
self.init(a:10,b:b)
}
convenience init()
{
self.init(a:10)
self.b = 10
}
}
class Teacher: Person {
var c:Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
convenience init(a:Int)
{
self.init(a: a, b: 2,c: 5)
}
}
Person(a:10)
像这个代码假如初始化一个Person 会经过便捷初始化器int(a:Int),然后到指令初始化器初始化c,然后调用父类的指令初始化器初始化a,b然后第一阶段初始化结束
第二阶段:设置新的存储属性值
感觉第一阶段过后初始化就已经完成了,可以执行在初始化器里面的其它的内容了,所以self.函数是不能写在初始化第一阶段之前的,比如
class Teacher: Person {
var c:Int
init(a: Int, b: Int, c: Int) {
self.ptr()
self.c = c
super.init(a: a, b: b)
self.ptr()
}
convenience init(a:Int)
{
self.init(a: a, b: 2,c: 5)
}
func ptr() {
print("sth")
}
}
self.ptr写在self.c = c这行前面就会报错,因为初始化第一阶段并没完成self.函数是没意义的
安全检查
1.子类存储对象必须先初始化才能调用父类,因为父类的初始化器是无法初始化子类存储属性,也就是self.c = c 这样的属性初始化必须写在super.init前面
2.即使子类要给父类存储属性赋值也得先调用父类的初始化器
比如这个self.a = a就是错误的,因为父类的第一阶段初始化还没有完成,甚至都还没调用
便捷初始化器也得先让指定初始化器初始化完成才能值操作
总而言之,在初始化器里面在一阶段初始化完成之前不能运用self.干除了初始化以外的操作,并且本地必须优先初始化
重写初始化器
参数返回值一样,内容有偏差才是重写
class Person {
var a:Int,b:Int
init(a:Int,b:Int) {
self.a = a
self.b = b
}
}
class Teacher: Person {
var c:Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
override init(a:Int,b:Int) {
self.c = 10
super.init(a: a, b: b)
}
}
感觉初始化器的重写意义不是很大,因为仍然需要给c赋值,而且这么写在继承里面使用也会变的更复杂,值得注意的是子类无法重写父类的便捷化初始化器
另外如果子类没写初始化器会自动继承父类所有指令初始化器,个人觉得除非子类没有存储属性,否则还是写个初始化器