Swift 2.0 2015.09.16版

This commit is contained in:
Channe
2015-10-14 21:38:24 +08:00
parent a8dfe88635
commit 19c491cdfa
2 changed files with 55 additions and 38 deletions

View File

@ -119,15 +119,15 @@ default:
<a name="associated_values"></a>
## 相关值Associated Values
上一小节的例子演示了如何定义(分类)枚举的成员。你可以为`Planet.Earth`设置一个常量或者变量,并且在赋值之后查看这个值。不管怎样,如果有时候能够把其他类型的*相关值*和成员值一起存储起来会很有用。这能让你存储成员值之外的自定义信息,并且当你每次在代码中使用该成员时允许这个信息产生变化。
上一小节的例子演示了如何定义(分类)枚举的成员。你可以为`Planet.Earth`设置一个常量或者变量,并且在赋值之后查看这个值。不管怎样,如果有时候能够把其他类型的**相关值**和成员值一起存储起来会很有用。这能让你存储成员值之外的自定义信息,并且当你每次在代码中使用该成员时允许这个信息产生变化。
你可以定义 Swift 的枚举存储任何类型的相关值如果需要的话每个成员的数据类型可以是各不相同的。枚举的这种特性跟其他语言中的可辨识联合discriminated unions标签联合tagged unions或者变体variants相似。
例如,假设一个库存跟踪系统需要利用两种不同类型的条形码来跟踪商品。有些商品上标有 UPC-A 格式的一维t条形码,它使用数字 0 到 9。每一个条形码都有一个代表“数字系统”的数字该数字后接 5 个代表“生产代码”的数字接下来是5位“产品代码”。最后一个数字是“检查”位用来验证代码是否被正确扫描
例如,假设一个库存跟踪系统需要利用两种不同类型的条形码来跟踪商品。有些商品上标有 UPC-A 格式的一维条形码,它使用数字 0 到 9。每一个条形码都有一个代表“数字系统”的数字该数字后接 5 个代表“生产代码”的数字接下来是5位“产品代码”。最后一个数字是“检查”位用来验证代码是否被正确扫描
<img width="252" height="120" alt="" src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/barcode_UPC_2x.png">
其他商品上标有 QR 码格式的二维码,它可以使用任何 ISO8859-1 字符,并且可以编码一个最多拥有 2,953 字符的字符串:
其他商品上标有 QR 码格式的二维码,它可以使用任何 ISO 8859-1 字符,并且可以编码一个最多拥有 2953 字符的字符串:
<img width="169" height="169" alt="" src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/barcode_QR_2x.png">
@ -248,11 +248,10 @@ let earthsOrder = Planet.Earth.rawValue
let sunsetDirection = CompassPoint.West.rawValue
// sunsetDirection 值为 "West"
```
### 使用原始值来初始化(Initializing from a Raw Value)
### 使用原始值初始化枚举变量Initializing from a Raw Value
如果在定义枚举类型的时候使用了原始值,那么将会自动获得一个初始化方法,这个方法将原始值类型作为参数,返回枚举成员或`nil`。你可以使用这种初始化方法来创建一个新的枚举变量。
如果在定义枚举类型的时候使用了原始值,那么将会自动获得一个初始化方法,这个方法将原始值类型作为参数,返回值是枚举成员或`nil`。你可以使用这种初始化方法来创建一个新的枚举变量。
这个例子通过原始值`7`从而创建枚举成员:
@ -261,9 +260,9 @@ let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet 类型为 Planet? 值为 Planet.Uranus
```
然而,并非所有可能的`Int`值都可以找到一个匹配的行星。正因为如此,构造函数可以返回一个*可选*的枚举成员。在上面的例子中,`possiblePlanet``Planet?`类型,或“可选的`Planet`”。
然而,并非所有可能的`Int`值都可以找到一个匹配的行星。正因为如此,构造函数可以返回一个**可选**的枚举成员。在上面的例子中,`possiblePlanet``Planet?`类型,或“可选的`Planet`”。
>注意:
>注意:
>原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见[可失败构造器](../chapter3/05_Declarations#failable_initializers)
如果你试图寻找一个位置为9的行星通过参数为`rawValue`构造函数返回的可选`Planet`值将是`nil`
@ -288,9 +287,9 @@ if let somePlanet = Planet(rawValue: positionToFind) {
<a name="recursive_enumerations"></a>
## 递归枚举Recursive Enumerations
在对操作符进行描述的时候,使用枚举类型来对数据建模很方便,因为需要考虑的情况固定可枚举。操作符可以将两个由数字组成的算数表达式连接起来,例如,将`5`连接成复杂一些的表达式`5+4`.
在对操作符进行描述的时候,使用枚举类型来对数据建模很方便,因为需要考虑的情况固定可枚举。操作符可以将两个由数字组成的算数表达式连接起来,例如,将`5`连接成复杂一些的表达式`5+4`
表达式的一个重要特性是,表达式可以嵌套使用。例如,表达式`(5 + 4) * 2`乘号右边是一个数字,左边则是另一个表达式。因为数据是嵌套的,因而用来存储数据的枚举类型也许要支持这种嵌套————这表示枚举类型需要支持递归。
表达式的一个重要特性是,表达式可以嵌套使用。例如,表达式`(5 + 4) * 2`乘号右边是一个数字,左边则是另一个表达式。因为数据是嵌套的,因而用来存储数据的枚举类型也许要支持这种嵌套————这表示枚举类型需要支持递归。
`递归枚举recursive enumeration`是一种枚举类型,表示它的枚举中,有一个或多个枚举成员拥有该枚举的其他成员作为相关值。使用递归枚举时,编译器会插入一个中间层。你可以在枚举成员前加上`indirect`来表示这成员可递归。
@ -317,7 +316,7 @@ indirect enum ArithmeticExpression {
上面定义的枚举类型可以存储三种算数表达式:纯数字、两个表达式的相加、两个表达式相乘。`Addition``Multiplication`成员的相关值也是算数表达式————这些相关值使得嵌套表达式成为可能。
递归函数可以很直观地使用具有递归性质的数据结构。例如,下面是一个计算算表达式的函数:
递归函数可以很直观地使用具有递归性质的数据结构。例如,下面是一个计算算表达式的函数:
```swift
func evaluate(expression: ArithmeticExpression) -> Int {
@ -340,4 +339,4 @@ print(evaluate(product))
// 输出 "18"
```
该函数如果遇到纯数字,就直接返回该数字的值。如果遇到的是加法或乘法算,则分别计算左边表达式和右边表达式的值,然后相加或相乘。
该函数如果遇到纯数字,就直接返回该数字的值。如果遇到的是加法或乘法算,则分别计算左边表达式和右边表达式的值,然后相加或相乘。

View File

@ -20,21 +20,21 @@
- [通过闭包和函数来设置属性的默认值](#setting_a_default_property_value_with_a_closure_or_function)
构造过程是为了使用某个类、结构体或枚举类型的实例而进行的准备过程。这个过程包含了为实例中每个存储型属性设置初始值和为其执行必要的准备和初始化任务
构造过程是使用类、结构体或枚举类型一个实例的准备过程。在新实例可用前必须执行这个过程,具体操作包括设置实例中每个存储型属性初始值和执行其他必须的设置或初始化工作
构造过程是通过定义构造器(`Initializers`)来实现,这些构造器可以看做是用来创建特定类型实例的特殊方法。与 Objective-C 中的构造器不同Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。
通过定义构造器(`Initializers`)来实现构造过程,这些构造器可以看做是用来创建特定类型实例的特殊方法。与 Objective-C 中的构造器不同Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。
类的实例也可以通过定义析构器(`deinitializer`)在实例释放之前执行特定的清除工作。想了解更多关于析构器的内容,请参考[析构过程](./15_Deinitialization.html)。
<a name="setting_initial_values_for_stored_properties"></a>
## 存储型属性的初始
## 设置存储型属性的初始值
类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。
类和结构体在创建实例时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。
你可以在构造器中为存储型属性赋初值,也可以在定义属性时为其设置默认值。以下节将详细介绍这两种方法。
你可以在构造器中为存储型属性赋初值,也可以在定义属性时为其设置默认值。以下节将详细介绍这两种方法。
>注意:
当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观测器`property observers`)。
>注意:
当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观察者`property observers`)。
### 构造器
@ -67,7 +67,7 @@ print("The default temperature is \(f.temperature)° Fahrenheit")
如前所述,你可以在构造器中为存储型属性设置初始值。同样,你也可以在属性声明时为其设置默认值。
>注意:
如果一个属性总是使用同一个初始值,可以为其设置一个默认值。无论定义默认值还是在构造器中赋值,最终它们实现的效果是一样的,只不过默认值属性的初始化和属性的声明结合的更紧密。使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型;同时,它也能让你充分利用默认构造器、构造器继承(后续章节将讲到)等特性
如果一个属性总是使用相同的初始值,那么为其设置一个默认值比每次都在构造器中赋值要好。两种方法的效果是一样的,只不过使用默认值属性的初始化和声明结合的更紧密。使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型;同时,它也能让你充分利用默认构造器、构造器继承等特性(后续章节将讲到)。
你可以使用更简单的方式在定义结构体`Fahrenheit`时为属性`temperature`设置默认值:
@ -80,17 +80,17 @@ struct Fahrenheit {
<a name="customizing_initialization"></a>
## 自定义构造过程
你可以通过输入参数和可选属性类型来定义构造过程,也可以在构造过程中修改常量属性。这些都将在后面章节中提到。
你可以通过输入参数和可选属性类型来定义构造过程,也可以在构造过程中修改常量属性。这些都将在后面章节中提到。
### 构造参数
你可以在定义构造器时提供构造参数,为其提供自定义构造所需值的类型和名字。构造参数的功能和语法跟函数和方法参数相同。
自定义`构造过程`时,可以在定义中提供构造参数,指定所需值的类型和名字。构造参数的功能和语法跟函数和方法参数相同。
下面例子中定义了一个包含摄氏度温度的结构体`Celsius`。它定义了两个不同的构造器:`init(fromFahrenheit:)``init(fromKelvin:)`,二者分别通过接受不同刻度表示的温度值来创建新的实例:
```swift
struct Celsius {
var temperatureInCelsius: Double = 0.0
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
@ -199,12 +199,12 @@ cheeseQuestion.response = "Yes, I do like cheese."
<a name="assigning_constant_properties_during_initialization"></a>
### 构造过程中常量属性的修改
只要在构造过程结束前常量的值能确定,你可以在构造过程中的任意时间点修改常量属性的值。
你可以在构造过程中的任意时间点修改常量属性的值,只要在构造过程结束时是一个确定的值。一旦常量属性被赋值,它将永远不可更改
>注意:
某个类实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。
于类的实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。
你可以修改上面的`SurveyQuestion`示例,用常量属性替代变量属性`text`指明问题内容`text`创建之后不会再被修改。尽管`text`属性现在是常量,我们仍然可以在其类的构造器中设置它的值:
你可以修改上面的`SurveyQuestion`示例,用常量属性替代变量属性`text`表示问题内容`text``SurveyQuestion`的实例被创建之后不会再被修改。尽管`text`属性现在是常量,我们仍然可以在其类的构造器中设置它的值:
```swift
class SurveyQuestion {
@ -226,7 +226,7 @@ beetsQuestion.response = "I also like beets. (But not with cheese.)"
<a name="default_initializers"></a>
## 默认构造器
Swift 将为所有属性已提供默认值的且自身没有定义任何构造器的结构体或基类,提供一个默认构造器。这个默认构造器将简单的创建一个所有属性值都设置为默认值的实例。
如果结构体和类的所有属性都有默认值,同时没有定义构造器,那么 Swift 会给这些结构体和类创建一个默认构造器。这个默认构造器将简单的创建一个所有属性值都设置为默认值的实例。
下面例子中创建了一个类`ShoppingListItem`,它封装了购物清单中的某一项的属性:名字(`name`)、数量(`quantity`)和购买状态 `purchase state`
@ -635,7 +635,7 @@ let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name.lowercaseString)"
var output = "\(quantity) x \(name)"
output += purchased ? " ✔" : " ✘"
return output
}
@ -674,7 +674,7 @@ for item in breakfastList {
<a name="failable_initializers"></a>
## 可失败构造器
如果一个类结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败构造器,是非常有必要的。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。
如果一个类结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败构造器,是非常有的。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。
为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在`init`关键字后面加添问号`(init?)`
@ -723,11 +723,11 @@ if anonymousCreature == nil {
```
> 注意:
空字符串(`""`)和一个值为`nil`的可选类型的字符串是两个完全不同的概念。上例中的空字符串(`""`)其实是一个有效的,非可选类型的字符串。这里我们只所以让`Animal`的可失败构造器,构建对象失败,只是因为对于`Animal`这个类的`species`属性来说,它更适合有一个具体的值,而不是空字符串。
空字符串(`""`,而不是`"Giraffe"`)和一个值为`nil`的可选类型的字符串是两个完全不同的概念。上例中的空字符串(`""`)其实是一个有效的,非可选类型的字符串。这里我们只所以让`Animal`的可失败构造器,构建对象失败,只是因为对于`Animal`这个类的`species`属性来说,它更适合有一个具体的值,而不是空字符串。
###枚举类型的可失败构造器
你可以通过构造一个带一个或多个参数的可失败构造器来获取枚举类型中特定的枚举成员。还能在参数不满足你所期望的条件时,导致构造失败。
你可以通过构造一个带一个或多个参数的可失败构造器来获取枚举类型中特定的枚举成员。还能在参数不满足枚举成员期望的条件时,构造失败。
下例中定义了一个名为TemperatureUnit的枚举类型。其中包含了三个可能的枚举成员(`Kelvin``Celsius`,和 `Fahrenheit`)和一个被用来找到`Character`值所对应的枚举成员的可失败构造器:
@ -791,9 +791,11 @@ if unknownUnit == nil {
###类的可失败构造器
值类型(如结构体或枚举类型)的可失败构造器,对何时何地触发构造失败这个行为没有任何的限制。比如在前面的例子中,结构体`Animal`的可失败构造器触发失败的行为,甚至发生在`species`属性的值被初始化以前。而对类而言,就没有那么幸运了。类的可失败构造器只能在所有的类属性被初始化后和所有类之间的构造器之间的代理调用发生完后触发失败行为。
值类型(如结构体或枚举类型)的可失败构造器,对何时何地触发构造失败这个行为没有任何的限制。比如在前面的例子中,结构体`Animal`的可失败构造器触发失败的行为,甚至发生在`species`属性的值被初始化以前。
下例子中,定义了一个名为`Product`的类,其内部结构和结构体`Animal`很相似,内部也有一个名为`name``String`类型的属性。由于该属性的值同样不能为空字符串,所以我们加入了可失败构造器来确保该类满足上述条件。但由于`Product`类不是一个结构体,所以当想要在该类中添加可失败构造器触发失败条件时,必须确保`name`属性被初始化。因此我们把`name`属性的`String`类型做了一点点小小的修改,把其改为隐式解析可选类型(`String!`),来确保可失败构造器触发失败条件时,所有类属性都被初始化了。因为所有可选类型都有一个默认的初始值`nil`。因此最后`Product`类可写为:
而对类而言,就没有那么幸运了。类的可失败构造器只能在所有的类属性被初始化后和所有类之间的构造器之间的代理调用发生完后触发失败行为。
下面例子展示了如何使用隐式解析可选类型来实现这个类的可失败构造器的要求:
```swift
class Product {
@ -804,8 +806,13 @@ class Product {
}
}
```
上面定义的`Product`类,其内部结构和之前`Animal`结构体很相似。`Product`类有一个不能为空字符串的`name`常量属性。为了强制满足这个要求,`Product`类使用了可失败构造器来确保这个属性的值在构造器成功时不为空。
因为`name`属性是一个常量,所以一旦`Product`类构造成功,`name`属性肯定有一个非`nil`的值。因此完全可以放心大胆的直接访问`Product`类的`name`属性,而不用考虑去检查`name`属性是否有值。
毕竟,`Product`是一个类而不是结构体,也就不能和`Animal`一样了。`Product`类的所有可失败构造器必须在自己失败前给`name`属性一个初始值。
上面的例子中,`Product`类的`name`属性被定义为隐式解析可选字符串类型(`String!`)。因为它是一个可选类型,所以在构造过程里的赋值前,`name`属性有个默认值`nil`。用默认值`nil`意味着`Product`类的所有属性都有一个合法的初始值。因而,在构造器中给`name`属性赋一个特定的值前,可失败构造器能够在传入一个空字符串时触发构造过程的失败。
因为`name`属性是一个常量,所以一旦`Product`类构造成功,`name`属性肯定有一个非`nil`的值。即使它被定义为隐式解析可选类型,也完全可以放心大胆地直接访问,而不用考虑`name`属性是否有值。
```swift
if let bowTie = Product(name: "bow tie") {
@ -897,7 +904,7 @@ class Document {
}
```
下面这个例子,定义了一个名为`AutomaticallyNamedDocument``Document`类的子类。这个子类重写了类的两个指定构造器确保不论在何种情况下`name`属性总是有一个非空字符串`[Untitled]`的值
下面这个例子,定义了一个`Document`类的子类`AutomaticallyNamedDocument`。这个子类重写了类的两个指定构造器确保不论是通过没有 name 参数的构造器,还是通过传一个空字符串给`init(name:)`构造器,生成的实例中的`name`属性总有初始值`"[Untitled]"`
```swift
class AutomaticallyNamedDocument: Document {
@ -916,15 +923,26 @@ class AutomaticallyNamedDocument: Document {
}
```
`AutomaticallyNamedDocument`用一个非可失败构造器`init(name:)`,重写了类的可失败构造器`init?(name:)`。因为子类用不同的方法处理了`name`属性的值为一个空字符串的这种情况。所以子类将不再需要一个可失败的构造器。
`AutomaticallyNamedDocument`用一个非可失败构造器`init(name:)`,重写了类的可失败构造器`init?(name:)`。因为子类用不同的方法处理了`name`属性的值为一个空字符串的这种情况。所以子类将不再需要一个可失败的构造器,用一个非可失败版本代替了父类的版本
你可以在构造器中调用父类的可失败构造器强制解包,以实现子类的非可失败构造器。比如,下面的`UntitledDocument `子类总有值为`"[Untitled]"`的 name 属性,它在构造过程中用了父类的可失败的构造器`init(name:)`
```swift
class UntitledDocument: Document {
override init() {
super.init(name: "[Untitled]")!
}
}
```
在这个例子中,如果在调用父类的构造器`init(name:)`时传给 name 的是空字符串,那么强制解绑操作会造成运行时错误。不过,因为这里是通过字符串常量来调用它,所以并不会发生运行时错误。
###可失败构造器 init!
通常来说我们通过在`init`关键字后添加问号的方式来定义一个可失败构造器,但你也可以使用通过在`init`后面添加惊叹号的方式来定义一个可失败构造器`(init!)`,该可失败构造器将会构建一个特定类型的隐式解析可选类型的对象。
通常来说我们通过在`init`关键字后添加问号的方式`init?`来定义一个可失败构造器,但你也可以使用通过在`init`后面添加惊叹号的方式来定义一个可失败构造器`(init!)`,该可失败构造器将会构建一个特定类型的隐式解析可选类型的对象。
你可以在 `init? `构造器中代理调用 `init!`构造器,反之亦然。
你也可以用 `init?`重写 `init!`,反之亦然。
你还可以用 `init`代理调用`init!`,但这会触发一个断言:是否 `init!` 构造器会触发构造失败?
你还可以用 `init`代理调用`init!`,但这会触发一个断言: `init!` 构造器是否会触发构造失败?
<a name="required_initializers"></a>
##必要构造器
@ -938,7 +956,7 @@ class SomeClass {
}
}
```
子类重写类的必要构造器时,必须在子类的构造器前同样添加`required`修饰符以确保当其它类继承该子类时,该构造器同为必要构造器。在重写类的必要构造器时,不需要添加`override`修饰符:
子类重写类的必要构造器时,必须在子类的构造器前添加`required`修饰符,这是为了保证继承链上子类的构造器也是必要构造器。在重写类的必要构造器时,不需要添加`override`修饰符:
```swift
class SomeSubclass: SomeClass {