This commit is contained in:
949478479
2015-11-12 21:43:13 +08:00
parent 0aab1a5c49
commit b1d42fa534

View File

@ -24,7 +24,7 @@
- [通过闭包和函数来设置属性的默认值](#setting_a_default_property_value_with_a_closure_or_function)
构造过程是使用类、结构体或枚举类型一个实例的准备过程。在新实例可用前必须执行这个过程,具体操作包括设置实例中每个存储型属性的初始值和执行其他必须的设置或初始化工作。
构造过程是使用类、结构体或枚举类型的实例之前的准备过程。在新实例可用前必须执行这个过程,具体操作包括设置实例中每个存储型属性的初始值和执行其他必须的设置或初始化工作。
通过定义构造器(`Initializers`)来实现构造过程,这些构造器可以看做是用来创建特定类型新实例的特殊方法。与 Objective-C 中的构造器不同Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。
@ -37,13 +37,13 @@
你可以在构造器中为存储型属性赋初值,也可以在定义属性时为其设置默认值。以下小节将详细介绍这两种方法。
>注意:
> 注意
当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观察者(`property observers`)。
<a name="initializers"></a>
### 构造器
构造器在创建某特定类型的新实例时调用。它的最简形式类似于一个不带任何参数的实例方法,以关键字`init`命名
构造器在创建某特定类型的新实例时调用。它的最简形式类似于一个不带任何参数的实例方法,以关键字`init`命名
```swift
init() {
@ -65,15 +65,15 @@ print("The default temperature is \(f.temperature)° Fahrenheit")
// 输出 "The default temperature is 32.0° Fahrenheit”
```
这个结构体定义了一个不带参数的构造器`init`,并在里面将存储型属性`temperature`的值初始化为`32.0`(华氏度下水的冰点)。
这个结构体定义了一个不带参数的构造器`init`,并在里面将存储型属性`temperature`的值初始化为`32.0`(华氏度下水的冰点)。
<a name="default_property_values"></a>
### 默认属性值
如前所述,你可以在构造器中为存储型属性设置初始值。同样,你也可以在属性声明时为其设置默认值。
>注意
如果一个属性总是使用相同的初始值,那么为其设置一个默认值比每次都在构造器中赋值要好。两种方法的效果是一样的,只不过使用默认值让属性的初始化和声明结合更紧密。使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型;同时,它也能让你充分利用默认构造器、构造器继承等特性(后续章节将讲到)。
> 注意
如果一个属性总是使用相同的初始值,那么为其设置一个默认值比每次都在构造器中赋值要好。两种方法的效果是一样的,只不过使用默认值让属性的初始化和声明结合更紧密。使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型;同时,它也能让你充分利用默认构造器、构造器继承等特性(后续章节将讲到)。
你可以使用更简单的方式在定义结构体`Fahrenheit`时为属性`temperature`设置默认值:
@ -86,14 +86,14 @@ struct Fahrenheit {
<a name="customizing_initialization"></a>
## 自定义构造过程
你可以通过输入参数和可选属性类型来自定义构造过程,也可以在构造过程中修改常量属性。这些都将在后面章节中提到。
你可以通过输入参数和可选类型的属性来自定义构造过程,也可以在构造过程中修改常量属性。这些都将在后面章节中提到。
<a name="initialization_parameters"></a>
### 构造参数
自定义`构造过程`时,可以在定义中提供构造参数,指定所需值的类型和名字。构造参数的功能和语法跟函数和方法的参数相同。
下面例子中定义了一个包含摄氏度温度的结构体`Celsius`。它定义了两个不同的构造器:`init(fromFahrenheit:)``init(fromKelvin:)`,二者分别通过接受不同刻度表示的温度值来创建新的实例:
下面例子中定义了一个包含摄氏度温度的结构体`Celsius`。它定义了两个不同的构造器:`init(fromFahrenheit:)``init(fromKelvin:)`,二者分别通过接受不同温标下的温度值来创建新的实例:
```swift
struct Celsius {
@ -116,13 +116,13 @@ let freezingPointOfWater = Celsius(fromKelvin: 273.15)
<a name="local_and_external_parameter_names"></a>
### 参数的内部名称和外部名称
跟函数和方法参数相同,构造参数也存在一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字。
跟函数和方法参数相同,构造参数也拥有一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字。
然而,构造器并不像函数和方法那样在括号前有一个可辨别的名字。所以在调用构造器时,主要通过构造器中的参数名和类型来确定需要调用的构造器。正因为参数如此重要如果你在定义构造器时没有提供参数的外部名字Swift 会为每个构造器的参数自动生成一个跟内部名字相同的外部名,就相当于在每个构造参数之前加了一个哈希符号
然而,构造器并不像函数和方法那样在括号前有一个可辨别的名字。因此在调用构造器时,主要通过构造器中的参数名和类型来确定应该被调用的构造器。正因为参数如此重要如果你在定义构造器时没有提供参数的外部名字Swift 会为每个构造器的参数自动生成一个跟内部名字相同的外部名。
以下例子中定义了一个结构体`Color`,它包含了三个常量:`red``green``blue`。这些属性可以存储0.01.0之间的值,用来指示颜色中红、绿、蓝成分的含量。
以下例子中定义了一个结构体`Color`,它包含了三个常量:`red``green``blue`。这些属性可以存储`0.0``1.0`之间的值,用来指示颜色中红、绿、蓝成分的含量。
`Color`提供了一个构造器,其中包含三个`Double`类型的构造参数。`Color`也可以提供第二个构造器,它只包含`Double`类型名叫`white`的参数,它被用于给上述三个构造参数赋予同样的值。
`Color`提供了一个构造器,其中包含三个`Double`类型的构造参数。`Color`也可以提供第二个构造器,它只包含名为`white``Double`类型的参数,它被用于给上述三个构造参数赋予同样的值。
```swift
struct Color {
@ -140,7 +140,7 @@ struct Color {
}
```
两种构造器都能用于创建一个新的`Color`实例,你需要为构造器每个外部参数传值
两种构造器都能用于创建一个新的`Color`实例,你需要为构造器每个外部参数传值
```swift
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
@ -157,13 +157,13 @@ let veryGreen = Color(0.0, 1.0, 0.0)
<a name="initializer_parameters_without_external_names"></a>
### 不带外部名的构造器参数
如果你不希望为构造器的某个参数提供外部名字,你可以使用下划线(_)来显描述它的外部名,以此重写上面所说的默认行为。
如果你不希望为构造器的某个参数提供外部名字,你可以使用下划线(`_`)来显描述它的外部名,以此重写上面所说的默认行为。
下面是之前`Celsius`例子的扩展,跟之前相比添加了一个带有`Double`类型参数名为`celsius`的构造器,其外部名用`_`代替
下面是之前`Celsius`例子的扩展,跟之前相比添加了一个带有`Double`类型参数的构造器,其外部名用`_`代替
```swift
struct Celsius {I
var temperatureInCelsius: Double = 0.0
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
@ -178,12 +178,12 @@ let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius 为 37.0
```
调用这种不需要外部参数名称的`Celsius(37.0)`构造器看起来十分简明的。因此适使用这种`init(_ celsius: Double)`构造器可以提供`Double`类型的参数值而不需要加上外部名。
调用`Celsius(37.0)`意图明确,不需要外部参数名称。因此适使用`init(_ celsius: Double)`这样的构造器,从而可以通过提供`Double`类型的参数值调用构造器,而不需要加上外部名。
<a name="optional_property_types"></a>
### 可选属性类型
如果你定制的类型包含一个逻辑上允许取值为空的存储型属性--不管是因为它无法在初始化时赋值,还是因为它可以在之后某个时间点可以赋值为空--你都需要将它定义为可选类型`optional type`。可选类型的属性将自动初始化为`nil`,表示这个属性是意在初始化时设置为空的。
如果你定制的类型包含一个逻辑上允许取值为空的存储型属性——无论是因为它无法在初始化时赋值,还是因为它在之后某个时间点可以赋值为空——你都需要将它定义为可选类型`optional type`。可选类型的属性将自动初始化为`nil`,表示这个属性是意在初始化时设置为空的。
下面例子中定义了类`SurveyQuestion`,它包含一个可选字符串属性`response`
@ -204,17 +204,17 @@ cheeseQuestion.ask()
cheeseQuestion.response = "Yes, I do like cheese."
```
调查问题在问题提出之后,我们才能得到回答。所以我们将属性回答`response`声明为`String?`类型,或者说是可选字符串类型`optional String`。当`SurveyQuestion`实例化时,它将自动赋值为`nil`,表明暂时还不存在此字符串。
调查问题的答案在回答前是无法确定的,因此我们将属性`response`声明为`String?`类型,或者说是可选字符串类型`optional String`。当`SurveyQuestion`实例化时,它将自动赋值为`nil`,表明此字符串暂时还没有值
<a name="assigning_constant_properties_during_initialization"></a>
### 构造过程中常量属性的修改
你可以在构造过程中的任意时间点修改常量属性的值,只要在构造过程结束时是一个确定的值。一旦常量属性被赋值,它将永远不可更改。
>注意
> 注意
对于类的实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。
你可以修改上面的`SurveyQuestion`示例,用常量属性替代变量属性`text`,表示问题内容`text``SurveyQuestion`的实例被创建之后不会再被修改。尽管`text`属性现在是常量,我们仍然可以在类的构造器中设置它的值:
你可以修改上面的`SurveyQuestion`示例,用常量属性替代变量属性`text`,表示问题内容`text``SurveyQuestion`的实例被创建之后不会再被修改。尽管`text`属性现在是常量,我们仍然可以在类的构造器中设置它的值:
```swift
class SurveyQuestion {
@ -236,9 +236,9 @@ beetsQuestion.response = "I also like beets. (But not with cheese.)"
<a name="default_initializers"></a>
## 默认构造器
如果结构体类的所有属性都有默认值,同时没有自定义的构造器,那么 Swift 会给这些结构体和类创建一个默认构造器。这个默认构造器将简单创建一个所有属性值都设置为默认值的实例。
如果结构体类的所有属性都有默认值,同时没有自定义的构造器,那么 Swift 会给这些结构体或类提供一个默认构造器。这个默认构造器将简单创建一个所有属性值都设置为默认值的实例。
下面例子中创建了一个类`ShoppingListItem`,它封装了购物清单中的某一的属性:名字(`name`)、数量(`quantity`)和购买状态 `purchase state`
下面例子中创建了一个类`ShoppingListItem`,它封装了购物清单中的某一物品的属性:名字(`name`)、数量(`quantity`)和购买状态 `purchase state`
```swift
class ShoppingListItem {
@ -254,13 +254,13 @@ var item = ShoppingListItem()
<a name="memberwise_initializers_for_structure_types"></a>
### 结构体的逐一成员构造器
除上面提到的默认构造器,如果结构体对所有存储型属性提供了默认值且自身没有提供定制的构造器,它们自动获得一个逐一成员构造器。
上面提到的默认构造器,如果结构体没有提供自定义的构造器,它们自动获得一个逐一成员构造器,即使结构体的存储型属性没有默认值
逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法。我们在调用逐一成员构造器时,通过与成员属性名相同的参数名进行传值来完成对成员属性的初始赋值。
下面例子中定义了一个结构体`Size`,它包含两个属性`width``height`。Swift 可以根据这两个属性的初始赋值`0.0`自动推导出它们的类型`Double`
下面例子中定义了一个结构体`Size`,它包含两个属性`width``height`。Swift 可以根据这两个属性的初始赋值`0.0`自动推导出它们的类型`Double`
由于这两个存储型属性都有默认值,结构体`Size`自动获得了一个逐一成员构造器 `init(width:height:)` 你可以用它来为`Size`创建新的实例:
由于这两个存储型属性都有默认值,结构体`Size`自动获得了一个逐一成员构造器`init(width:height:)`。你可以用它来为`Size`创建新的实例:
```swift
struct Size {
@ -274,14 +274,14 @@ let twoByTwo = Size(width: 2.0, height: 2.0)
构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。
构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给本身提供的其它构造器。类则不同,它可以继承自其它类(请参考[继承](./13_Inheritance.html)),这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节[类的继承和构造过程](#class_inheritance_and_initialization)中介绍。
构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给提供给它的构造器。类则不同,它可以继承自其它类(请参考[继承](./13_Inheritance.html)),这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节[类的继承和构造过程](#class_inheritance_and_initialization)中介绍。
对于值类型,你可以使用`self.init`在自定义的构造器中引用其它的属于相同值类型的构造器。并且你只能在构造器内部调用`self.init`
对于值类型,你可以使用`self.init`在自定义的构造器中引用类型中的其它构造器。并且你只能在构造器内部调用`self.init`
如果你为某个值类型定义了一个定制的构造器,你将无法访问到默认构造器(如果是结构体,无法访问逐一对象构造器)。这个限制可以防止你为值类型定义了一个更复杂的,完成了重要准备构造器之后,别人还是错误使用了个自动生成的构造器。
如果你为某个值类型定义了一个自定义的构造器,你将无法访问到默认构造器(如果是结构体,还将无法访问逐一成员构造器)。这个限制可以防止你为值类型定义了一个进行额外必要设置的复杂构造器之后,别人还是错误使用了个自动生成的构造器。
>注意
假如你想通过默认构造器、逐一对象构造器以及你自己定制的构造器为值类型创建实例,我们建议你将自己定制的构造器写到扩展(`extension`)中,而不是值类型定义混在一起。想查看更多内容,请查看[扩展](./21_Extensions.html)章节。
> 注意
假如你希望默认构造器、逐一成员构造器以及你自己的自定义构造器都能用来创建实例,可以将自定义的构造器写到扩展(`extension`)中,而不是写在值类型的原始定义中。想查看更多内容,请查看[扩展](./21_Extensions.html)章节。
下面例子将定义一个结构体`Rect`,用来代表几何矩形。这个例子需要两个辅助的结构体`Size``Point`,它们各自为其所有的属性提供了初始值`0.0`
@ -294,7 +294,7 @@ struct Point {
}
```
你可以通过以下三种方式为`Rect`创建实例--使用默认的0值来初始化`origin``size`属性;使用特定的`origin``size`实例来初始化;使用特定的`center``size`来初始化。在下面`Rect`结构体定义中,我们为这三种方式提供了三个自定义的构造器:
你可以通过以下三种方式为`Rect`创建实例——使用被初始化为默认值的`origin``size`属性来初始化;提供指定的`origin``size`实例来初始化;提供指定的`center``size`来初始化。在下面`Rect`结构体定义中,我们为这三种方式提供了三个自定义的构造器:
```swift
struct Rect {
@ -313,11 +313,11 @@ struct Rect {
}
```
第一个`Rect`构造器`init()`,在功能上跟没有自定义构造器时自动获得的默认构造器是一样的。这个构造器是一个空函数,使用一对大括号`{}`描述,它没有执行任何定制的构造过程。调用这个构造器将返回一个`Rect`实例,它的`origin``size`属性都使用定义时的默认值`Point(x: 0.0, y: 0.0)``Size(width: 0.0, height: 0.0)`
第一个`Rect`构造器`init()`,在功能上跟没有自定义构造器时自动获得的默认构造器是一样的。这个构造器是一个空函数,使用一对大括号`{}`表示,它没有执行任何构造过程。调用这个构造器将返回一个`Rect`实例,它的`origin``size`属性都使用定义时的默认值`Point(x: 0.0, y: 0.0)``Size(width: 0.0, height: 0.0)`
```swift
let basicRect = Rect()
// basicRect 的原点是 (0.0, 0.0)尺寸是 (0.0, 0.0)
// basicRect 的 origin 是 (0.0, 0.0)size 是 (0.0, 0.0)
```
第二个`Rect`构造器`init(origin:size:)`,在功能上跟结构体在没有自定义构造器时获得的逐一成员构造器是一样的。这个构造器只是简单地将`origin``size`的参数值赋给对应的存储型属性:
@ -325,20 +325,20 @@ let basicRect = Rect()
```swift
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
// originRect 的原点是 (2.0, 2.0)尺寸是 (5.0, 5.0)
// originRect 的 origin 是 (2.0, 2.0)size 是 (5.0, 5.0)
```
第三个`Rect`构造器`init(center:size:)`稍微复杂一点。它先通过`center``size`的值计算出`origin`的坐标然后再调用(或代理给)`init(origin:size:)`构造器来将新的`origin``size`值赋值到对应的属性中:
第三个`Rect`构造器`init(center:size:)`稍微复杂一点。它先通过`center``size`的值计算出`origin`的坐标然后再调用(或者说代理给)`init(origin:size:)`构造器来将新的`origin``size`值赋值到对应的属性中:
```swift
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
// centerRect 的原点是 (2.5, 2.5)尺寸是 (3.0, 3.0)
// centerRect 的 origin 是 (2.5, 2.5)size 是 (3.0, 3.0)
```
构造器`init(center:size:)`可以自己`origin``size`的新值赋值到对应的属性中。然而尽量利用现有构造器和它所提供的功能来实现`init(center:size:)`功能,是更方便、更清晰和更直观的方法
构造器`init(center:size:)`可以直接`origin``size`的新值赋值到对应的属性中。然而,利用恰好提供了相关功能的现有构造器会更为方便,构造器`init(center:size:)`意图也会更加清晰
>注意
> 注意
如果你想用另外一种不需要自己定义`init()``init(origin:size:)`的方式来实现这个例子,请参考[扩展](./21_Extensions.html)。
<a name="class_inheritance_and_initialization"></a>