- 与 Objective -C 不同,Swift 的构造器没有返回值(特殊情况,可失败构造器返回 nil)。
存储属性的初始赋值
初始化器内部,修改存储属性默认值,不会触发属性监听器
构造器 / 初始化器
- 最简形式
init() {
// 在此处执行构造过程
}
- 保存华氏温度的结构体
Fahrenheit
,它拥有一个Double
类型的存储型属性temperature
:
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// 打印“The default temperature is 32.0° Fahrenheit”
- 定义了一个不带形参的构造器
init
,并在里面将存储型属性temperature
的值初始化为32.0
(华氏温度下水的冰点)。
默认属性值
- 在属性声明时为
temperature
提供默认值
struct Fahrenheit {
var temperature = 32.0
}
自定义构造过程
形参的构造过程
- 跟函数和方法的形参相同
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius 是 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius 是 0.0
- 转换成摄氏温度值
形参命名和实参标签
struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)
let veryGreen = Color(0.0, 1.0, 0.0)
// 报编译期错误-需要实参标签
不带实参标签的构造器形参
- 使用下划线(
_
)来代替显式的实参标签来重写默认行为
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
init(_ celsius: Double){
temperatureInCelsius = celsius
}
}
let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius 为 37.0
可选属性类型
- 可选类型的属性将自动初始化为
nil
,表示这个属性是特意在构造过程设置为空
class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
cheeseQuestion.ask()
// 打印“Do you like cheese?”
cheeseQuestion.response = "Yes, I do like cheese."
- 调查问题的答案在询问前是无法确定的,因此我们将属性
response
声明为String?
类型
构造过程中常量属性的赋值
- 初始化方法内部,可给常量赋值,一旦赋值,将不可更
类的实例,常量属性只能在类的构造过程中修改;
常量属性,不能在子类中修改。
- 用常量属性替代变量属性
text
,表示创建之后不会再被修改
class SurveyQuestion {
let text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// 打印“How about beets?”
beetsQuestion.response = "I also like beets. (But not with cheese.)"
(自动生成)默认构造器
-
适用:结构体、类
-
前提条件
- 为所有属性有默认值
- 没有自定义一个构造器
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
结构体类型的成员初始化器
- 适用:结构体
- 跟默认初始化器区别
- 属性无需有默认值
- 生成的初始化方法,可传参,然后传入默认值
- 可省略拥有默认值的属性
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
let zeroByTwo = Size(height: 2.0)
print(zeroByTwo.width, zeroByTwo.height)
// 打印 "0.0 2.0"
let zeroByZero = Size()
print(zeroByZero.width, zeroByZero.height)
// 打印 "0.0 0.0"
值类型的构造器代理
- 构造器代理:初始化器调用其他初始化器
- 值类型构造器
- 不支持继承
- 用
self.init
在自定义构造器中引用其它构造器(内部) - 对于值类型,自定义构造器,默认构造器失效
- 解决:使用扩展(
extension
)自定义构造器
- 解决:使用扩展(
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
- 三种方式提供了三个自定义的构造器:
struct Rect {
var origin = Point()
var size = Size()
init() {}
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
let basicRect = Rect()
// basicRect 的 origin 是 (0.0, 0.0),size 是 (0.0, 0.0)
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
// originRect 的 origin 是 (2.0, 2.0),size 是 (5.0, 5.0)
- 先通过
center
和size
的值计算出origin
的坐标,然后再调用(或者说代理给)init(origin:size:)
构造器来将新的origin
和size
值赋值到对应的属性中
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
// originRect 的 origin 是 (2.0, 2.0),size 是 (5.0, 5.0)
类的继承和构造过程
- 类的存储属性(包括继承父类的),在初始化时,必须有默认值
- 保证存储属性有初始值的两构造器:
- 指定构造器 - designated initializers
- 便利构造器 - convenience initializers
指定构造器和便利构造器
- 指定初始化器(主要)
- 一个类至少有一个(继承父类也算)
- 像漏斗,连接父和子类的链条
- 便捷初始化器(可选)
- 可有可无
- 可调用指定初始化器
指定构造器和便利构造器的语法
- 指定初始化器:与值类型的一致
init(parameters) {
statements
}
- 便捷初始化器:多了一个
convenience
关键字
convenience init(parameters) {
statements
}
class 类型的构造器代理
- 构造器实现规则 - 指定和便利的调用关系
- 规则1:子类指定必须调用(直接)父类的指定
- 规则2:便利必须调用(同类)其它构造器。
- 规则3:便利构造器最后必须调用指定(间接调用也行)
- 记忆口诀
- 指定必向上调用
- 便利必横向调用
- 图解-三规则:
[图片上传失败...(image-bbdc9f-1608532055814)]
- 图示-父类为基类,规则 1 没用
- 两指定必须调用父类唯一(直接)指定,满足规则 1
3个规则,只影响构造器实现,不影响类的实例创建,上面任一构造器都可创建实例
- 4个类的例子:
<img src="https://www.logcg.com/wp-content/uploads/2015/08/initializerDelegation02_2x.png" alt="initializerDelegation02_2x" style="zoom: 50%;" />
两段式构造过程
- 类初始化两过程
- 第一阶段(完成初始化):存储属性全赋值
- 第二阶段(自定义操作):自定义 存储属性值
OC 初始化多为0或 nil,Swift 初始化全赋初值
-
两段式的四种安全检查:【实例非空 = 非继承属性有值】【自定义值前,避免覆盖】
- 安全检查 1:调用父类前,所有(非继承)属性必须赋值
- 安全检查 2:自定义继承属性值前,先调用父类初始化器(避免被覆盖)
- 安全检查3:便捷初始化器,先调用其他构造器,再自定义属性值(避免被覆盖)
- 安全检查4:第一阶段完成前,不能调用实例方法、属性、self(实例为 nil)
-
两段式初始化 + 四种检查 详细流程:
- 阶段1:
- 调用(指定 / 便利)构造器
- 完成实例的内存分配,但内存没被初始化(属性内存大小没确定)
- 调用指定构造器,确保存储属性的内存完成初始化
- 指定构造器,调用父类构造器,完成存储属性初始化
- 继承链顶部,最后一类的存储实现已赋值,这个实例被认为完全初始化(阶段1完成)
- 阶段2:(非必须)调用便利初始化器 = 自定义属性的初始化值
- 自顶向下,继承链中的每个类,在自定义实例时,可访问
self
、修改属性并调用实例方法等 - 便利构造器可自定义实例和使用
self
- 自顶向下,继承链中的每个类,在自定义实例时,可访问
- 阶段1:
-
阶段1
<img src="https://docs.swift.org/swift-book/_images/twoPhaseInitialization01_2x.png" alt="img" style="zoom: 50%;" />
- 阶段2:
<img src="https://docs.swift.org/swift-book/_images/twoPhaseInitialization02_2x.png" alt="img" style="zoom:50%;" />
构造器的继承和重写
- 与 OC 不同,Swift子类不继承父类构造器。(防止父类简单初始化器被子类继承后,无法完成初始化或被错误初始化)
特殊情况会继承。
参考后续章节 构造器的自动继承。
- 在子类可自定义与父类同名的指定初始化器(自动生成的父类初始化器,也可以重写)
- 必须写 override 修饰符(检查父类是否有同名初始化器)
重写指定初始化器,必须写 override 修饰符,哪怕重写为 便捷初始化器
- 先写一个父类(Swift 自动创建默认(指定)构造器)
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
let vehicle = Vehicle()
print("Vehicle: \(vehicle.description)")
// Vehicle: 0 wheel(s)
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
- Bicycle 自定义指定构造器
init()
,和父类指定构造器相匹配,所以要写 override 修饰符 - 若 没有自定义 numberOfWheels = 2 && 父类无参数 = 可省略
super.init()
的调用
- 隐式调用 super.init()(确保父类能完整初始化)
class Hoverboard: Vehicle {
var color: String
init(color: String) {
self.color = color
// super.init() 在这里被隐式调用
}
override var description: String {
return "\(super.description) in a beautiful \(color)"
}
}
继承来的属性,子类只能修改变量,不能修改常量
构造器的自动继承
-
默认情况,不继承父类构造器。
-
自动继承的前提: (记忆:属性全值;没指定,全指定;全指定,全便捷)
- 子类所有新属性有默认值
- 继承所有指定:
- 没有自定义指定构造器(便利除外)
- 继承所有便利:
- 实现了父类所有指定(继承的 、自定义的实现)
- 继承所有指定:
- 子类所有新属性有默认值
-
综上,属性全值 + 不自定义 = 继承全部的 指定 + 便利
子类可以将父类的指定构造器实现为便利构造器来满足规则 2
指定构造器和便利构造器实践
-
定义三个类
Food
、RecipeIngredient
以及ShoppingListItem
-
Food
引入一name
的String
类型的属性,两个构造器创建Food
实例
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
- Food 类初始化链
- class 没有逐一成员构造器,
Food
类提供一个接受单一参数name
的指定构造器
let namedMeat = Food(name: "Bacon")
// namedMeat 的名字是 "Bacon"
-
init(name: String)
确保Food
所有存储属性有值 = 指定构造器 -
Food
没父类 ,无需调用super.init()
-
RecipeIngredient
类用来表示食谱中的一项原料
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
类的构造器链
<img src="https://docs.swift.org/swift-book/_images/initializersExample02_2x.png" alt="img" style="zoom:50%;" />
-
指定构造器
init(name: String, quantity: Int)
,给RecipeIngredient
所有属性赋值 -
将父类同名的指定重写为了便利,
RecipeIngredient
自动继承所有便利构造器init()
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
- 购物单中的每一项总是从未购买状态开始的
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? " ✔" : " ✘"
return output
}
}
- 属性都有值 + 没定义构造器 = 继承父类 指定 + 便利 构造器
<img src="https://docs.swift.org/swift-book/_images/initializersExample03_2x.png" alt="img" style="zoom:50%;" />
- 使用三个继承来的构造器
var breakfastList = [
ShoppingListItem(),
ShoppingListItem(name: "Bacon"),
ShoppingListItem(name: "Eggs", quantity: 6),
]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
print(item.description)
}
// 1 x orange juice ✔
// 1 x bacon ✘
// 6 x eggs ✘
- 组的类型也能被自动推导为
[ShoppingListItem]
。
可失败构造器
- 语法:在
init
后面添加问号(init?
) - 类型:为自身类型的可选类型的对象
禁止可失败和非可失败的初始化器为相同参数类型和名称
- 通过
return nil
语句来表明可失败
- 严格来说,构造器都不支持返回值.
- 可写 return nil 触发初始化失败,但不能用 return 关键字来表示初始化成功
- 针对数字类型转换的可失败构造器
let wholeNumber: Double = 12345.0
let pi = 3.14159
if let valueMaintained = Int(exactly: wholeNumber) {
print("\(wholeNumber) conversion to Int maintains value of \(valueMaintained)")
}
// 打印“12345.0 conversion to Int maintains value of 12345”
let valueChanged = Int(exactly: pi)
// valueChanged 是 Int? 类型,不是 Int 类型
if valueChanged == nil {
print("\(pi) conversion to Int does not maintain value")
}
// 打印“3.14159 conversion to Int does not maintain value”
- 传入的
species
值是否为一个空字符串。如果为空字符串,则构造失败。
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty {
return nil
}
self.species = species
}
}
- 使用
let someCreature = Animal(species: "Giraffe")
// someCreature 的类型是 Animal? 而不是 Animal
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
// 打印“An animal was initialized with a species of Giraffe”
- 传入一个空字符串
let anonymousCreature = Animal(species: "")
// anonymousCreature 的类型是 Animal?, 而不是 Animal
if anonymousCreature == nil {
print("The anonymous creature could not be initialized")
}
// 打印“The anonymous creature could not be initialized”
空字符串(
""
)其实是一个有效的,非可选类型的字符串让
Animal
的可失败构造器构造失败,它更适合有一个具体的值,而不是空字符串
枚举类型的可失败构造器
- 场景:获取枚举类型中特定的枚举成员
enum TemperatureUnit {
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// 打印“This is a defined temperature unit, so initialization succeeded.”
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// 打印“This is not a defined temperature unit, so initialization failed.”
带原始值的枚举类型的可失败构造器
- 带原始值的枚举类型:自带一个可失败构造器
init?(rawValue:)
enum TemperatureUnit: Character {
case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
}
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// 打印“This is a defined temperature unit, so initialization succeeded.”
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// 打印“This is not a defined temperature unit, so initialization failed.”
构造失败的传递
- 代理到的其他可失败构造器触发构造失败,整个构造过程将立即终止,接下来的任何构造代码不会再被执行
可失败构造器也,可代理到其它的不可失败构造器
为已有的初始化过程添加初始化失败的条件
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
class CartItem: Product {
let quantity: Int
init?(name: String, quantity: Int) {
if quantity < 1 { return nil }
self.quantity = quantity
super.init(name: name)
}
}
-
若
quantity
值无效,则立即终止整个构造过程 -
成功创建
if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
// 打印“Item: sock, quantity: 2”
- 值为 0 的
quantity
来创建,失败
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
print("Unable to initialize zero shirts")
}
// 打印“Unable to initialize zero shirts”
- 值为空字符串的
name
来创建,失败
if let oneUnnamed = CartItem(name: "", quantity: 1) {
print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
print("Unable to initialize one unnamed product")
}
// 打印“Unable to initialize one unnamed product”
重写一个可失败构造器
- 子类重写父类可失败:
- 子类非失败重写父类可失败
- 需对父类可失败返回值强制解包
非失败重写可失败,但反过来却不行
- 例子:属性的值必须为一个非空字符串或
nil
class Document {
var name: String?
// 该构造器创建了一个 name 属性的值为 nil 的 document 实例
init() {}
// 该构造器创建了一个 name 属性的值为非空字符串的 document 实例
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
- 子类重写了所有父类引入的指定构造器
- 确保生成的实例中的
name
属性总有初始值"[Untitled]"
:
class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
}
override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
} else {
self.name = name
}
}
}
- 使用强制解包来调用父类的可失败构造器
class UntitledDocument: Document {
override init() {
super.init(name: "[Untitled]")!
}
}
- 这里是通过字符串常量来调用,构造器不会失败,所以不会(强制解包)运行时错误。
init! 可失败构造器
- 可失败构造器:
- (
init?
) - (
init!
)- 隐式解包
- (
- 调用:可在 init? 初始化器中委托调用 init! 初始化器,反之亦然。
- 重写:也可以用 init! 重写 init? ,反之亦然
- 用 init 委托调用 init!,初始化失败时会触发断言
必要(实现)构造器
- 子类都必须实现该初始化器
- 语法:类的构造器前添加
required
修饰符
class SomeClass {
required init() {
// 构造器的实现代码
}
}
- 重写父类的必要构造器
- 要添加
required
修饰符 - 重写父类必要的指定构造器时,不需要添加
override
修饰符
- 要添加
class SomeSubclass: SomeClass {
required init() {
// 构造器的实现代码
}
}
继承的构造器满足基本使用,则无需在子类实现必要构造器
通过闭包或函数设置属性的默认值
- 模板
class SomeClass {
let someProperty: SomeType = {
// 在这个闭包中给 someProperty 创建一个默认值
// someValue 必须和 SomeType 类型相同
return someValue
}()
}
- 花括号后面接了一对空的小括号, 立即执行此闭包
- 忽略小括号,将闭包本身作为值赋值给了属性
在闭包执行时,实例的其它部分都还没有初始化
不能在闭包里访问其它属性,即使这些属性有默认值,也不能使用隐式的
self
属性,或者调用任何实例方法
- 结构体
Chessboard
,构建西洋跳棋游戏的棋盘
<img src="https://docs.swift.org/swift-book/_images/chessBoard_2x.png" alt="img" style="zoom:33%;" />
-
值为
true
的元素表示一个黑格,值为false
的元素表示一个白格 -
第一个元素代表棋盘上左上角的格子,最后一个元素代表棋盘上右下角的格子
-
通过一个闭包来初始化并设置颜色值
struct Chessboard {
let boardColors: [Bool] = {
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoard
}()
func squareIsBlackAt(row: Int, column: Int) -> Bool {
return boardColors[(row * 8) + column]
}
}
let board = Chessboard()
print(board.squareIsBlackAt(row: 0, column: 1))
// 打印“true”
print(board.squareIsBlackAt(row: 7, column: 7))
// 打印“false”
网友评论