初始化
初始化是准备使用的类,结构或枚举实例的过程。此过程涉及为该实例上的每个存储属性设置一个初始值,并执行新实例准备使用之前所需的任何其他设置或初始化。
您可以通过定义初始值设定项来实现此初始化过程,初始值设定项类似于可以调用以创建特定类型新实例的特殊方法。与Objective-C初始值设定项不同,Swift初始值设定项不会返回值。它们的主要作用是确保首次使用类型之前,正确初始化类型的新实例。
类类型的实例还可以实现一个deinitializer,它在释放该类的实例之前执行任何自定义清除。有关反初始化程序的更多信息,请参见反初始化。
1. 设置存储属性的初始值
- 类和结构必须在创建该类或结构的实例时将其所有存储的属性设置为适当的初始值。
- 存储的属性不能处于不确定状态。
您可以在初始化程序中为存储的属性设置初始值,或者通过将默认属性值分配为属性定义的一部分来设置初始值。以下各节介绍了这些操作。
注意
当您为存储的属性分配默认值,或在初始化程序中设置其初始值时,将直接设置该属性的值,而无需调用任何属性观察器。
1.1 初始化器
调用初始化程序以创建特定类型的新实例。最简单的形式是,初始化程序就像没有参数的实例方法,使用init关键字编写:
init() {
// perform some initialization here
}
下面的示例定义了一个新结构,称为Fahrenheit存储以华氏度表示的温度。该Fahrenheit结构具有一个存储属性temperature,其类型为Double:
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// Prints "The default temperature is 32.0° Fahrenheit"
该结构定义了一个init没有参数的初始化程序,该初始化程序使用值32.0(水的冰点,以华氏度为单位)初始化存储的温度。
1.2 默认属性值
您可以从初始化程序中设置存储属性的初始值,如上所示。或者,指定默认属性值作为属性声明的一部分。您可以通过在定义属性时为其分配初始值来指定默认属性值。
注意
如果属性始终采用相同的初始值,请提供默认值,而不要在初始化程序中设置值。最终结果是相同的,但是默认值将属性的初始化与其声明紧密联系在一起。它使初始化程序更短,更清晰,并使您能够从其默认值推断属性的类型。默认值还使您更容易利用默认初始化程序和初始化程序继承,如本章稍后所述。
您可以Fahrenheit通过temperature在声明属性时为其属性提供默认值,以更简单的形式从上面编写结构:
struct Fahrenheit {
var temperature = 32.0
}
2. 自定义初始化
您可以使用输入参数和可选属性类型,或通过在初始化期间分配常量属性来自定义初始化过程,如以下各节所述。
2.1 初始化参数
您可以提供初始化参数作为初始化程序定义的一部分,以定义自定义初始化过程的值的类型和名称。初始化参数具有与功能和方法参数相同的功能和语法。
以下示例定义了一个名为的结构Celsius,该结构存储以摄氏度表示的温度。该Celsius结构实现了两个自定义的初始化程序,称为init(fromFahrenheit:)和init(fromKelvin:),它们使用不同温度范围内的值初始化该结构的新实例:
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 is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0
- 第一个初始化程序具有一个初始化参数,其参数标签为fromFahrenheit,参数名称为fahrenheit。
- 第二个初始化程序具有一个初始化参数,其参数标签为fromKelvin,参数名称为kelvin。
两个初始化程序都将其单个参数转换为相应的摄氏值,并将此值存储在名为的属性中temperatureInCelsius。
2.2 参数名称和参数标签
与函数和方法参数一样,初始化参数可以具有用于初始化程序主体的参数名称和用于调用初始化程序的参数标签。
但是,初始化程序在其括号前没有以函数和方法那样的方式标识函数的名称。因此,初始化器参数的名称和类型在确定应调用哪个初始化器中起着特别重要的作用。因此,如果您不提供初始化方法,则Swift会为初始化程序中的每个参数提供一个自动参数标签。
下面的例子定义了一个名为结构Color,具有三个恒定属性叫做red,green,和blue。这些属性在0.0和之间存储一个值,1.0以指示颜色中红色,绿色和蓝色的数量。
Color为初始化程序Double的红色,绿色和蓝色分量提供三个适当命名的类型的参数。Color还提供了带有单个white参数的第二个初始化器,该初始化器用于为所有三个颜色分量提供相同的值。
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
}
}
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)
// this reports a compile-time error - argument labels are required
2.3 不带参数标签的初始化参数
如果不想为初始化参数使用参数标签,**请为该参数写下划线(_)**而不是显式参数标签,以覆盖默认行为。
带有一个附加的初始化程序,可使用已经在摄氏度范围内的值来创建新实例:CelsiusDouble
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)
初始化调用Celsius(37.0)的意图很明确,不需要参数标签。因此,应将此初始化程序编写为:可以通过提供未命名的值来调用它。init(_ celsius: Double)Double
2.4 可选属性类型
如果您的自定义类型具有逻辑上**允许具有“无值”**的存储属性
- 可能是因为在初始化期间无法设置其值
- 稍后允许其具有“无值”
请使用可选类型。可选类型的属性将使用值自动初始化nil,表明该属性在初始化过程中故意具有“没有值”。
以下示例定义了一个名为的类SurveyQuestion,其具有一个可选String属性response:
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()
// Prints "Do you like cheese?"
cheeseQuestion.response = "Yes, I do like cheese."
直到询问问题后,才能知道对调查问题的回答,因此该response属性用String?或的类型声明为“可选String”。初始化nil新实例时,会自动为其指定默认值,表示“没有字符串” SurveyQuestion。
2.5 在初始化期间分配常量属性
您可以在初始化期间的任何时候为常量属性分配一个值,只要在初始化完成时将其设置为确定值即可。为常数属性分配值后,就无法再对其进行修改。
注意
对于类实例,只能在引入常量的类的初始化期间对其进行修改。子类不能修改它。
您可以SurveyQuestion从上面修改示例,以text对问题的属性使用常量属性而不是变量属性,以表明一旦SurveyQuestion创建了实例,问题就不会更改。即使该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()
// Prints "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)"
3. 默认初始化器
迅速提供了一个默认初始值对于所有其属性提供缺省值,并且不提供至少一个初始值设定本身的任何结构或类。默认初始化程序仅创建一个新实例,并将其所有属性设置为其默认值。
本示例定义了一个名为的类ShoppingListItem,该类将购物清单中商品的名称,数量和购买状态封装起来:
class ShoppingListItem {
var name: String?
var quantity = 1
v