Swift基础9(初始化)
初始化
初始化是为类、结构体或者枚举准备实例
的过程。这个过需要给实例里的每一个存储属性设置一个初始值并且在新实例可以使用之前执行任何其他所必须的配置或初始化
。
你通过定义初始化器来实现这个初始化过程,它更像是一个用来创建特定类型新实例的特殊的方法。不同于 Objective-C 的初始化器,Swift 初始化器不返回值。这些初始化器主要的角色就是确保在第一次使用之前某类型的新实例能够正确初始化。
类有两种初始化器
- 1、指定初始化器(designated initializer)
- 2、便捷初始化器(convenience initializer)
class Person {
var age: Int
var name: String
//指定初始化器
init(age:Int, name:String) {
self.age = age
self.name = name
}
//便捷初始化器
convenience init(age:Int){
self.init(age:age,name:"")
}
}
- 1、每一个类至少有一个指定初始化器,指定初始化器是类的最主要初始化器
- 2、默认初始化器总是类的指定初始化器
- 3、类偏向于少量指定初始化器,一个类通常就只有一个指定初始化器
初始化器的相互调用规则
- 1、指定初始化器必须从他的直系父类调用指定初始化器
- 2、便捷初始化器必须从相同的类里调用另一个初始化器
- 3、便捷初始化器最终必须调用一个指定初始化器
使用这一套规则保证了使用任意初始化器都可以完成的初始化实例
指定初始化器必须从他的直系父类调用指定初始化器
class Person {
var age = 0
init(age:Int) {
self.age = age
}
}
class Student: Person {
var score = 0
init(age:Int,score:Int) {
self.score = score
super.init(age: age)
}
}
便捷初始化器必须从相同的类里调用另一个初始化器
class Size {
var height = 0
var width = 0
init(height:Int,width:Int) {
self.height = height
self.width = width
}
convenience init(width:Int){
self.width = width
}
}
上面的错误日志就是,在convenience
需要先初始化
convenience init(width:Int){
self.init(height:0, width:width)
self.width = width
}
其实这种是一种比较安全的设计,我们在指定初始化器中初始化好所有必须要初始化的值,在便捷初始化中,调用指定初始化器,这样就不会出现一些忘记初始化的值
两段式初始化
swift在编码安全方面,为了保证安全,设定了两段式
、安全检查
两段式初始化第一阶段
第一阶段初始化所有的存储属性
- 1、外层调用指定\便捷初始化器
- 2、分配内存给实例,但并未初始化
- 3、指定初始化器确保当前类定义的存储属性都初始化
- 4、指定初始化器调用父类的初始化器,不断向上,形成初始化器链条
class Person {
var age:Int
init(age:Int) {
self.age = age
}
}
class Student: Person {
var score:Int!
init(age:Int,score:Int) {
self.score = score //3、指定初始化器确保当前类定义的存储属性都初始化
super.init(age: age)
}
}
//1、外层调用指定\便捷初始化器
var stu = Student(age: 10, score: 99)
1、外层调用指定\便捷初始化器
这句话就是var stu = Student(age: 10, score: 99)
这句代码
2、分配内存给实例,但并未初始化
这个时候我们需要查看汇编了
在var stu = Student(age: 10, score: 99)
打断点查看汇编,汇编代码如下
0x100000bfe <+46>: callq 0x1000011f0 ;
初始化.Student.__allocating_init(age: Swift.Int, score: Swift.Int) ->
初始化.Student at main.swift:35
3、指定初始化器确保当前类定义的存储属性都初始化
对于Student
就是这句赋值代码self.score = score
4、指定初始化器调用父类的初始化器,不断向上,形成初始化器链
子类Student
调用父类Person
的初始化器init(age:Int) {}
两段式初始化第二阶段
第二阶段设置新的存储属性
- 1、从顶部初始化器往下,链中的每一个指定初始化器都有机会进一步定制实例
- 2、初始化器现在能够使用
self
(访问、修改他的属性、调用他的实例方法等) - 3、最终、链中的任何便捷初始化器都有机会定制实例,以及使用
self
class Student: Person {
var score:Int
init(age:Int,score:Int) {
self.test()
self.score = score
super.init(age: age)
}
func test() {
}
}
他会在self.test()
处报错'self' used in method call 'test' before 'super.init' call
是因为两段式初始化第一阶段的3、指定初始化器确保当前类定义的存储属性都初始化
还没有完成,当把self.test()
放到super.init(age: age)
就可以了。
安全检查
-
1、指定初始化器必须保证在向上委托给父类初始化器之前,其所在类引入的所有属性都要初始化完成。
-
2、指定初始化器必须先向上委托父类初始化器,然后才能为继承的属性设置新值。如果不这样做,指定初始化器赋予的新值将被父类中的初始化器所覆盖。
-
3、便捷初始化器必须先委托同类中的其它初始化器,然后再为任意属性赋新值(包括同类里定义的属性)。如果没这么做,便捷构初始化器赋予的新值将被自己类中其它指定初始化器所覆盖。
-
4、初始化器在第一阶段初始化完成之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用 self 作为值。
直到第一阶段结束类实例才完全合法。属性只能被读取,方法也只能被调用,直到第一阶段结束的时候,这个类实例才被看做是合法的。
重写
- 1、当重写父类的指定初始化器,必须加上
override
(即使子类的实现是便捷初始化器) - 2、如果子类写了一个匹配父类便捷初始化器的初始化器,不用加上
override
- 因为父类的便捷初始化器永远不会通过子类调用,因此,严格来说,子类无法重写父类的便捷初始化器
class Person {
var age:Int
init(age:Int) {
self.age = age
}
}
class Student: Person {
var score:Int
init(age:Int,score:Int) {
self.score = score
super.init(age: age)
}
override init(age: Int) {
super.init(age: age)
}
}
这时在super.init(age: age)
报错Property 'self.score' not initialized at super.init call
,是因为自己的存储属性还没有初始化结束
override init(age: Int) {
self.score = 10
super.init(age: age)
}
这样写就不报错了。
如果子类写了一个匹配父类便捷初始化器的初始化器,不用加上override
class Person {
var age:Int
var name:String
init(age:Int,name:String) {
self.age = age
self.name = name
}
convenience init(age:Int){
self.init(age:age,name:"")
self.age = age
}
}
class Student: Person {
var score:Int
init(age:Int,score:Int) {
self.score = score
super.init(age: age,name:"")
}
init(age:Int) {
self.score = 99
super.init(age: age, name: "")
}
}
var stu = Student(age: 10)
我们在Person
的便捷初始化器 convenience init(age:Int){}
和子类Student
的指定初始化器init(age:Int) {}
打断点,我们可以发现并没有走到Person
的便捷初始化器 convenience init(age:Int){}
里面
便捷初始化器只能横向调用,不能够继承,被重写
自动继承
-
1、如果子类没有自定义任何指定初始化器,它会自动继承父类所有的指定初始化器
-
2、如果子类提供了父类所有指定初始化器的实现(要么通过方式1继承,要么重写),子类自动继承了父类的所有便捷初始化器
-
3、就算子类添加了更多的便捷初始化器,这些规则仍然适用
-
4、子类以便捷初始化器的形式重写父类的指定初始化器,也可以作为满足规则2的一部分
required
- 如果用
required
修饰指定初始化器,表明其所有子类必须实现该初始化器 - 如果子类重写了
required
初始化器,子类也必须要加上required
,不用加override
class Person {
required init() {
}
}
class Student: Person {
required init() {
super.init()
}
}
属性观察器
父类的属性在他自己的初始化器中赋值不会触发属性观察器,但在子类的初始化器中赋值会触发属性观察器
class Person {
var age:Int{
willSet{
print("willSet",newValue)
}
didSet{
print("didSet","oldValue:\(oldValue)","age:\(age)")
}
}
init() {
self.age = 0
}
}
class Student: Person {
override init() {
super.init()
self.age = 1
}
}
var stu = Student()
打印
willSet 1
didSet oldValue:0 age:1
反初始化器deinit
deinit
叫做反初始化器,类似OC中的dealloc
当类中的实例对象被释放内存的时,就会调用实例对象的deinit
class Student: Person {
override init() {
super.init()
self.age = 1
}
deinit {
print("Student对象销毁")
}
}
- 1、
deinit
不接受任何参数,不能写小括号,不能自行调用 - 2、父类的
deinit
能被子类继承 - 3、子类的
deinit
实现执行完毕以后会调用父类的deinit