初始化器
类、枚举、结构体都可以定义初始化器。
类有两种初始化器: 指定初始化器(designated initializer)、便捷初始化器(convenience initializer).
class Person {
var age: Int
// 指定初始化器
init(age: Int) {
self.age = age
}
// 便捷初始化器
convenience init() {
self.init(age: 10)
}
}
知识点:
-
每个类至少有一个指定初始化器,指定初始化器是类的主要初始化器。
-
默认初始化器总是类的指定初始化器。
-
类偏向于少量的指定初始化器,一个类通常只有一个指定初始化器。
-
初始化器的相互调用规则
- 子类指定初始化器必须从它的直系父类调用指定初始化器。
- 便捷初始化器必须从相同类里调用另一个初始化器。
- 便捷初始化器最终必须调用一个指定初始化器。
class Person {
var age: Int
var name: String = ""
// 指定初始化器(主线1)
init(age: Int) {
self.age = age
}
// 指定初始化器 (主线2)
init(age: Int, name: String) {
self.age = age
self.name = name
}
// 便捷初始化器
convenience init() {
self.init(name: "long") // 2. 便捷初始化器必须从相同类里调用另一个初始化器。
}
convenience init(name: String) {
self.init(age: 10, name: name) // 3. 便捷初始化器最终必须调用一个指定初始化器。
}
}
class Student: Person {
init() {
super.init(age: 10) // 1. 子类的指定初始化器必须从它的直系父类调用指定初始化器。
}
}
let stu = Student()
初始化器的相互调用:
如图可知:
- 指定初始化器的调用是纵向的。(子类调父类)
- 便捷初始化器的调用时横向的。(同类内调用)
这套规则保证了,任意初始化器都可以完整初始化实例。
两段式初始化
Swift在编码安全方面是煞费苦心,为了保证初始化过程的安全,设定了两段式初始化
、安全检查
两段式初始化
-
第一阶段:初始化所有的存储属性。 1. 外层调用指定\便捷初始化器。 2. 分配内存给实例,但未初始化。 3. 指定初始化器确保当前类定义的存储属性都被初始化。(自己的存储属性先初始化,然后再初始化父类的) 4. 子类指定初始化器调用父类的指定初始化器,不断地往上调用,形成初始化链。
-
第二阶段:设置新的存储属性的值。 1. 从顶部初始化器往下,链中的每一个指定初始化器都有机会进一步定制实例。 2. 初始化器现在能够使用
self
(访问、修改它的属性、调用它的实例方法。等等) 3. 最终,链中任何便捷初始化器都有机会定义实例以及使用self
.
cl ass Person {
var age: Int
var name: String
// 指定初始化器
init(age: Int, name: String) {
// 第一阶段,初始化所有存储属性
self.age = age
self.name = name
// 第二阶段,个性化定制,可以使用self(访问、修改存储属性的值,调用方法)
self.run()
}
convenience init(name: String) {
// 第一阶段,初始化所有存储属性
self.init(age: 10, name: name)
// 第二阶段,个性化定制,可以使用self(访问、修改存储属性的值,调用方法)
self.run()
}
func run() {
print("Person Runn")
}
}
// Student 类
class Student: Person {
var weight: Int
init() {
// 第一阶段,初始化所有存储属性
self.weight = 20 // 先确保自己的存储属性先初始化
super.init(age: 10, name: "long") // 调用父类的指定初始化器,初始化父类的存储属性
// 第二阶段,个性化定制,可以使用self(访问、修改存储属性的值,调用方法)
self.age = 15
self.run()
}
func playing() {
print("Student playing")
}
}
安全检查
- 指定初始化器必须保证在调用父类的指定初始化器之前,其所在类的所有存储属性都要初始化完成。
- 指定初始化器必须先调用父类的指定初始化器,然后才能为继承的属性赋值。
- 便捷初始化器必须先调用同类中的其他初始化器,然后再为其任意属性赋值。
- 初始化器在完成第一阶段之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用
self
. - 直到第一阶段结束,实例才算完全合法。
官网图:
重写
- 当重写父类的指定初始化器时,必须加上
override
(即使子类的实现是便捷初始化器) - 如果子类写了一个匹配父类便捷初始化器的初始化器,不用加上
override
, 因为父类的便捷初始化器永远不会通过子类来直接调用(便捷初始化器只能横向调用),因此,严格来说,子类无法重写父类的便捷初始化器的。
class Person {
var age: Int
init(age: Int) {
self.age = age
}
convenience init() {
self.init(age: 10)
}
}
class Student: Person {
// 当重写父类的初始化器时,必须加上`override`
override init(age: Int) {
super.init(age: age)
}
// 如果子类写了一个匹配父类便捷初始化器的初始化器,不用加上`override`
convenience init() {
self.init(age: 10)
}
}
自动继承
- 如果子类没有自定义任何指定初始化器,它会自动继承父类所有的指定初始化器。
- 如果子类提供了父类所有指定初始化器的实现(要么通过继承、要么是重写),那么子类会自动继承父类所有的便捷初始化器。
- 就算子类添加了再多的便捷初始化器,这些规则依然适用。
- 子类以便捷初始化器的形式重写父类指定初始化器,也可以作为满足规则2的一部分。
required
- 用
required
修饰指定初始化器,表明其所有子类都必须实现该初始化器。(继承或者重写) - 如果子类重写了
required
初始化器,也必须加上required
,不用加override
class Person {
var age: Int
required init(age: Int) {
self.age = age
}
convenience init() {
self.init(age: 10)
}
}
class Student: Person {
required init(age: Int) { // 父类是required,子类也必须使用required修饰,保证其子类也必须实现
super.init(age: 10)
}
}
属性观察器
父类的属性在它自己的初始化器中赋值不会触发属性观察器,但在其子类的初始化器中赋值会触发属性观察器。
class Person {
var age: Int {
willSet {
print("Person willSet:", newValue)
}
didSet {
print("Person didSet:", oldValue, age)
}
}
init() {
self.age = 10 // 不会触发属性观察器
}
}
class Student: Person {
override init() {
super.init()
self.age = 20 // 会触发属性观察器
}
}
// 输出:
Person willSet: 20
Person didSet: 10 20
可失败初始化器
类、结构体、枚举 都可以使用init?
来定义可失败初始化器
class Person {
var name: String
init?(name: String) {
if name.isEmpty {
return nil
}
self.name = name
}
}
let person = Person(name: "") // 返回为nil
let person2 = Person(name: "long") // 返回 Option(Person) 可选项
注意:
- 不允许同时定义参数个数、参数标签、参数类型相同的可失败初始化器和非可失败初始化器。
- 可以用init!定义隐式解包的可失败初始化器。
- 可失败初始化器可以调用非可失败初始化器,非可失败初始化器调用可失败初始化器时需要进行解包。
- 如果初始化器调用一个可失败初始化器导致初始化失败,那么整个初始化过程都失败,并且之后的代码都停止运行。
- 可以用一个非可失败初始化器来重写一个可失败初始化器,但反过来是不行的。
class Person {
var name: String
init!(name: String) { // init! 隐式解包
if name.isEmpty {
return nil
}
self.name = name
}
convenience init() {
self.init(name: "") // 指定初始化器使用了隐式解包,所以这个不用在后面加!, 但是使用隐式解包是有风险的,如本例子,如果外部使用了便捷初始化器初始化,则会发生崩溃。
}
}
反初始化器(deinit)
deinit
叫做反初始化器,类似于C++的析构函数,OC中的dealloc
方法。
当类的实例对象被释放内存时,就会调用实例对象的deinit
方法
class Person {
deinit {
print("Person deinit")
}
}
var person: Any = Person()
person = 10 // person 重新赋值别的值后,上面的Person()对象就会被释放,会调用deinit方法。
deinit
方法不接受任何参数、不能写小括号、不能自行调用- 父类的
deinit
能被子类继承 - 子类的
deinit
实现执行完毕后会调用父类的deinit
。(和初始化过程一样,先确保子类的先完成,在调用父类的)
class Person {
deinit {
print("Person deinit")
}
}
class Student: Person {
deinit {
print("Student deinit")
}
}
var stu: Any = Student()
stu = 10
// 输出:
Student deinit
Person deinit
参考: