构造过程:为某个类、结构体或枚举的实例中的每个属性设置初始值和为其执行必要准备和初始化任务。
构造器:用来创建特定类型实例的特殊方法,保证实例在第一次使用前完成正确的初始化。
14.1 存储型属性的初始赋值
背景:类和结构体在创建实例时,必须为所有
存储型属性
设置合适的初始值。存储型属性的值不能处于一个未知的状态。
说明:可以在构造器中
为存储型属性赋初值,也可以在定义属性时
为其设置默认值
初始化时机 | 初始化方式 | 是否触发属性观察器 | 是否自动推导类型 |
---|---|---|---|
定义属性时 | 设置默认值 | 否 | 是 |
构造器中 | 为存储型属性赋初始值 | 否 | 否 |
14.1.1 构造器
说明:构造器在创建某个特定类型的新实例时被调用,以
init
关键字声明构造函数
1 | struct Fahrenheit { |
14.1.2 默认属性值
说明:相比
在构造器中设置初始值
有如下优点
- 将属性的声明和初始化结合得更紧密
- 使构造器更加简洁、清晰
- 能够自动推导出属性的类型(不需要声明属性的类型)
- 充分利用默认构造器、构造器继承
1 | struct Fahrenheit { |
14.2 自定义构造过程
14.2.1 构造参数
说明:构造器参数的功能和语法跟函数和方法参数相同。
1 | struct Celsius { |
14.2.2 内部和外部参数名
说明:和
函数(方法)
不同点在于第一个参数也会有外部参数名(同后面的其它参数一样)。
注意:如果构造起定义了某个外部参数名,就必须使用它。
1 | struct Color { |
14.2.3 可选属性类型
说明:如果你定制的类型包含一个逻辑上允许取值为空的存储型属性,需要将它定义为可选类型。
- 可选类型的属性将自动初始化为
nil
使用场景:
- 无法在初始化时赋值
- 在之后某个时间点可以赋值为空
1 | // 问题调查 |
14.2.4 构造过程中常量属性的修改
说明:可以在构造过程中的任意时间点修改常量属性的值
实例属性类型 | 可修改时机 | 是否可在子类中修改 |
---|---|---|
常量属性 | 构造过程结束前 | 否 |
变量属性 | 实例的整个生命周期 | 是 |
1 | class SurveyQuestion { |
14.3 默认构造器
说明:满足以下条件的
结构体
或类
会拥有一个默认的构造器。
- 所有属性已提供默认值
- 自身没有定义任何构造器
注意:类必须要没有父类的基类
1 | //1:所有属性都有默认值;2:基类;3:没有定义构造器 |
14.3.1 结构体的逐一成员构造器
说明:满足了以下条件的
结构体
将自动获得一个逐一成员构造器
。
- 没有自定义构造器
用途:用来初始化结构体新实例里成员属性的快捷方法
语法:在调用逐一成员构造器时,通过与成员属性名相同的参数名进行传值来完成对成员属性的初始赋值。
1 | struct Size { |
14.4 值类型的构造器代理
说明:
构造器代理
指在构造器中调用其它构造器完成构造过程
类型 | 说明 | |
---|---|---|
值类型 | 结构体、枚举 | 只能代理给本身提供的其它构造器,可以在构造器内部使用self.init 调用其它构造器 |
类类型 | 类 | 代理给本身的构造器或父类的构造器(完成父类的初始化) |
注意:一旦在类型的原始定义中定义了构造器
- 结构体:无法访问
默认构造器
和逐一成员构造器
- 类:无法访问
默认构造器
引伸:假如你希望
默认构造器
、逐一成员构造器
以及你自己的自定义构造器
都能用来创建实例,可以将自定义的构造器写到扩展
中。
1 | struct Size { |
14.5 类的继承和构造过程
说明:
类
里面的所有存储型属性
(包括所有继承自父类的属性)都必须在构造过程中设置初始值。Swift
为类提供了两种构造器来确保实例中所有存储型属性都能获得初始值
- 指定构造器
- 便利构造器
14.5.1 指定构造器和便利构造器
说明:
指定构造器
是类中最主要的构造器,便利构造器
是类中比较次要的、辅助型的构造器。
特性 | 指定构造器 | 便利构造器 |
---|---|---|
使用场景 | 必需 | 必要时(快捷调用某个指定构造器来使类更加清晰) |
数量 | 至少一个(可以从父类继承) | 至少零个 |
用途 | 初始化类中所有属性,初始化父类 | 次要、辅助型构造器 |
使用 | 根据父类链往上调用父类的构造器 | 调用同一个类中的指定构造器 |
构造器链规则 | 总是向上代理 | 总是横向代理 |
>**语法:**`指定构造器`和`便利构造器`
1 | // 指定构造器 |
14.5.2 构造器链
- 指定构造器
- 必须调用直接父类的指定构造器
- 便利构造器
- 必须调用同一类中的其它构造器
- 必须最终以调用一个指定构造器结束
14.5.3 两段式构造过程
14.5.3.1 两个阶段
阶段一:通过构造器设置初始值
成员 | 状态 |
---|---|
实例方法 | 不能调用 |
实例属性 | 不能读取 |
self |
不能引用 |
1 | st=>start: 开始 |
阶段二:在新实例准备使用之前进一步定制它们的存储型属性(可以访问
self
,修改属性、调用实例方法)
特点:沿构造器链从上往下
1 | st=>start: 开始 |
14.5.3.2 安全检查
- 指定构造器中的安全检查
1 | st=>start: 调用指定构造器 |
- 便利构造器中的安全检查
1 | st=>start: 调用便利构造器 |
14.5.4 构造器的继承和重写
说明:编写一个和父类中指定构造器相匹配的子类构造器时,你实际上是在重写父类的这个指定构造器。
- 子类不会默认继承父类的构造器(除非满足一定条件)
- 子类可以重载父类的构造器
- 子类可以在初始化时修改继承来的
变量属性
,但是不能修改继承来的常量属性
- 重载构造器一定需要使用
override
(即使将父类的指定构造器重写为了便利构造器)注意:子类中“重写”一个父类
便利构造器
时,不需要加override
前缀(子类不能直接调用父类的便利构造器
,因此并没有真的重写
)
1 | // 基类(超类、父类) |
14.5.5 自动构造器的继承
说明:子类在默认情况下不会继承父类的构造器。但是如果满足特定条件,父类构造器是可以被自动继承的。
特定条件 | 说明 |
---|---|
子类没有定义任何指定构造器 | 自动继承父类所有的指定构造器 |
子类提供了所有父类构造器的实现(继承过来的也算) | 自动继承所有父类的便利构造器 |
14.5.5.1 指定构造器和便利构造器语法
- 指定构造器
1 | init(parameters){ |
- 便利构造器
1 | convenience init(paramaters){ |
14.5.5.2 指定构造器和便利构造器实战
第一层:食品类
1 | /** |
第二层:调味剂类
1 | /** |
第三层:购物清单类
1 | /** |
14.6 可失败构造器
支持:类、结构体、枚举
说明:在构造自身的过程中有可能失败的情况,可以定义一个(或多个)可失败的构造器,“失败”是指
- 构造器传入无效的参数
- 缺少某种需要的外部资源
- 不满足某种必要条件等
语法:
init?
1 | init? (...) { |
注意:严格说来构造器并不支持返回值(失败构造器表明失败的
return
是个特例)
1 | struct Animal { |
14.6.1 枚举类型的可失败构造器
1 | enum TemperatureUnit{ |
14.6.2 带原始值的枚举类型的可失败构造器
说明:带原始值的枚举类型会自带一个可失败构造器
- 该可失败构造器有一个名为
rawValue
的参数,其类型和枚举类型的原始值类型一致- 如果该参数的值能够和某个枚举成员的原始值匹配,则该构造器会构造相应的枚举成员,否则构造失败
语法:
init?(rawValue:)
组成 | 说明 |
---|---|
参数名 | rawValue |
参数类型 | 和case的原始值(绑定的值)类型一致 |
返回值 | 参数和某个成员的原始值匹配则返回该值;否则构造失败,返回nil |
1 | enum TemperatureUnit:Character{ |
14.6.3 类的可失败构造器
说明:类的所有属性被初始化完毕,以及构造器之间的代理调用都结束后才能触发败行为。
1 | class Product { |
14.6.4 构造失败的传递
传递方式:
横向代理
和向上代理
传递方式 | 支持 | 说明 |
---|---|---|
横向代理 | 结构体、枚举、类 | 可失败构造器可以横向代理其它构造器(包括可失败构造器) |
向上代理 | 类 | 子类的可失败构造器可以向上代理父类的构造器(包括可失败构造器) |
注意:
- 当代理可失败构造器触发构造失败时,整个构造过程将终止(无论横向代理还是向上代理)
- 非可失败构造器不能代理失败构造器 (
init!
声明的除外,但会触发断言)
1 | class Product { |
14.6.5 重写一个可失败构造器
说明:可以在子类中重写父类的
可失败构造器
(通过定义一个可失败构造器
或非可失败构造器
)
用途:即使父类的构造器为可失败构造器,但当子类的构造器在构造过程不可能失败时,我们也可以把它修改过来。
情景:
- 子类的
可失败构造器
覆盖父类的可失败构造器- 子类的
非可失败构造器
覆盖父类的可失败构造器注意:
- 当你用子类的
非可失败构造器
重写父类的可失败构造器时,向上代理到父类的可失败构造器的唯一方式是对父类的可失败构造器的返回值进行强制解包
。- 你可以用
非可失败构造器
重写可失败构造器,但反过来却不行
1 | // 父类 |
14.6.6 可失败构造器init!
说明:构造
可失败构造器
其实有两种方式,两种方式创建的构造器可以相互代理调用
,也可以相互重写
对方。
init?
init!
:会构建一个相应类型的隐式解包可选类型的对象。描述:用
init!
声明的可失败构造器
注意:还可以用init
代理到init!
,不过,一旦init!
构造失败,则会触发一个断言。
14.7 必要构造器
说明:在类的构造器前添加
required
修饰符,称为必要构造器
。所有该类的子类都必须实现该构造器。在子类重写父类的必要构造器时
,必须在子类的构造器前也添加required
修饰符。
1 | // 父类 |
14.8 通过闭包和函数来设置属性的默认值
说明:如果某个
存储型属性的默认值
需要一些定制或设置
,你可以使用闭包
或全局函数
为其提供定制的默认值。
限制:立即执行闭包
执行时,实例的其他部分还没有初始化,因此
- 不能够在闭包中访问其它属性
- 不能使用
self
属性- 不能调用其它实例方法
1 | >class SomeClass:SomeType { |
1 | struct Checkedboard { |