美文网首页
14、【Swift】构造 / 初始化- Initializati

14、【Swift】构造 / 初始化- Initializati

作者: Sunday_David | 来源:发表于2020-12-21 14:18 被阅读0次
  • 与 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)
  • 先通过 centersize 的值计算出 origin 的坐标,然后再调用(或者说代理给)init(origin:size:) 构造器来将新的 originsize 值赋值到对应的属性中
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

<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

指定构造器和便利构造器实践

  • 定义三个类 FoodRecipeIngredient 以及 ShoppingListItem

  • Food 引入一 nameString 类型的属性,两个构造器创建 Food 实例

class Food {
    var name: String
    init(name: String) {
        self.name = name
    }

    convenience init() {
        self.init(name: "[Unnamed]")
    }
}
  • Food 类初始化链
img
  • 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”

相关文章

  • 14、【Swift】构造 / 初始化- Initializati

    与 Objective -C 不同,Swift 的构造器没有返回值(特殊情况,可失败构造器返回 nil)。 存储属...

  • Swift 5.1 (14) - 初始化和反初始化

    Swift 5.1 (14) - 初始化和反初始化Swift 5.1 (14) - 初始化和反初始化

  • Swift构造器

    推荐swiftGG的官方文档翻译《swift构造器》 swift类的构造分为两个阶段,第一个阶段是指定构造器初始化...

  • Swift构造函数和便利构造函数

    [转]Swift构造函数和便利构造函数 构造函数基础 构造函数是一种特殊的函数,主要用来在创建对象时初始化对象,为...

  • IOS 自定义折线图[OC]

    swift版:https://www.jianshu.com/p/f581194cb02c 预览 变量初始化 构造...

  • Swift小结 -- Initialization (1)初始化

    详细描述请参考: The Swift Programming Language 第二章 构造过程(初始化过程)是使...

  • Swift学习:构造器(中)

    Swift之构造器(上)Swift之构造器(中)Swift之构造器(下) 本篇继续对Swift中的构造器进行介绍,...

  • Swift笔记:关于init的总结

    两种构造器 Swift中为确保类在创建时每个属性都会被初始化,定义了两种构造器,分别为指定构造器(designat...

  • swift3.0- 构造函数

    构造函数的介绍 作用: 对实例对象的内容进行初始化Swift要求类或者结构体中的存储属性(非lazy的)在对象构造...

  • 构造方法

    构造方法: 作用:对实例对象的内容进行初始化Swift要求类或者结构体中的存储属性(非lazy的)在对象构造完毕后...

网友评论

      本文标题:14、【Swift】构造 / 初始化- Initializati

      本文链接:https://www.haomeiwen.com/subject/iyernktx.html