swift初始化器有两种:
- 指定初始化器(designated initializer)
- 便捷初始化器(convenience initializer)
初始化器的一些特点:
- 每个类至少有一个初始化器
- 自己没有添加初始化器的话,编译器会自动添加
init(){ }
- 一旦自己实现了指定初始化器的话,编译器就不会自动添加
init(){ }
- 自己没有添加初始化器的话,编译器会自动添加
- 官方建议 类使用少量指定初始化器
初始化器的调用规则:
- 指定初始化器必须从他的直系父类调用初始化器(安全:保证父类的初始化完成)
- 同一个类中,多个指定初始化器之间不能相互调用
- 便捷初始化器必须从相同的类里调用另一个初始化器
- 不能调用父类的初始化器,只能横向调用
- 多个便捷初始化器,可以相互调用
- 便捷初始化器最终必须调用一个指定初始化器(这样做事为了安全,不管使用哪种方式创建,都必须走初始化代码)
- 便捷初始化器只能且必须调用自己的初始化器,不能调用父类的初始化器
下图是初始化器之间的相互调用关系:
image.png
这样保证了,使用任意初始化器,都可以完整的初始化实例
两段式初始化
第一阶段:初始化所有成员
- 初始化自己的所有成员变量
- 调用父类的初始化方法,初始化父类的成员变量,如果还有父类,一层层向上调用
调用如下图:
image.png
第二阶段
- 对属性设置新值(例如对父类的成员变量赋值)
- 只有第一阶段完成,即初始化结束,才能使用 self 访问、修改成员变量或者调用他的实例方法
这个调用是从上到下的如图:
image.png一个完整的初始化过程从上到下:
image.png
总结:苹果做的安全检查
- 指定初始化器必须在调用父类初始化之前,给自己所有成员变量赋值
- 指定初始化器必须先调用父类的初始化器,然后才能为继承的属性赋值
- 便捷初始化器必须先调用同类的其他初始化器,然后再为任意属性设值
- 初始化器在第一阶段完成之前,不能调用任何实例方法,不能读取任何属性的值,不能 引用self
override
特点
- 重写父类的指定初始化器,添加override
- 子类实现跟父类相同的便捷初始化器,不需要添加override,
- 重写的特点就是在子类中能否调用到父类的方法,而子类的便捷初始化器是调用不到父类的便捷初始化器的,严格来说,子类无法重写父类的便捷初始化器
自动继承
- 如果子类没有定义任何指定初始化器,它会自动继承父类所有的初始化器,自己不会在添加
init(){}
如图:
image.png
- 如果子类提供了父类所有的初始化器的实现(要么通过继承:上面自动继承的方式,要么重写)
- 子类自动继承多有的父类便捷初始化器
-
即使子类以便捷初始化器的形式重写指定初始化器,同样满足上面一条的规则
如图:
image.png
举个反例:
image.png
required
- 使用required修饰指定初始化器,其子类必须都实现该初始化器(继承或者重写)
- 子类重写了required初始化器,也必须添加required,但是无需加override
- 补充一点:required只对指定初始化器有效,便捷初始化器添加上也不会报错,只是没有用,原因很简单,便捷初始化器是横向调用的,没有子类继承父类这种
属性观察器
这里涉及到一个属性观察器的一个小知识点:
属性观察器在自己的初始化中是不会被调用的,但是在子类中的初始化中,是会被调用的
class Animal: NSObject {
var name: String {
willSet {
print("willSet: \(newValue)")
}
didSet {
print("didSet: \(oldValue)")
}
}
init(name: String) {
self.name = name
super.init()
}
}
class Person: Animal {
var height: Double
init(height: Double) {
self.height = height
super.init(name: "jack")
self.name = "rose"
}
}
// 没有打印
Animal(name: "swift")
/*
打印
willSet: rose
didSet: jack
*/
Person(height: 19)
可失败初始化器
init?(){ }
允许初始化器为nil;calss,结构体,枚举 都可以定义可失败初始化器
Int("xxxx")
public init?(_ description: String)
这个就是可失败初始化器
deinit
deinit
类似于OC中的dealloc,内存释放的时候调用
class Aniaml {
deinit {
print("对象销毁了")
}
}
父类的deinit可以被继承;
子类的deinit调用完成后会在调用父类的deinit
网友评论