前言
Swift 的初始化器的设计和 Objective-C 有很大的不同。 Objective-C默认为属性设置了初始值,而Swift则不会。需要显示地设置各个属性的值。 初始化的目的是为所有的属性提供一个初始值,确保所有的属性在使用的时候是有值的。
值类型的初始化
来看一个例子
struct Person {
var name: String?
var age: Int
}
对于值类型,如果不写任何自定义的初始化方法,系统会提供一个默认的始化器,这个默认的初始化器是使用所有属性名称作为参数名称:
init(name: String?, age: Int)
当为Person提供了自定义的初始化器之后,默认的初始化器将会消失,不可以被使用。
///这里提供了初始化方法,init(name: String?, age: Int)将不能被使用
///只能使用 init(fullName:String,age:Int) 进行初始化
init(fullName: String?,age: Int) {
self.name = fullName
self.age = age
}
某些情况对于参数是有严格限制的,参数不符合条件会导致初始化失败,会失败的初始化器在swift中使用init?关键字,并在失败时 return nil,例如:
//这是一个可以失败的初始化器
init?(fullName: String?,age: Int) {
//年龄不能小于1岁
if age < 1 { return nil }
self.name = fullName
self.age = age
}
类的初始化
类的初始化比值类型的初始化要复杂一些,因为类可以继承。但是原则是不变的,就是要确保所有的属性都要被初始化。
类初始化有两种类型:Designated Initializers (指定初始化器) 和 Convenience Initializers (便捷初始化器)
指定初始化器的作用是为类的所有属性提供初始化的方法 ,类可以有多个 指定初始化器。 便捷初始化器的作用是为类提供一个便捷的初始化的方法 , 类也可以有多个,便捷初始化器。
用文档中的例子:
class Food {
var name: String
///指定初始化器
init(name: String) {
self.name = name
}
///便捷初始化器
///这个初始化器很方便,不需要传递name参数
convenience init() {
self.init(name: "[Unnamed]")
}
}
为什么要分出两种类型的初始化器呢? 这个跟类的初始化方法继承有关后面会讲。
指定初始化器 和 便捷初始化器 有固定的交互规则,如下图:
- 1.子类的指定初始化器必须调用父类的初始化器
- 2.便捷初始化器必须调用同一个类中的另一个初始化器
- 3.便捷初始化器最终会调用指定初始化器
上面这三条规则非常重要,其指导了子类如何编写自定义的初始化程序来正确的初始化类
类的初始化器的继承
依据上面的规则,子类继承父类初始化方法时有如下规则:
- 1.如果子类没有定义任何指定的初始化器,则自动继承所有父类指定初始化器
- 2.如果子类子类提供了所有父类指定初始化器的实现(通过规则1继承或者自定义实现),则继承所有父类的便捷初始化方法
来看文档中的例子:
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
class RecipeIngredient: Food {
var quantity: Int
init(name:String , quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name,quantity:1)
}
}
这些初始化方法的交互图:

RecipeIngredient 实现了父类Food的指定初始化器 init(name: String),所以其自动继承了父类便捷初始化器init(),另外RecipeIngredient 实现了自己的指定只初始化器init(name:String, quantity: Int). 在init(name:String.quantity: Int)方法中先设置了子类特有属性quantity的值,然后调用了父类的指定初始化器init(name:).这里也是有讲究的,这关系到swift类初始化的安全检查过程。
初始化安全检查
Swift中的类初始化是一个分为两个阶段的过程。在第一阶段,每个存储的属性都由引入它的类分配一个初始值。一旦确定了每个存储属性的初始状态,便开始第二阶段,并且在认为新实例可以使用之前,每个类都有机会自定义其存储属性。 这两个阶段可以防止类在初始化过程中意外的将属性设置为其它值。 Swift执行的安全检查有四个:
- 安全检查1: 子类的特有属性必须在调用父类的指定初始化方法之前设置初始值。
- 安全检查2: 只有在调用了父类的指定初始化方法之后,子类才可以给父类的属性设置初始值。(否则父类属性的初始值将会被覆盖)
- 安全检查3: 便捷初始化程序在调用另个初始化程序之后设置属性的值。(否则将会被另个初始化程序重载)
- 安全检查4: 在self的第一个阶段完成后,初始化程序才能调用实例方法,读取属性的值。
初始化两个阶段的详细过程:
阶段1
-
在类上调用了指定的或便捷初始化方法
-
分配该类的新实例内存,但是内存还没有被初始化
-
该类的指定初始化方法确认该类引入的所有存储属性都具有值。这些存储属性的内存现在已经被初始化。
-
调用父类的指定初始化器,父类重复上面的过程,一直到达继承链的顶部
-
一旦到达链的顶部,则实例的内存被视为完全初始化,阶段1已经完成
阶段2
-
从链的顶部向下回溯,链中每个指定初始化程序都可以选择并且进一步的自定义实例。初始化程序现在可以访问self并且可以修改其属性,调用其实例方法。
-
最后,链中的所有便捷初始化方法都可以自定义实例并使用self
必须的初始化器
必须的初始化器实际上已一种特殊的指定初始化器,要求子类必须实现(而不是使用默认的继承)。实现形式是在方法前加上required关键字。
required init() {
// initializer implementation goes here
}
总结:
1.swift初始器的目的是为所有属性设置正确的初始值。
2.类的初始化器有指定和便捷之分。有固定的调用规则。
3.类的初始化有两个阶段,在每个阶段中要进行安全检查。 第一个阶段沿着继承链向上调用指定初始化方法一直到顶部,第二个阶段是回溯到底部,实现子类的自定义属性,以及访问self和调用实例方法。