一、初始化器
- 指定初始化器
init(weidth:Int,height:Int) {
}
1、指定初始化器是主要的初始化器,每个类至少有一个指定初始化器。
2、默认初始化器总是类的指定初始化器。
3、类偏向于少量指定初始化器。
- 便捷初始化器
convenience init(width:Int) {
}
便捷初始化器是次要的,为一个类支持初始化器。
二、初始化器的相互调用
规则1
1、便捷初始化器最终必须调用一个指定初始化器。
class Person {
init(age:Int) {
print(“运行”)
}
convenience init(){
self.init(age:20)
}
}
class Person {
init(age:Int) {
}
convenience init(){
self.init(score:40)
}
convenience init(score:Int){
self.init(age:20)
}
}
- 一个类中便捷初始化可以是一个或者多个,一个便捷初始化器可以直接调用另一个便捷初始化器,但其中一个最终必须调用一个指定初始化器。
规则2
2、指定初始化器必须从它的直系父类调用指定初始化器。
class Person {
init(age:Int) {
}
}
class student :Person{
init(height:Int) {
super.init(age: 20)
}
}
- 子类的指定初始化器必须调用父类中的一个指定初始化器。
- 相同类里指定初始化器是不能去调用另一个指定初始化器。
规则3
3、便捷初始化器必须从相同的类里调用另一个初始化器。
student类和Person类是不同类,这里子类便捷初始化器直接调用父类的指定初始化器是不允许的
class Person {
init(age:Int) {
}
}
class student :Person{
init(height:Int) {
super.init(age: 20)
}
convenience init(weight:Int){
self.init(height:60)
}
}
- 便捷初始化器可以调用另一个初始化器,这一个初始化器可以是指定初始化器,也可以是便捷初始化器,但这个初始化器必须和便捷初始化器在同一个类中。
- 指定初始化器必须总是向上委托。
- 便捷初始化器必须总是横向委托。
三、初始化安全
Swift编译器执行四种有效的安全检查来确保两段式初始化过程能够顺利完成:
安全检查:
- 安全检查1:指定初始化器必须保证在向上委托给父类初始化器之前,其所在类引入的所有属性都要初始化完成。
- 安全检查2:指定初始化器必须先向上委托父类初始化器,然后才能为继承的属性设置新值。如果不这样做,指定初始化器赋予的新值将被父类中的初始化器所覆盖。
- 安全检查3:便捷初始化器必须先委托同类中的其它初始化器,然后再为任意属性赋新值(包括同类里定义的属性)。如果没这么做,便捷构初始化器赋予的新值将被自己类中其它指定初始化器所覆盖。
- 安全检查4: 初始化器在第一阶段初始化完成之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用 self 作为值。直到第一阶段结束类实例才完全合法。属性只能被读取,方法也只能被调用,直到第一阶段结束的时候,这个类实例才被看做是合法的。
以下是两段初始化过程,基于上述四种检查的流程:
两段式初始化:
第1阶段:初始化所有存储属性
- 1、外部调用指定或便捷初始化器
- 2、分配内存给实例,但未初始化
- 3、指定初始化器确保当前类定义的存储属性都初始化。
- 4、指定初始化器调用父类的初始化器,不断向上调用,形成初始化器链。
class Person {
var age:Int
init(age:Int) {
self.age = age
}
}
class student :Person{
var sext:String
var weight:Int
init(height:Int) {
//3.确保当前类定义的存储属性都初始化
self.sext = “男”
self.weight = 80
//4.指定初始化器调用父类的初始化器,不断向上调用,形成初始化器链。
super.init(age: 20)
}
}
//1.外部调用指定初始化器
let s = student(height: 20)
//2.分配内存给实例
第2阶段:设置新的存储属性值
- 从顶部初始化器往下,链中的每一个指定初始化器都有机会进一步定制实例。初始化器现在能够访问 self 并且可以修改它的属性,调用它的实例方法等等;
- 最终,链中任何便捷初始化器都有机会定制实例以及使用 slef
lass Person {
var age:Int
init(age:Int) {
self.age = age
//1、顶部初始化器开始,调用它的实例方法
self.test()
}
func test() {
print(“test”)
}
}
class student :Person{
var sext:String
var weight:Int
init(height:Int) {
self.sext = “男”
self.weight = 80
super.init(age: 20)
//2、修改它的属性
self.age = 23
}
convenience init(score:Int){
self.init(height: 20)
//2、修改它的属性
self.age = 33
self.test()
}
}
let s = student(height: 20)
四、总结
- 1、每个类至少有一个指定初始化器,指定初始化器是主要的初始化器;初始化器作为主要的指定初始化器偏向于少量,而便捷初始化器才是为了便捷初始化而设计的。
- 2、同类指定初始化器不能相互调用,指定初始化器必须从它的直系父类调用指定初始化器。
- 3、便捷初始化器必须从相同的类里调用另一个初始化器.
- 4、便捷初始化器最终必须调用另一个指定初始化器。
- 5、指定初始化器首先要确保当前类定义的存储属性都初始化。
- 6、初始化器必须先委托同类中的其它初始化器,然后再为任意属性赋新值(包括同类里定义的属性)。
五、可失败初始化器
类、结构体、枚举都可以用init?定义可失败初始化器
5.1 、定义可失败初始化器
struct Person {
init?(name:String) {
if name.isEmpty {
print("初始化失败")
return nil
}
}
}
class Person {
init!(name:String) {
if name.isEmpty {
print("初始化失败")
return nil
}
}
}
let p = Person(name: "eee")
print(p!)
init!定义隐式解包的一个可失败初始化器,在上面的例子无法体现区别。在下面的例子就可以感受到。
5.2、
class Person {
convenience init!(name:String) {
if name.isEmpty {
print("初始化失败")
return nil
}
self.init()
}
init() {
}
}
可失败初始化器可以调用非可失败初始化器
class Person {
init?(name:String) {
if name.isEmpty {
print("初始化失败")
return nil
}
}
convenience init() {
self.init(name:"")!
}
}
class Person {
init!(name:String) {
if name.isEmpty {
print("初始化失败")
return nil
}
}
convenience init() {
self.init(name:"")
}
}
- 非可失败初始化器调用可失败初始化器需要解包,这里init?需要手动解包,init!会隐式解包。
六、反初始化器(deinit)
deinit叫做反初始化器,类似于c++的析构函数、OC中的delloc方法
class Person {
deinit {
print("Person对象销毁了")
}
}
var p:Person? = Person()
p = nil