属性初始化和自定义构造 2个月前

构造过程

构造过程是使用类、结构体或枚举类型的实例之前的准备过程。在新实例使用前有个过程是必须的,它包括设置实例中每个存储属性的初始值和执行其他必须的设置或构造过程。

你要通过定义构造器来实现构造过程,它就像用来创建特定类型新实例的特殊方法。与 Objective-C 中的构造器不同,Swift 的构造器没有返回值。它们的主要任务是保证某种类型的新实例在第一次使用前完成正确的初始化。

类的实例也可以通过实现析构器来执行它释放之前自定义的清理工作。想了解更多关于析构器的内容,请参考 析构过程

存储属性的初始赋值

类和结构体在创建实例时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。

你可以在构造器中为存储型属性设置初始值,也可以在定义属性时分配默认值。以下小节将详细介绍这两种方法。

注意

当你为存储型属性分配默认值或者在构造器中设置初始值时,它们的值是被直接设置的,不会触发任何属性观察者。

构造器

构造器在创建某个特定类型的新实例时被调用。它的最简形式类似于一个不带任何形参的实例方法,以关键字 init 命名:

init() {
// 在此处执行构造过程
}

下面例子中定义了一个用来保存华氏温度的结构体 Fahrenheit,它拥有一个 Double 类型的存储型属性 temperature

structFahrenheit {
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 提供默认值来使用更简单的方式定义结构体 Fahrenheit

structFahrenheit {
var temperature =32.0
}

自定义构造过程

你可以通过输入形参和可选属性类型来自定义构造过程,也可以在构造过程中分配常量属性。这些都将在后面章节中提到。

形参的构造过程

自定义构造过程时,可以在定义中提供 构造形参 ,指定其值的类型和名字。构造形参的功能和语法跟函数和方法的形参相同。

下面例子中定义了一个用来保存摄氏温度的结构体 Celsius。它定义了两个不同的构造器:init(fromFahrenheit:)init(fromKelvin:),二者分别通过接受不同温标下的温度值来创建新的实例:

structCelsius {
var temperatureInCelsius: Double
init(fromFahrenheitfahrenheit: Double) {
        temperatureInCelsius = (fahrenheit -32.0) /1.8
    }
init(fromKelvinkelvin: 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

第一个构造器拥有一个构造形参,其实参标签为 fromFahrenheit,形参命名为 fahrenheit;第二个构造器也拥有一个构造形参,其实参标签为 fromKelvin,形参命名为 kelvin。这两个构造器都将单一的实参转换成摄氏温度值,并保存在属性 temperatureInCelsius 中。

形参命名和实参标签

跟函数和方法形参相同,构造形参可以同时使用在构造器里使用的形参命名和一个外部调用构造器时使用的实参标签。

然而,构造器并不像函数和方法那样在括号前有一个可辨别的方法名。因此在调用构造器时,主要通过构造器中形参命名和类型来确定应该被调用的构造器。正因如此,如果你在定义构造器时没有提供实参标签,Swift 会为构造器的每个形参自动生成一个实参标签。

以下例子中定义了一个结构体 Color,它包含了三个常量:redgreenblue。这些属性可以存储 0.01.0 之间的值,用来表明颜色中红、绿、蓝成分的含量。

Color 提供了一个构造器,为红蓝绿提供三个合适 Double 类型的形参命名。Color 也提供了第二个构造器,它只包含名为 whiteDouble 类型的形参,它为三个颜色的属性提供相同的值。

structColor {
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
    }
}

两种构造器都能通过为每一个构造器形参提供命名值来创建一个新的 Color 实例:

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)
// 报编译期错误-需要实参标签

不带实参标签的构造器形参

如果你不希望构造器的某个形参使用实参标签,可以使用下划线(_)来代替显式的实参标签来重写默认行为。

下面是之前 形参的构造过程Celsius 例子的扩展,多了一个用已经的摄氏表示的 Double 类型值来创建新的 Celsius 实例的额外构造器:

structCelsius {
var temperatureInCelsius: Double
init(fromFahrenheitfahrenheit: Double) {
        temperatureInCelsius = (fahrenheit -32.0) /1.8
    }
init(fromKelvinkelvin: Double) {
        temperatureInCelsius = kelvin -273.15
    }
init(_celsius: Double){
        temperatureInCelsius = celsius
    }
}

let bodyTemperature =Celsius(37.0)
// bodyTemperature.temperatureInCelsius 为 37.0

构造器调用 Celsius(37.0) 意图明确,不需要实参标签。因此适合使用 init(_ celsius: Double) 这样的构造器,从而可以通过提供未命名的 Double 值来调用构造器。

可选属性类型

如果你自定义的类型有一个逻辑上允许值为空的存储型属性——无论是因为它无法在初始化时赋值,还是因为它在之后某个时机可以赋值为空——都需要将它声明为 可选类型。可选类型的属性将自动初始化为 nil,表示这个属性是特意在构造过程设置为空。

下面例子中定义了类 SurveyQuestion,它包含一个可选 String 属性 response

classSurveyQuestion {
var text: String
var response: String?
init(text: String) {
        self.text= text
    }
funcask() {
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? 类型,或者说是 “可选类型 String“。当 SurveyQuestion 的实例初始化时,它将自动赋值为 nil,表明“暂时还没有字符“。

构造过程中常量属性的赋值

你可以在构造过程中的任意时间点给常量属性赋值,只要在构造过程结束时它设置成确定的值。一旦常量属性被赋值,它将永远不可更改。

注意

对于类的实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。

你可以修改上面的 SurveyQuestion 示例,用常量属性替代变量属性 text,表示问题内容 textSurveyQuestion 的实例被创建之后不会再被修改。尽管 text 属性现在是常量,我们仍然可以在类的构造器中设置它的值:

classSurveyQuestion {
let text: String
var response: String?
init(text: String) {
        self.text= text
    }
funcask() {
print(text)
    }
}
let beetsQuestion =SurveyQuestion(text:"How about beets?")
beetsQuestion.ask()
// 打印“How about beets?”
beetsQuestion.response ="I also like beets. (But not with cheese.)"
image
EchoEcho官方
无论前方如何,请不要后悔与我相遇。
1377
发布数
439
关注者
2222874
累计阅读

热门教程文档

MyBatis
19小节
Spring Cloud
8小节
Swift
54小节
React
18小节
Next
43小节