类和结构体的构造器
- Swift的构造器构造出来的实例由系统隐式返回,不用return
- 构造器的作用完成每个类,结构体中实例存储属性的初始化
- 为实例存储属性赋初始值的时机:1.定义实例存储属性时指定初始值。2.在构造器中为实例存储属性指定初始值.
- swift只为类提供一个无参数的构造器
- swift为结构体提供两个构造器:无参数的构造器和初始化所有实例存储属性的构造器
构造器代理:
在定义构造器时,通过self.init(实参)调用其他构造器来完成实例的部分构造过程,就是A构造器调用B构造器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35struct Apple
{
var name: String
var color: String
var weight: Double!
init(_ name: String , _ color:String)
{
self.name = name
self.color = color
}
// 两个参数的构造器
init(name: String , color:String)
{
self.init(name , color) // 构造器代理
}
// 为构造器显式指定外部形参名
init(appleName n: String , appleColor c:String)
{
// name = "临时值" // 这行代码将导致错误
self.init(n , c) // 构造器代理
// 调用构造器代理之后,即可通过self访问该实例的属性
print("--执行显式指定外部形参名的构造器--\(self.name)")
}
// 定义三个参数的构造器
init(name: String , color: String , weight: Double)
{
self.init(name , color) // 构造器代理
self.weight = weight
}
}
var ap1 = Apple("红富士" , "粉红色")
print("\(ap1.name)--->\(ap1.color)")
var ap2 = Apple(appleName:"青苹果" , appleColor:"绿色")
print("\(ap2.name)--->\(ap2.color)")
var ap3 = Apple(name:"美国苹果" , color:"红色" , weight:0.45)
指定构造器
一个类中至少有一个指定构造器,其必须负责初始化类中所有的实例存储属性。
便利构造器
- 便利构造器属于次要的,辅助性的构造器
- 类中可以不定义便利构造器,便利构造器必须调用同一个类中的其他构造器完成初始化
- 只有类中才有便利构造器的概念
1 | 便利构造器的语法格式: |
1 | class Apple{ |
类的构造器链
规则
- 子类构造器必须调用直接父类的指定构造器(如果有父类)
- 便利构造器必须调用同一个类中的其他构造器;
- 便利构造器调用的构造器链的最终节点必须是指定构造器
简化记忆为:
- 指定构造器总是向上代理(调用父类构造器)
- 便利构造器总是必须横向代理(调用当前类的其他构造器)
类的构造器链1
类的构造器链2
代码示例
1 | class Fruit { |
两段式构造
第一阶段
- 程序调用子类的某个构造器
- 为实例分配内存,此时实例的内存还没有被初始化
- 指定构造器确保子类定义的所有实例存储属性都已被赋初值
- 指定构造器将调用父类的构造器,完成父类定义的实例存储属性的初始化
- 沿着调用父类构造器的构造器链一直往上执行,直到到达构造器链的最顶部
第二阶段
- 沿着继承树往下,构造器此时可以修改实例属性,访问self,甚至可以调用实例方法
- 最后,构造器链中的便利构造器都有机会定制实例和使用self
安全检查规则
- 指定构造器必须先初始化当前类中定义的实例存储属性,然后才能向上调用父类构造器
- 指定构造器必须先向上调用父类构造器,然后才能对继承得到的属性赋值
- 便利构造器必须先调用同一个类的其他构造器,然后才能对属性赋值。否则如果先赋值,在调用,那就会把刚刚赋值的给覆盖掉
- 构造器在第一阶段完成之前,不能调用实例方法,不能读取实例属性
- 总之,建议实例存储属性指定初始值
安全检查代码示例1
1 | class Fruit { |
安全检查代码示例2
1 | class Fruit { |
继承
Swift的子类不会自动继承父类的构造器,若继承,则满足如下规则:
1.如果子类没有提供任何指定构造器,那么它将自动继承父类的所有指定构造器
2.如果子类实现了父类所有的指定构造器,无论如何实现的,都将自动继承父类的所有便利构造器
1 | class Fruit { |
重写
- 子类构造器重写了父类的指定构造器,必须添加override修饰符
- 子类中定义的构造器只是与父类中便利构造器的形参列表,外部形参名相同,不算重写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41class Fruit {
var name:String
var weight:Double
init(){ //1
self.name = ""
self.weight = 0.0
}
init(name:String,weight:Double){ //2
self.name = name
self.weight = weight
}
convenience init(name:String){ //3
self.init(name:name,weight:0.0)
}
convenience init(_ name:String){ //4
self.init(name:name)
}
}
class Apple:Fruit {
var color:String
//重写父类的指定构造器 //override 2
override init(name:String,weight:Double){
self.color = "Default"
//调用父类构造器
super.init(name:name, weight:weight)
}
//定义指定构造器 //5 这个和父类的便利构造器名和形参名都相同,但是不算做重写
init(name:String){
self.color = "Default"
super.init(name: name, weight: 0.0)
}
//便利构造器
convenience init(name: String, weight: Double,color:String) { //6
self.init(name:name,weight:weight)
self.weight = weight
}
//使用便利构造器重写父类的指定构造器 //override 1
override convenience init() {
self.init(name:"Apple",weight:0.0,color:"Pink")
}
}
可能失败的构造器
- 可能失败的构造器:使用Init?或者!关键字进行定义
- 在构造器执行体中使用return nil 来表示构造器失败
- Swift不允许定义两个相同形参列表的构造器。即使一个是可能失败的构造器,一个是普通的构造器也不行
可能失败的构造器必须满足如下两个条件才能触发:
- 该类中的所有实例存储属性都已被赋初始值
- 所有的构造器调用都已经被执行
- 可能失败的构造器可以调用同一个类中的普通构造器
- 普通构造器不能调用同一个类中可能失败的构造器init?,但可以调用init!隐式解析的构造器
- 结构体中,普通构造器却可以调用同一个结构体中可能失败的构造器
代码1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18struct Cat
{
let name: String
init?(name: String) {
// 如果传入的name参数为空字符串,构造器失败,返回nil
if name.isEmpty {
return nil
}
self.name = name
}
}
let c1 = Cat(name: "Kitty")
if c1 != nil {
// 创建c1的构造器是init?,因此程序必须对c1执行强制解析
print("c1的name为:\(c1!.name)")
}
let c2 = Cat(name: "")
print(c2 == nil) // 输出true,表明c2为nil
1 | enum Season{ |
代码2
1 | class User { |
1 | class User{ |
重写可能失败的构造器
- 子类可以用可能失败的构造器或者普通的构造器重写父类中的可能失败的构造器
- 子类的普通构造器不能向上调用父类的可能失败的构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40class Bird {
var name:String!
//定义普通构造器
init(){}
//定义可能失败的构造器
init?(name:String){
if name.isEmpty{
return nil
}
self.name = name
}
}
class Sparrow:Bird {
let weight:Double!
init?(name:String,weight:Double){
//由于该构造器是可能失败的构造器,可以调用父类可能失败的构造器
super.init(name: name)
if weight <= 0 {
return nil
}
self.weight = weight
self.name = name
}
//使用普通构造器重写父类的构造器
override init(name: String) {
//由于该构造器是普通构造器,因此不能调用父类的可能失败的构造器
//因此只能调用父类的普通构造器
super.init()
if name.isEmpty {
super.name = "麻雀"
}
self.name = name
}
}
let sp1 = Sparrow(name: "")
print("sp1的name:\(sp1.name)")
let sp2 = Sparrow(name: "", weight: 2.2)
print(sp2 == nil)
let sp3 = Sparrow(name: "小黄嘴", weight: 0)
print(sp3 == nil)
required构造器
- Swift允许在父类构造器前添加require关键字,用于声明所有子类必须包含该require构造器
- 父类中声明的required构造器即可是指定构造器,也可以便利构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31class Fruit {
var name:String
var weight:Double
required init(name:String){//1
self.name = name
self.weight = 0.0
}
required convenience init(name:String,weight:Double){//2
self.init(name:name)
self.weight = weight
}
}
class Apple:Fruit {
var color:String
required init(name:String,weight:Double){// override 2
self.color = "Pink"
super.init(name: name)
}
init(color:String){
self.color = color
super.init(name: "")
}
required convenience init(name:String){//override 1
self.init(color:"aaaa")
super.name = name
}
}
//该类将会继承得到父类的request构造器
class Grape:Fruit{
var sugarRate:Double = 0.45
}
析构器
- 析构器是一个名为deinit的函数,不需要使用func关键字,无参数和返回值
- 析构器在实例释放之前由系统自动调用,不要主动调用析构器
- 子类自动继承父类的析构器,而且无论如何,子类析构器一定会调用父类析构器
- 析构器可以访问该实例的所有实例存储属性,或者根据这些属性来关闭资源,比如关闭哪个文件,哪个程序链接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30class Fruit {
var name:String
var weight:Double
//定义指定构造器
init(name:String){
self.name = name
self.weight = 0.0
}
//定义析构器
deinit{
print("程序准备释放fruit")
//此处可访问实例属性,可用于释放资源
}
}
class Apple:Fruit {
var color:String
//定义指定构造器
init(name:String,weight:Double,color:String){
self.color = color
super.init(name: name)
}
//定义析构器
deinit{
print("程序准备释放apple")
//此处可访问实例属性,可用于释放资源
}
}
var ap:Apple? = Apple(name: "红富士", weight: 0.34, color: "红色")
print(ap!.name + "-->" + ap!.color)
ap = nil //此处会自动调用析构器
总结
- 便利构造器不是必要的,使用convenience关键字,横向调用类中的构造器,代码执行顺序:先调用构造器,再赋值
- 指定构造器,一个类中至少有一个指定构造器,其必须负责初始化类中所有的实例存储属性
- 指定构造器的代码执行顺序: 初始化属性–>赋值–>super init—> 赋值继承得到的属性
- 类的构造第一阶段, 初始化属性–>赋值–>super init—> 赋值继承得到的属性 —>调用该属性
- 类的构造第二阶段, 赋值或调用
- 总之建议变量初始化的时候就直接赋值,最好。
- 构造器的继承,子类有自己的就用自己的,没有就继承父类的全部
- 重写使用override
- 可能失败的构造器
- 重写可能失败的构造器
- required构造器
- 析构器