@ -8,7 +8,7 @@
|
||||
> 2.0
|
||||
> 翻译+校对:[shanks](http://codebuild.me)
|
||||
|
||||
> 2.0,2.1
|
||||
> 2.1
|
||||
> 翻译+校对:[shanks](http://codebuild.me),[Realank](https://github.com/Realank)
|
||||
|
||||
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
> 翻译+校对:[chenmingbiao](https://github.com/chenmingbiao)
|
||||
|
||||
> 2.1
|
||||
> 翻译:[Channe](https://github.com/Channe)
|
||||
> 校对:[shanks](http://codebuild.me),2015-10-30
|
||||
> 翻译:[Channe](https://github.com/Channe),[Realank](https://github.com/Realank)
|
||||
> 校对:[shanks](http://codebuild.me),2016-1-23
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -118,7 +118,7 @@ let freezingPointOfWater = Celsius(fromKelvin: 273.15)
|
||||
|
||||
跟函数和方法参数相同,构造参数也拥有一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字。
|
||||
|
||||
然而,构造器并不像函数和方法那样在括号前有一个可辨别的名字。因此在调用构造器时,主要通过构造器中的参数名和类型来确定应该被调用的构造器。正因为参数如此重要,如果你在定义构造器时没有提供参数的外部名字,Swift 会为每个构造器的参数自动生成一个跟内部名字相同的外部名。
|
||||
然而,构造器并不像函数和方法那样在括号前有一个可辨别的名字。因此在调用构造器时,主要通过构造器中的参数名和类型来确定应该被调用的构造器。正因为参数如此重要,如果你在定义构造器时没有提供参数的外部名字,Swift 会为构造器的每个参数自动生成一个跟内部名字相同的外部名。
|
||||
|
||||
以下例子中定义了一个结构体`Color`,它包含了三个常量:`red`、`green`和`blue`。这些属性可以存储`0.0`到`1.0`之间的值,用来指示颜色中红、绿、蓝成分的含量。
|
||||
|
||||
@ -183,7 +183,7 @@ let bodyTemperature = Celsius(37.0)
|
||||
<a name="optional_property_types"></a>
|
||||
### 可选属性类型
|
||||
|
||||
如果你定制的类型包含一个逻辑上允许取值为空的存储型属性——无论是因为它无法在初始化时赋值,还是因为它在之后某个时间点可以赋值为空——你都需要将它定义为可选类型`optional type`。可选类型的属性将自动初始化为`nil`,表示这个属性是有意在初始化时设置为空的。
|
||||
如果你定制的类型包含一个逻辑上允许取值为空的存储型属性——无论是因为它无法在初始化时赋值,还是因为它在之后某个时间点可以赋值为空——你都需要将它定义为`可选类型`(optional type)。可选类型的属性将自动初始化为`nil`,表示这个属性是有意在初始化时设置为空的。
|
||||
|
||||
下面例子中定义了类`SurveyQuestion`,它包含一个可选字符串属性`response`:
|
||||
|
||||
@ -204,12 +204,12 @@ 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>
|
||||
### 构造过程中常量属性的修改
|
||||
|
||||
你可以在构造过程中的任意时间点修改常量属性的值,只要在构造过程结束时是一个确定的值。一旦常量属性被赋值,它将永远不可更改。
|
||||
你可以在构造过程中的任意时间点给常量属性指定一个值,只要在构造过程结束时是一个确定的值。一旦常量属性被赋值,它将永远不可更改。
|
||||
|
||||
> 注意
|
||||
对于类的实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。
|
||||
@ -236,7 +236,7 @@ beetsQuestion.response = "I also like beets. (But not with cheese.)"
|
||||
<a name="default_initializers"></a>
|
||||
## 默认构造器
|
||||
|
||||
如果结构体或类的所有属性都有默认值,同时没有自定义的构造器,那么 Swift 会给这些结构体或类提供一个默认构造器。这个默认构造器将简单地创建一个所有属性值都设置为默认值的实例。
|
||||
如果结构体或类的所有属性都有默认值,同时没有自定义的构造器,那么 Swift 会给这些结构体或类提供一个默认构造器(default initializers)。这个默认构造器将简单地创建一个所有属性值都设置为默认值的实例。
|
||||
|
||||
下面例子中创建了一个类`ShoppingListItem`,它封装了购物清单中的某一物品的属性:名字(`name`)、数量(`quantity`)和购买状态 `purchase state`:
|
||||
|
||||
@ -260,7 +260,7 @@ var item = ShoppingListItem()
|
||||
|
||||
下面例子中定义了一个结构体`Size`,它包含两个属性`width`和`height`。Swift 可以根据这两个属性的初始赋值`0.0`自动推导出它们的类型为`Double`。
|
||||
|
||||
由于这两个存储型属性都有默认值,结构体`Size`自动获得了一个逐一成员构造器`init(width:height:)`。你可以用它来为`Size`创建新的实例:
|
||||
结构体`Size`自动获得了一个逐一成员构造器`init(width:height:)`。你可以用它来为`Size`创建新的实例:
|
||||
|
||||
```swift
|
||||
struct Size {
|
||||
@ -270,11 +270,12 @@ let twoByTwo = Size(width: 2.0, height: 2.0)
|
||||
```
|
||||
|
||||
<a name="initializer_delegation_for_value_types"></a>
|
||||
|
||||
## 值类型的构造器代理
|
||||
|
||||
构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。
|
||||
|
||||
构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给提供给它的构造器。类则不同,它可以继承自其它类(请参考[继承](./13_Inheritance.html)),这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节[类的继承和构造过程](#class_inheritance_and_initialization)中介绍。
|
||||
构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给自己的其它构造器。类则不同,它可以继承自其它类(请参考[继承](./13_Inheritance.html)),这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节[类的继承和构造过程](#class_inheritance_and_initialization)中介绍。
|
||||
|
||||
对于值类型,你可以使用`self.init`在自定义的构造器中引用类型中的其它构造器。并且你只能在构造器内部调用`self.init`。
|
||||
|
||||
@ -351,11 +352,11 @@ Swift 为类类型提供了两种构造器来确保实例中所有存储型属
|
||||
<a name="designated_initializers_and_convenience_initializers"></a>
|
||||
### 指定构造器和便利构造器
|
||||
|
||||
*指定构造器*是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。
|
||||
*指定构造器*(designated initializers)是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。
|
||||
|
||||
每一个类都必须拥有至少一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。具体内容请参考后续章节[构造器的自动继承](#automatic_initializer_inheritance)。
|
||||
|
||||
*便利构造器*是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入值的实例。
|
||||
*便利构造器*(convenience initializers)是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入值的实例。
|
||||
|
||||
你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清晰明了。
|
||||
|
||||
@ -415,14 +416,14 @@ convenience init(parameters) {
|
||||
<a name="two_phase_initialization"></a>
|
||||
### 两段式构造过程
|
||||
|
||||
Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性通过引入它们的类的构造器来设置初始值。当每一个存储型属性值被确定后,第二阶段开始,它给每个类一次机会在新实例准备使用之前进一步定制它们的存储型属性。
|
||||
Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性被引入它们的类指定一个初始值。当每个存储型属性的初始值被确定后,第二阶段开始,它给每个类一次机会,在新实例准备使用之前进一步定制它们的存储型属性。
|
||||
|
||||
两段式构造过程的使用让构造过程更安全,同时在整个类层级结构中给予了每个类完全的灵活性。两段式构造过程可以防止属性值在初始化之前被访问,也可以防止属性被另外一个构造器意外地赋予不同的值。
|
||||
|
||||
> 注意
|
||||
Swift 的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1,Objective-C 给每一个属性赋值`0`或空值(比如说`0`或`nil`)。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以`0`或`nil`作为合法默认值的情况。
|
||||
|
||||
Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能顺利完成:
|
||||
Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能不出错地完成:
|
||||
|
||||
##### 安全检查 1
|
||||
|
||||
@ -559,7 +560,7 @@ print("Bicycle: \(bicycle.description)")
|
||||
|
||||
##### 规则 2
|
||||
|
||||
如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承所有父类的便利构造器。(即使属性没有默认值,只要实现了父类的所有指定构造器,就会自动继承父类的所有便利构造器)
|
||||
如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承所有父类的便利构造器。
|
||||
|
||||
即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。
|
||||
|
||||
@ -691,7 +692,7 @@ for item in breakfastList {
|
||||
|
||||
如果一个类、结构体或枚举类型的对象,在构造过程中有可能失败,则为其定义一个可失败构造器。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。
|
||||
|
||||
为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在`init`关键字后面加添问号`(init?)`。
|
||||
为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在`init`关键字后面添加问号(`init?`)。
|
||||
|
||||
> 注意
|
||||
可失败构造器的参数名和参数类型,不能与其它非可失败构造器的参数名,及其参数类型相同。
|
||||
@ -833,6 +834,8 @@ class Product {
|
||||
|
||||
> 译者注
|
||||
> 上面的示例代码和描述并不相符,根据描述,`if name.isEmpty { return nil }`这句代码应该在`self.name = name`之前,而这却会导致编译错误`error: all stored properties of a class instance must be initialized before returning nil from an initializer`,除非将`let name: String!`改为`var name: String!`。
|
||||
>
|
||||
> 根据前面的介绍和实测,常量存储属性,只能被初始化一次。上面的代码,在构造器中有初始化常量属性`name`的过程,所以在此之前不会被赋予默认值`nil`,而在构造器的初始化`name`过程之前,因为并不是所有存储型属性都被初始化,是不可以返回`nil`的;而如果将`name`声明为变量存储属性,那么就可以被多次赋值,所以在执行构造器方法之前,就会获得默认值`nil`,和构造器中的再次赋值并不冲突,所以此时,`if name.isEmpty { return nil }`才可以放在构造器前面。英文原文在这里表述有误
|
||||
|
||||
因为`name`属性是一个常量,所以一旦构造成功,`name`属性肯定有一个非`nil`的值。即使它被定义为隐式解包可选类型,也完全可以放心大胆地直接访问,而不用检查`name`属性的值是否为`nil`:
|
||||
|
||||
@ -874,7 +877,7 @@ class CartItem: Product {
|
||||
如果由于`name`的值为空字符串而导致父类的可失败构造器构造失败,则`CartIem`类的整个构造过程都将立即失败,之后的构造代码将不会再被执行。如果父类构造成功,`CartIem`的可失败构造器会进一步验证`quantity`的值是否不小于`1`。
|
||||
|
||||
> 译者注
|
||||
> 上面的示例代码和描述也不相符,根据描述,`self.quantity = quantity`这句代码应该放在最后一行,而这却会导致编译错误`error: property 'self.quantity' not initialized at super.init call`,除非将`let quantity: Int!`改为`var quantity: Int!`。
|
||||
> 上面的示例代码和描述也不相符,`name`属性在被赋予特定的值之前不能获得一个默认的初始值`nil`,并且根据描述,`self.quantity = quantity`这句代码应该放在最后一行,而这却会导致编译错误`error: property 'self.quantity' not initialized at super.init call`,除非将`let quantity: Int!`改为`var quantity: Int!`。
|
||||
|
||||
如果你构造一个`name`的值为非空字符串,`quantity`的值不小于`1`的`CartItem`实例,则可成功构造:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user