存储属性初始化
为存储型属性分配默认值或者在构造器中设置初始值时,它们的值是被直接设置的,不会触发任何属性观察者。
形参命名和实参标签
构造器并不像函数和方法那样在括号前有一个可辨别的方法名。因此在调用构造器时,主要通过构造器中形参命名和类型来确定应该被调用的构造器。正因如此,如果你在定义构造器时没有提供实参标签,Swift 会为构造器的每个形参自动生成一个实参标签。
struct Person {
var name:String
var age:Int
// 没有实参标签,有两个形参name、age
init(_ name:String,_ age:Int){
self.name = name
self.age = age
}
}
常量属性
对于类的实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。
结构体的逐一成员构造器
struct Person {
var name:String
var age:Int = 12
/*
自动生成两个逐一成员构造器
init(name:)// 因为age已经设置了默认值
init(name:age:)//如果age是常量,那么就不会自动生成这个构造器
*/
}
指定构造器和便利构造器
指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并调用合适的父类构造器让构造过程沿着父类链继续往上进行。类倾向于拥有极少的指定构造器,但每个类都必须至少拥有一个指定构造器。
便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为部分形参提供默认值。你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清晰明了。
class Person {
var name:String = ""
var age:Int = 1
init(parama:String){
// 指定构造器
}
convenience init(data:String){ // 便利构造器 convenience
self.init(parama: data)// 便利构造器内必须调用指定构造器
}
}
继承关系下的指定构造器和便利构造器
-
指定构造器必须总是向上代理
-
便利构造器必须总是横向代理
![](https://img.haomeiwen.com/i3478571/b5321b75dc36c63b.png)
![](https://img.haomeiwen.com/i3478571/6eca6b4f3bacc2ed.png)
两段式构造过程
Swift 中类的构造过程包含两个阶段。第一个阶段,类中的每个存储型属性赋一个初始值。当每个存储型属性的初始值被赋值后,第二阶段开始,它给每个类一次机会,在新实例准备使用之前进一步自定义它们的存储型属性。
两段式构造过程的使用让构造过程更安全,同时在整个类层级结构中给予了每个类完全的灵活性。两段式构造过程可以防止属性值在初始化之前被访问,也可以防止属性被另外一个构造器意外地赋予不同的值。
阶段 1
-
类的某个指定构造器或便利构造器被调用。
-
完成类的新实例内存的分配,但此时内存还没有被初始化。
-
指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化。
-
指定构造器切换到父类的构造器,对其存储属性完成相同的任务。
-
这个过程沿着类的继承链一直往上执行,直到到达继承链的最顶部。
-
当到达了继承链最顶部,而且继承链的最后一个类已确保所有的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段 1 完成。
阶段 2
-
从继承链顶部往下,继承链中每个类的指定构造器都有机会进一步自定义实例。构造器此时可以访问
self
、修改它的属性并调用实例方法等等。 -
最终,继承链中任意的便利构造器有机会自定义实例和使用
self
。
总结:
构造器在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引用 self
作为一个值。
指定构造器:优先保证当前类内声明的属性被赋值(第一阶段) -> 调用父类的指定构造器(第一阶段)-> 给父类的属性赋新值(第二阶段)
便利构造器:调用其他构造器(第一阶段) -> 赋新值(第二阶段)
构造器的继承和重写
Swift 中的子类默认情况下不会继承父类的构造器
如果子类的构造器没有在阶段 2 过程中做自定义操作,并且父类有一个无参数的指定构造器,你可以在所有子类的存储属性赋值之后省略
super.init()
的调用。重写的不能是父类的便利构造方法
class Food {
var name: String
init(name: String) { // 指定构造器
self.name = name
}
convenience init() { // 便利构造器 convenience
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)
}
}
let oneMysteryItem = RecipeIngredient()// 调用继承自父类的无参便利构造器 -> 子类便利构造器(重写的父类指定构造器) -> 子类指定构造器 -> 父类指定构造器
可失败的构造器
class Person {
var name:String
init(){
print("3")
self.name = "[unName]"
}
init?(nameCannotBeEmpty name:String){
print("2")
if name.isEmpty {
return nil
}
self.name = name
}
}
class Student:Person {
var id : Int = 0
init?(id:Int,name:String){
print("1")
if id <= 0 {
return nil
}
self.id = id
// super.init(nameCannotBeEmpty: name) 可以调用父类指定构造器,也可以调用父类的可失败构造器
super.init()
}
}
var s = Student.init(id: 10086, name: "Li")
print(s?.name,s?.id)
必要构造器
在类的构造器前添加 required
修饰符表明所有该类的子类都必须实现该构造器
通过闭包或者全局函数给存储属性设置默认值
如果你使用闭包来初始化属性,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能在闭包里访问其它属性,即使这些属性有默认值。同样,你也不能使用隐式的
self
属性,或者调用任何实例方法。
网友评论