指定构造函数
struct Person {
init() {
// 构造函数
}
}
class Person {
init() {
// 构造函数
}
}
如果在结构体中定义了属性,不用自己写构造函数,默认会有一个逐一成员构造函数。
如果在类中定义了属性(非可选),自己就得在构造函数对属性进行初始化赋值,构造函数必须写。
便利构造器
在Swift中,多了一个便利构造器,即在init前面加上“convenience”
struct Person {
convenience init() {
// 便利构造函数
// 必须调用同类的指定构造函数(其中一个即可)
self.init()
}
}
class Person {
convenience init() {
// 便利构造函数
// 必须调用同类的指定构造函数(其中一个即可)
self.init()
}
}
便利构造器有什么用呢,顾名思义嘛,就是为了便利。
3个规则
规则1: 指定构造器必须调用其直接父类的的指定构造器。
规则2: 便利构造器必须调用同类中定义的其他构造器。
规则3: 便利构造器必须最终导致一个指定构造器被调用。
其实和容易记住
指定构造器必须总是向上代理。
便利构造器必须总是横向代理。
2个阶段
Swift的构造过程分2个阶段:
阶段1:
(1)构造器被调用
(2)新实例分配内存,但内存还没初始化
(3)指定构造器中,先对所在的类引入的属性(特有的属性)赋初始值。
(4)指定构造器调用父类的构造器,对父类的属性赋初始值。
(5)沿着构造链,一直往上执行,(父类可能还有父类,继续调用父类的构造器),直到构造链的顶端。
(6)确保类所有属性(子类,父类所有的属性)都已经赋初始值,这就认为这个实例的内存已经完全初始化了,也就是可以使用了。阶段1完成。
注意了,阶段1中,因为实例还没完成初始化,不能调用任何实例方法,不能读取任何实例的值,也不能引用self作为值使用。(可以使用self赋值,如 self.name = name)
阶段2:
(1)顶端构造器开始往下执行,每个构造器都有机会定制实例。当然,因为实例已经初始化了,可以调用方法,也可以修改属性值,也可以使用self。
(2)最后,便利构造器也可以定制实例。
class RootClass {
var a: Int
init(a: Int) {
self.a = a // (3) ------------ 阶段1
apple() // (4) ------------ 阶段2
}
func apple() {
print("a apple")
}
}
class SonClass: RootClass {
var b: Int
init(b: Int) {
self.b = b // (2) ------------ 阶段1
super.init(a: 1)
banana() // (5) ------------ 阶段2
}
func banana() {
print("b banana")
}
}
class GrandsonClass: SonClass {
var c: Int
init(c: Int) {
self.c = c // (1) ------------ 阶段1
super.init(b: 2)
cat() // (6) ------------ 阶段2
}
func cat() {
print("c cat")
}
}
let c = GrandsonClass(c: 3)
打印结果
a apple
b banana
c cat
两段式构造的优点:
(1)防止属性值在初始化之前被访问
(2)防止属性值被另一个构造器意外的赋值(??)
4个安全检查
为了保证两段式构造能顺利进行,编译器对代码进行4个安全检查
安全检查1
在调用父类的构造器前,要保证本类的属性全部赋初始值(这是初始化)。
安全检查2
必须先调用父类的构造器,然后再定制属性的值(这是定制),否则定制的属性值会被父类构造器覆盖。
安全检查3
便利构造器必须先调用同类的其他构造器,然后再定制属性的值(便利构造器定制属性值),否则定制的属性值会被同类的其他构造器覆盖。
安全检查4
在第1阶段没有完成前,不能调用实例的方法,不能读取属性值,不能引用self作为值使用。
只要理解了两段式构造,自然就知道为什么会有这4个检查了,慢慢体会!!我也是晕了很久的,哈哈。
指定构造器和便利构造器用途
指定构造器主要用于初始化属性,给属性赋值。
便利构造器主要方便外部函数调用,最后还是要调用指定构造器对属性进行初始化。
只要认清这2点,应该知道用哪种构造器。
构造器的自动继承
如果子类的属性都有默认值,或者是可选的,并且没有定义任何的构造器,这个情况下,子类就会自动继承父类所有构造器。
可失败构造函数
如果构造函数传入的参数不符合需求,则可以返回nil
class ITPerson {
var name: String
var sex: String
init?(name: String, sex: String) {
if sex != "male" {
return nil
}
self.name = name
self.sex = sex
}
}
如果sex不是male的话,则返回nil,使用这种构造函数创建实例,这个实例的类型属于可选类型,ITPerson?
var person = ITPerson(name: "johan", sex: "female") // person类型为ITPerson?
person?.name
必要构造器
init前面加上“required”就是必要构造器了,表明该类的子类都要实现该构造器。
class Person {
required init() {
// 必要构造函数
}
}