finally adjust
This commit is contained in:
@ -1,6 +1,10 @@
|
||||
> 翻译:[numbbbbb](https://github.com/numbbbbb), [lyuka](https://github.com/lyuka), [JaySurplus](https://github.com/JaySurplus), [xtymichael](https://github.com/xtymichael)
|
||||
> 1.0
|
||||
> 翻译:[numbbbbb](https://github.com/numbbbbb), [lyuka](https://github.com/lyuka), [JaySurplus](https://github.com/JaySurplus)
|
||||
> 校对:[lslxdx](https://github.com/lslxdx)
|
||||
|
||||
> 2.0
|
||||
> [xtymichael](https://github.com/xtymichael)
|
||||
|
||||
# 基础部分
|
||||
-----------------
|
||||
|
||||
@ -99,6 +103,12 @@ var welcomeMessage: String
|
||||
welcomeMessage = "Hello"
|
||||
```
|
||||
|
||||
你可以在一行中定义多个同样类型的变量,用逗号分割,并在最后一个变量名之后添加类型标注:
|
||||
|
||||
```swift
|
||||
var red, green, blue: Double
|
||||
```
|
||||
|
||||
> 注意:
|
||||
一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值,Swift可以推断出这个常量或者变量的类型,请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给`welcomeMessage`赋初始值,所以变量`welcomeMessage`的类型是通过一个类型标注指定的,而不是通过初始值推断的。
|
||||
|
||||
@ -165,7 +175,7 @@ print("The current value of friendlyWelcome is \(friendlyWelcome)")
|
||||
```
|
||||
|
||||
> 注意:
|
||||
字符串插值所有可用的选项,请参考[字符串插值](03_Strings_and_Characters.html#string_interpolation)。
|
||||
字符串插值所有可用的选项,请参考[字符串插值](./03_Strings_and_Characters.html#string_interpolation)。
|
||||
|
||||
<a name="comments"></a>
|
||||
## 注释
|
||||
@ -369,7 +379,7 @@ let twoThousandAndOne = twoThousand + UInt16(one)
|
||||
|
||||
现在两个数字的类型都是`UInt16`,可以进行相加。目标常量`twoThousandAndOne`的类型被推断为`UInt16`,因为它是两个`UInt16`值的和。
|
||||
|
||||
`SomeType(ofInitialValue)`是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,`UInt16`有一个构造器,可以接受一个`UInt8`类型的值,所以这个构造器可以用现有的`UInt8`来创建一个新的`UInt16`。注意,你并不能传入任意类型的值,只能传入`UInt16`内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考[扩展](20_Extensions.html)。
|
||||
`SomeType(ofInitialValue)`是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,`UInt16`有一个构造器,可以接受一个`UInt8`类型的值,所以这个构造器可以用现有的`UInt8`来创建一个新的`UInt16`。注意,你并不能传入任意类型的值,只能传入`UInt16`内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考[扩展](./20_Extensions.html)。
|
||||
|
||||
<a name="integer_and_floating_point_conversion"></a>
|
||||
### 整数和浮点数转换
|
||||
@ -440,7 +450,7 @@ if turnipsAreDelicious {
|
||||
// 输出 "Eww, turnips are horrible."
|
||||
```
|
||||
|
||||
条件语句,例如`if`,请参考[控制流](05_Control_Flow.html)。
|
||||
条件语句,例如`if`,请参考[控制流](./05_Control_Flow.html)。
|
||||
|
||||
如果你在需要使用`Bool`类型的地方使用了非布尔值,Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误:
|
||||
|
||||
@ -460,7 +470,7 @@ if i == 1 {
|
||||
}
|
||||
```
|
||||
|
||||
`i == 1`的比较结果是`Bool`类型,所以第二个例子可以通过类型检查。类似`i == 1`这样的比较,请参考[基本操作符](05_Control_Flow.html)。
|
||||
`i == 1`的比较结果是`Bool`类型,所以第二个例子可以通过类型检查。类似`i == 1`这样的比较,请参考[基本操作符](./05_Control_Flow.html)。
|
||||
|
||||
和 Swift 中的其他类型安全的例子一样,这个方法可以避免错误并保证这块代码的意图总是清晰的。
|
||||
|
||||
@ -522,10 +532,10 @@ print("The status message is \(http200Status.description)")
|
||||
// 输出 "The status message is OK"
|
||||
```
|
||||
|
||||
作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个`(Int, String)`元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考[函数参数与返回值](06_Functions.html#Function_Parameters_and_Return_Values)。
|
||||
作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个`(Int, String)`元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考[函数参数与返回值](./06_Functions.html#Function_Parameters_and_Return_Values)。
|
||||
|
||||
> 注意:
|
||||
元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考[类和结构体](09_Classes_and_Structures.html)。
|
||||
元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考[类和结构体](./09_Classes_and_Structures.html)。
|
||||
|
||||
<a name="optionals"></a>
|
||||
## 可选类型
|
||||
@ -608,7 +618,7 @@ if convertedNumber != nil {
|
||||
<a name="optional_binding"></a>
|
||||
### 可选绑定
|
||||
|
||||
使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在`if`和`while`语句中来对可选类型的值进行判断并把值赋给一个常量或者变量。`if`和`while`语句,请参考[控制流](05_Control_Flow.html)。
|
||||
使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在`if`和`while`语句中来对可选类型的值进行判断并把值赋给一个常量或者变量。`if`和`while`语句,请参考[控制流](./05_Control_Flow.html)。
|
||||
|
||||
像下面这样在`if`语句中写一个可选绑定:
|
||||
|
||||
@ -654,7 +664,7 @@ if let constantName = someOptional, anotherConstantName = someOtherOptional {
|
||||
|
||||
这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(`String?`)改成感叹号(`String!`)来声明一个隐式解析可选类型。
|
||||
|
||||
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考[类实例之间的循环强引用](16_Automatic_Reference_Counting.html#strong_reference_cycles_between_class_instances)。
|
||||
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考[无主引用以及隐式解析可选属性](./16_Automatic_Reference_Counting.html#unowned_references_and_implicitly_unwrapped_optional_properties)。
|
||||
|
||||
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型`String`和隐式解析可选类型`String`之间的区别:
|
||||
|
||||
@ -740,7 +750,7 @@ do {
|
||||
|
||||
如果没有错误被抛出, `eatASandwich()`函数会被调用。如果一个符合`Error.OutOfCleanDishes`的错误被抛出,`washDishes`函数会被调用。如果一个符合`Error.MissingIngredients`的错误被抛出,`buyGroceries(_:)`函数会被调用并传递相关被`catch`所捕捉到的`[String]`值。
|
||||
|
||||
抛出,捕捉,传递错误会在[错误处理](../chapter2/18_Error_Handling.html)章节详细说明。
|
||||
抛出,捕捉,传递错误会在[错误处理](./18_Error_Handling.html)章节详细说明。
|
||||
|
||||
<a name="assertions"></a>
|
||||
## 断言
|
||||
@ -777,7 +787,7 @@ assert(age >= 0)
|
||||
* 需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。
|
||||
* 一个可选值现在是`nil`,但是后面的代码运行需要一个非`nil`值。
|
||||
|
||||
请参考[下标脚本](12_Subscripts.html)和[函数](06_Functions.html)。
|
||||
请参考[下标脚本](./12_Subscripts.html)和[函数](./06_Functions.html)。
|
||||
|
||||
> 注意:
|
||||
断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
> 1.0
|
||||
> 翻译:[XieLingWang](https://github.com/xielingwang)
|
||||
> 校对:[EvilCome](https://github.com/Evilcome)
|
||||
|
||||
> 翻译:[XieLingWang](https://github.com/xielingwang), [JackAlan](https://github.com/AlanMelody)
|
||||
> 校对:[EvilCome](https://github.com/Evilcome), [JackAlan](https://github.com/AlanMelody)
|
||||
> 2.0
|
||||
> 翻译+校对:[JackAlan](https://github.com/AlanMelody)
|
||||
|
||||
# 基本运算符
|
||||
-----------------
|
||||
@ -19,11 +22,11 @@
|
||||
|
||||
运算符是检查、改变、合并值的特殊符号或短语。例如,加号`+`将两个数相加(如`let i = 1 + 2`)。更复杂的运算例子包括逻辑与运算符`&&`(如`if enteredDoorCode && passedRetinaScan`),或让 i 值加1的便捷自增运算符`++i`等。
|
||||
|
||||
Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。算术运算符(`+`,`-`,`*`,`/`,`%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](24_Advanced_Operators.html#overflow_operators)。
|
||||
Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。算术运算符(`+`,`-`,`*`,`/`,`%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](./24_Advanced_Operators.html#overflow_operators)。
|
||||
|
||||
区别于 C 语言,在 Swift 中你可以对浮点数进行取余运算(`%`),Swift 还提供了 C 语言没有的表达两数之间的值的区间运算符(`a..<b`和`a...b`),这方便我们表达一个区间内的数值。
|
||||
|
||||
本章节只描述了 Swift 中的基本运算符,[高级运算符](24_Advanced_Operators.html)包含了高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
|
||||
本章节只描述了 Swift 中的基本运算符,[高级运算符](./24_Advanced_Operators.html)包含了高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
|
||||
|
||||
<a name="terminology"></a>
|
||||
## 术语
|
||||
@ -77,7 +80,7 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
|
||||
|
||||
|
||||
与 C 语言和 Objective-C 不同的是,Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如`a &+ b`)。详情参见[溢出运算符](24_Advanced_Operators.html#overflow_operators)。
|
||||
与 C 语言和 Objective-C 不同的是,Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如`a &+ b`)。详情参见[溢出运算符](./24_Advanced_Operators.html#overflow_operators)。
|
||||
|
||||
加法运算符也可用于`String`的拼接:
|
||||
|
||||
@ -215,7 +218,7 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
- 小于等于(`a <= b`)
|
||||
|
||||
> 注意:
|
||||
Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](09_Classes_and_Structures.html)。
|
||||
Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](./09_Classes_and_Structures.html)。
|
||||
|
||||
每个比较运算都返回了一个标识表达式是否成立的布尔值:
|
||||
|
||||
@ -240,7 +243,7 @@ Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对
|
||||
// 输出 "hello, world", 因为 `name` 就是等于 "world"
|
||||
|
||||
|
||||
关于`if`语句,请看[控制流](05_Control_Flow.html)。
|
||||
关于`if`语句,请看[控制流](./05_Control_Flow.html)。
|
||||
|
||||
<a name="ternary_conditional_operator"></a>
|
||||
## 三目运算符(Ternary Conditional Operator)
|
||||
@ -335,7 +338,7 @@ Swift 提供了两个方便表达一个区间的值的运算符。
|
||||
// 5 * 5 = 25
|
||||
|
||||
|
||||
关于`for-in`,请看[控制流](05_Control_Flow.html)。
|
||||
关于`for-in`,请看[控制流](./05_Control_Flow.html)。
|
||||
|
||||
### 半开区间运算符
|
||||
|
||||
@ -356,7 +359,7 @@ Swift 提供了两个方便表达一个区间的值的运算符。
|
||||
// 第 4 个人叫 Jack
|
||||
|
||||
|
||||
数组有4个元素,但`0..<count`只数到3(最后一个元素的下标),因为它是半开区间。关于数组,请查阅[数组](04_Collection_Types.html#arrays)。
|
||||
数组有4个元素,但`0..<count`只数到3(最后一个元素的下标),因为它是半开区间。关于数组,请查阅[数组](./04_Collection_Types.html#arrays)。
|
||||
|
||||
<a name="logical_operators"></a>
|
||||
## 逻辑运算
|
||||
@ -434,10 +437,10 @@ Swift 提供了两个方便表达一个区间的值的运算符。
|
||||
|
||||
如果我们输入了正确的密码并通过了视网膜扫描; 或者我们有一把有效的钥匙; 又或者我们知道紧急情况下重置的密码,我们就能把门打开进入。
|
||||
|
||||
前两种情况,我们都不满足,所以前两个简单逻辑的结果是`false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是`true`。
|
||||
|
||||
>注意:
|
||||
Swift 逻辑操作符`&&`和`||`是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。
|
||||
前两种情况,我们都不满足,所以前两个简单逻辑的结果是`false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是`true`。
|
||||
|
||||
>注意:
|
||||
Swift 逻辑操作符`&&`和`||`是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。
|
||||
|
||||
### 使用括号来明确优先级
|
||||
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
> 翻译:[wh1100717](https://github.com/wh1100717)
|
||||
> 校对:[Hawstein](https://github.com/Hawstein)
|
||||
|
||||
# 字符串和字符(Strings and Characters)
|
||||
---
|
||||
|
||||
本页包含内容:
|
||||
|
||||
> 1.0
|
||||
> 翻译:[wh1100717](https://github.com/wh1100717)
|
||||
> 校对:[Hawstein](https://github.com/Hawstein)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[DianQK](https://github.com/DianQK)
|
||||
|
||||
# 字符串和字符(Strings and Characters)
|
||||
---
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [字符串字面量](#string_literals)
|
||||
- [初始化空字符串](#initializing_an_empty_string)
|
||||
- [字符串可变性](#string_mutability)
|
||||
@ -17,59 +21,59 @@
|
||||
- [计算字符数量](#counting_characters)
|
||||
- [访问和修改字符串](#accessing_and_modifying_a_string)
|
||||
- [比较字符串](#comparing_strings)
|
||||
- [字符串的 Unicode 表示形式](#unicode_representations_of_strings)
|
||||
|
||||
|
||||
`String`是例如"hello, world","albatross"这样的有序的`Character`(字符)类型的值的集合,通过`String`类型来表示。
|
||||
- [字符串的 Unicode 表示形式](#unicode_representations_of_strings)
|
||||
|
||||
|
||||
`String`是例如"hello, world","albatross"这样的有序的`Character`(字符)类型的值的集合,通过`String`类型来表示。
|
||||
Swift 的`String`和`Character`类型提供了一个快速的,兼容 Unicode 的方式来处理代码中的文本。
|
||||
创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。
|
||||
字符串连接操作只需要简单地通过`+`符号将两个字符串相连即可。
|
||||
与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。
|
||||
与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。
|
||||
尽管语法简易,但`String`类型是一种快速、现代化的字符串实现。
|
||||
每一个字符串都是由编码无关的 Unicode 字符组成,并支持访问字符的多种 Unicode 表示形式(representations)。
|
||||
你也可以在常量、变量、字面量和表达式中进行字符串插值操作,这可以帮助你轻松创建用于展示、存储和打印的自定义字符串。
|
||||
|
||||
每一个字符串都是由编码无关的 Unicode 字符组成,并支持访问字符的多种 Unicode 表示形式(representations)。
|
||||
你也可以在常量、变量、字面量和表达式中进行字符串插值操作,这可以帮助你轻松创建用于展示、存储和打印的自定义字符串。
|
||||
|
||||
> 注意:
|
||||
> Swift 的`String`类型与 Foundation `NSString`类进行了无缝桥接。就像 [AnyObject](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-ID343) 中提到的一样,在使用 Cocoa 中的 Foundation 框架时,您可以将创建的任何字符串的值转换成`NSString`,并调用任意的`NSString` API。您也可以在任意要求传入`NSString`实例作为参数的 API 中用`String`类型的值代替。
|
||||
> Swift 的`String`类型与 Foundation `NSString`类进行了无缝桥接。就像 [`AnyObject`类型](./20_Type_Casting.html#anyobject) 中提到的一样,在使用 Cocoa 中的 Foundation 框架时,您可以将创建的任何字符串的值转换成`NSString`,并调用任意的`NSString` API。您也可以在任意要求传入`NSString`实例作为参数的 API 中用`String`类型的值代替。
|
||||
> 更多关于在 Foundation 和 Cocoa 中使用`String`的信息请查看 *[Using Swift with Cocoa and Objective-C](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)*。
|
||||
|
||||
|
||||
|
||||
<a name="string_literals"></a>
|
||||
## 字符串字面量(String Literals)
|
||||
|
||||
|
||||
<a name="string_literals"></a>
|
||||
## 字符串字面量(String Literals)
|
||||
|
||||
您可以在您的代码中包含一段预定义的字符串值作为字符串字面量。字符串字面量是由双引号 ("") 包裹着的具有固定顺序的文本字符集。
|
||||
字符串字面量可以用于为常量和变量提供初始值:
|
||||
```let someString = "Some string literal value"```
|
||||
注意`someString`常量通过字符串字面量进行初始化,Swift 会推断该常量为`String`类型。
|
||||
字符串字面量可以用于为常量和变量提供初始值:
|
||||
```let someString = "Some string literal value"```
|
||||
注意`someString`常量通过字符串字面量进行初始化,Swift 会推断该常量为`String`类型。
|
||||
> 注意:
|
||||
> 更多关于在字面量的特殊字符,请查看 [Special Characters in String Literals](#special_characters_in_string_literals) 。
|
||||
|
||||
|
||||
<a name="initializing_an_empty_string"></a>
|
||||
## 初始化空字符串 (Initializing an Empty String)
|
||||
|
||||
|
||||
<a name="initializing_an_empty_string"></a>
|
||||
## 初始化空字符串 (Initializing an Empty String)
|
||||
|
||||
要创建一个空字符串作为初始值,可以将空的字符串字面量赋值给变量,也可以初始化一个新的`String`实例:
|
||||
|
||||
|
||||
```swift
|
||||
var emptyString = "" // 空字符串字面量
|
||||
var anotherEmptyString = String() // 初始化方法
|
||||
// 两个字符串均为空并等价。
|
||||
```
|
||||
|
||||
您可以通过检查其`Boolean`类型的`isEmpty`属性来判断该字符串是否为空:
|
||||
```
|
||||
|
||||
您可以通过检查其`Boolean`类型的`isEmpty`属性来判断该字符串是否为空:
|
||||
```swift
|
||||
if emptyString.isEmpty {
|
||||
print("Nothing to see here")
|
||||
}
|
||||
// 打印输出:"Nothing to see here"
|
||||
```
|
||||
|
||||
<a name="string_mutability"></a>
|
||||
## 字符串可变性 (String Mutability)
|
||||
|
||||
您可以通过将一个特定字符串分配给一个变量来对其进行修改,或者分配给一个常量来保证其不会被修改:
|
||||
|
||||
```
|
||||
|
||||
<a name="string_mutability"></a>
|
||||
## 字符串可变性 (String Mutability)
|
||||
|
||||
您可以通过将一个特定字符串分配给一个变量来对其进行修改,或者分配给一个常量来保证其不会被修改:
|
||||
|
||||
```swift
|
||||
var variableString = "Horse"
|
||||
variableString += " and carriage"
|
||||
@ -77,33 +81,33 @@ variableString += " and carriage"
|
||||
let constantString = "Highlander"
|
||||
constantString += " and another Highlander"
|
||||
// 这会报告一个编译错误 (compile-time error) - 常量不可以被修改。
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 在 Objective-C 和 Cocoa 中,您需要通过选择两个不同的类(`NSString`和`NSMutableString`)来指定该字符串是否可以被修改。
|
||||
|
||||
<a name="strings_are_value_types"></a>
|
||||
## 字符串是值类型(Strings Are Value Types)
|
||||
|
||||
> 在 Objective-C 和 Cocoa 中,您需要通过选择两个不同的类(`NSString`和`NSMutableString`)来指定该字符串是否可以被修改。
|
||||
|
||||
<a name="strings_are_value_types"></a>
|
||||
## 字符串是值类型(Strings Are Value Types)
|
||||
|
||||
Swift 的`String`类型是值类型。
|
||||
如果您创建了一个新的字符串,那么当其进行常量、变量赋值操作,或在函数/方法中传递时,会进行值拷贝。
|
||||
任何情况下,都会对已有字符串值创建新副本,并对该新副本进行传递或赋值操作。
|
||||
值类型在 [结构体和枚举是值类型](09_Classes_and_Structures.html#structures_and_enumerations_are_value_types) 中进行了详细描述。
|
||||
|
||||
任何情况下,都会对已有字符串值创建新副本,并对该新副本进行传递或赋值操作。
|
||||
值类型在 [结构体和枚举是值类型](./09_Classes_and_Structures.html#structures_and_enumerations_are_value_types) 中进行了详细描述。
|
||||
|
||||
> 注意:
|
||||
> 与 Cocoa 中的`NSString`不同,当您在 Cocoa 中创建了一个`NSString`实例,并将其传递给一个函数/方法,或者赋值给一个变量,您传递或赋值的是该`NSString`实例的一个引用,除非您特别要求进行值拷贝,否则字符串不会生成新的副本来进行赋值操作。
|
||||
|
||||
Swift 默认字符串拷贝的方式保证了在函数/方法中传递的是字符串的值。
|
||||
很明显无论该值来自于哪里,都是您独自拥有的。
|
||||
您可以放心您传递的字符串本身不会被更改。
|
||||
|
||||
在实际编译时,Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着您将字符串作为值类型的同时可以获得极高的性能。
|
||||
|
||||
<a name="working_with_characters"></a>
|
||||
## 使用字符(Working with Characters)
|
||||
|
||||
您可通过`for-in`循环来遍历字符串中的`characters`属性来获取每一个字符的值:
|
||||
|
||||
> 与 Cocoa 中的`NSString`不同,当您在 Cocoa 中创建了一个`NSString`实例,并将其传递给一个函数/方法,或者赋值给一个变量,您传递或赋值的是该`NSString`实例的一个引用,除非您特别要求进行值拷贝,否则字符串不会生成新的副本来进行赋值操作。
|
||||
|
||||
Swift 默认字符串拷贝的方式保证了在函数/方法中传递的是字符串的值。
|
||||
很明显无论该值来自于哪里,都是您独自拥有的。
|
||||
您可以放心您传递的字符串本身不会被更改。
|
||||
|
||||
在实际编译时,Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着您将字符串作为值类型的同时可以获得极高的性能。
|
||||
|
||||
<a name="working_with_characters"></a>
|
||||
## 使用字符(Working with Characters)
|
||||
|
||||
您可通过`for-in`循环来遍历字符串中的`characters`属性来获取每一个字符的值:
|
||||
|
||||
```swift
|
||||
for character in "Dog!🐶".characters {
|
||||
print(character)
|
||||
@ -113,12 +117,12 @@ for character in "Dog!🐶".characters {
|
||||
// g
|
||||
// !
|
||||
// 🐶
|
||||
```
|
||||
|
||||
for-in 循环在 [For Loops](05_Control_Flow.html#for_loops) 中进行了详细描述。
|
||||
|
||||
另外,通过标明一个`Character`类型并用字符字面量进行赋值,可以建立一个独立的字符常量或变量:
|
||||
|
||||
```
|
||||
|
||||
for-in 循环在 [For Loops](./05_Control_Flow.html#for_loops) 中进行了详细描述。
|
||||
|
||||
另外,通过标明一个`Character`类型并用字符字面量进行赋值,可以建立一个独立的字符常量或变量:
|
||||
|
||||
```swift
|
||||
let exclamationMark: Charater = "!"
|
||||
```
|
||||
@ -131,96 +135,96 @@ print(catString)
|
||||
// 打印输出:"Cat!🐱"
|
||||
```
|
||||
|
||||
<a name="concatenating_strings_and_characters"></a>
|
||||
## 连接字符串和字符 (Concatenating Strings and Characters)
|
||||
|
||||
字符串可以通过加法运算符(`+`)相加在一起(或称“连接”)创建一个新的字符串:
|
||||
|
||||
<a name="concatenating_strings_and_characters"></a>
|
||||
## 连接字符串和字符 (Concatenating Strings and Characters)
|
||||
|
||||
字符串可以通过加法运算符(`+`)相加在一起(或称“连接”)创建一个新的字符串:
|
||||
|
||||
```swift
|
||||
let string1 = "hello"
|
||||
let string2 = " there"
|
||||
var welcome = string1 + string2
|
||||
// welcome 现在等于 "hello there"
|
||||
```
|
||||
|
||||
您也可以通过加法赋值运算符 (`+=`) 将一个字符串添加到一个已经存在字符串变量上:
|
||||
|
||||
```
|
||||
|
||||
您也可以通过加法赋值运算符 (`+=`) 将一个字符串添加到一个已经存在字符串变量上:
|
||||
|
||||
```swift
|
||||
var instruction = "look over"
|
||||
instruction += string2
|
||||
// instruction 现在等于 "look over there"
|
||||
```
|
||||
|
||||
您可以用`append`方法将一个字符附加到一个字符串变量的尾部:
|
||||
|
||||
|
||||
您可以用`append`方法将一个字符附加到一个字符串变量的尾部:
|
||||
|
||||
```swift
|
||||
let exclamationMark: Character = "!"
|
||||
welcome.append(exclamationMark)
|
||||
// welcome 现在等于 "hello there!"
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。
|
||||
|
||||
|
||||
<a name="string_interpolation"></a>
|
||||
## 字符串插值 (String Interpolation)
|
||||
|
||||
字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。
|
||||
您插入的字符串字面量的每一项都在以反斜线为前缀的圆括号中:
|
||||
|
||||
<a name="string_interpolation"></a>
|
||||
## 字符串插值 (String Interpolation)
|
||||
|
||||
字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。
|
||||
您插入的字符串字面量的每一项都在以反斜线为前缀的圆括号中:
|
||||
|
||||
```swift
|
||||
let multiplier = 3
|
||||
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
|
||||
// message 是 "3 times 2.5 is 7.5"
|
||||
```
|
||||
|
||||
在上面的例子中,`multiplier`作为`\(multiplier)`被插入到一个字符串字面量中。
|
||||
当创建字符串执行插值计算时此占位符会被替换为`multiplier`实际的值。
|
||||
|
||||
`multiplier`的值也作为字符串中后面表达式的一部分。
|
||||
该表达式计算`Double(multiplier) * 2.5`的值并将结果 (7.5) 插入到字符串中。
|
||||
在这个例子中,表达式写为`\(Double(multiplier) * 2.5)`并包含在字符串字面量中。
|
||||
|
||||
```
|
||||
|
||||
在上面的例子中,`multiplier`作为`\(multiplier)`被插入到一个字符串字面量中。
|
||||
当创建字符串执行插值计算时此占位符会被替换为`multiplier`实际的值。
|
||||
|
||||
`multiplier`的值也作为字符串中后面表达式的一部分。
|
||||
该表达式计算`Double(multiplier) * 2.5`的值并将结果 (7.5) 插入到字符串中。
|
||||
在这个例子中,表达式写为`\(Double(multiplier) * 2.5)`并包含在字符串字面量中。
|
||||
|
||||
> 注意:
|
||||
> 插值字符串中写在括号中的表达式不能包含非转义双引号 (`"`) 和反斜杠 (`\`),并且不能包含回车或换行符。
|
||||
|
||||
|
||||
|
||||
<a name="unicode"></a>
|
||||
## Unicode
|
||||
|
||||
Unicode 是一个国际标准,用于文本的编码和表示。
|
||||
它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。
|
||||
## Unicode
|
||||
|
||||
Unicode 是一个国际标准,用于文本的编码和表示。
|
||||
它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。
|
||||
Swift 的字符串和字符类型是完全兼容 Unicode 标准的。
|
||||
|
||||
<a name="unicode_scalars"></a>
|
||||
### Unicode 标量(Unicode Scalars)
|
||||
|
||||
Swift 的`String`类型是基于 *Unicode 标量* 建立的。
|
||||
|
||||
Swift 的`String`类型是基于 *Unicode 标量* 建立的。
|
||||
Unicode 标量是对应字符的唯一21位数字或者修饰符,例如`U+0061`表示小写的拉丁字母(`LATIN SMALL LETTER A`)("`a`"),`U+1F425`表示小鸡表情(`FRONT-FACING BABY CHICK`) ("`🐥`")
|
||||
> 注意:
|
||||
> Unicode *码位(code poing)* 的范围是`U+0000`到`U+D7FF`或者`U+E000`到`U+10FFFF`。Unicode 标量不包括 Unicode *代理项(surrogate pair)* 码位,其码位范围是`U+D800`到`U+DFFF`。
|
||||
> Unicode *码位(code poing)* 的范围是`U+0000`到`U+D7FF`或者`U+E000`到`U+10FFFF`。Unicode 标量不包括 Unicode *代理项(surrogate pair)* 码位,其码位范围是`U+D800`到`U+DFFF`。
|
||||
|
||||
注意不是所有的21位 Unicode 标量都代表一个字符,因为有一些标量是保留给未来分配的。已经代表一个典型字符的标量都有自己的名字,例如上面例子中的`LATIN SMALL LETTER A`和`FRONT-FACING BABY CHICK`。
|
||||
|
||||
<a name="special_characters_in_string_literals"></a>
|
||||
### 字符串字面量的特殊字符 (Special Characters in String Literals)
|
||||
|
||||
|
||||
字符串字面量可以包含以下特殊字符:
|
||||
|
||||
* 转义字符`\0`(空字符)、`\\`(反斜线)、`\t`(水平制表符)、`\n`(换行符)、`\r`(回车符)、`\"`(双引号)、`\'`(单引号)。
|
||||
* Unicode 标量,写成`\u{n}`(u为小写),其中`n`为任意一到八位十六进制数且可用的 Unicode 位码。
|
||||
|
||||
下面的代码为各种特殊字符的使用示例。
|
||||
`wiseWords`常量包含了两个双引号;
|
||||
`dollarSign`、`blackHeart`和`sparklingHeart`常量演示了三种不同格式的 Unicode 标量:
|
||||
|
||||
```swift
|
||||
|
||||
* 转义字符`\0`(空字符)、`\\`(反斜线)、`\t`(水平制表符)、`\n`(换行符)、`\r`(回车符)、`\"`(双引号)、`\'`(单引号)。
|
||||
* Unicode 标量,写成`\u{n}`(u为小写),其中`n`为任意一到八位十六进制数且可用的 Unicode 位码。
|
||||
|
||||
下面的代码为各种特殊字符的使用示例。
|
||||
`wiseWords`常量包含了两个双引号;
|
||||
`dollarSign`、`blackHeart`和`sparklingHeart`常量演示了三种不同格式的 Unicode 标量:
|
||||
|
||||
```swift
|
||||
let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
|
||||
// "Imageination is more important than knowledge" - Enistein
|
||||
let dollarSign = "\u{24}" // $, Unicode 标量 U+0024
|
||||
let blackHeart = "\u{2665}" // ♥, Unicode 标量 U+2665
|
||||
let sparklingHeart = "\u{1F496}" // 💖, Unicode 标量 U+1F496
|
||||
let sparklingHeart = "\u{1F496}" // 💖, Unicode 标量 U+1F496
|
||||
```
|
||||
|
||||
<a name="extended_grapheme_clusters"></a>
|
||||
@ -262,22 +266,22 @@ let enclosedEAcute: Character = "\u{E9}\u{20DD}"
|
||||
```swift
|
||||
let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
|
||||
// regionalIndicatorForUS 是 🇺🇸
|
||||
```
|
||||
|
||||
<a name="counting_characters"></a>
|
||||
## 计算字符数量 (Counting Characters)
|
||||
```
|
||||
|
||||
<a name="counting_characters"></a>
|
||||
## 计算字符数量 (Counting Characters)
|
||||
|
||||
调用字符串的`count`属性,就可以获取一个字符串的字符数量:
|
||||
|
||||
|
||||
|
||||
|
||||
```swift
|
||||
let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
|
||||
print("unusualMenagerie has \(unusualMenagerie.characters.count) characters")
|
||||
// 打印输出:"unusualMenagerie has 40 characters"
|
||||
// 打印输出:"unusualMenagerie has 40 characters"
|
||||
```
|
||||
|
||||
注意在 Swift 中,使用可拓展的字符群集作为字符来连接或改变字符串时,并不一定会更改字符串的字符数量。
|
||||
例如,如果你用四个字符的单词 cafe 初始化一个新的字符串,然后添加一个 `COMBINING ACTUE ACCENT`(`U+0301`)作为字符串的结尾。最终这个字符串的字符数量仍然是4,因为第四个字符是 é ,而不是 e :
|
||||
例如,如果你用四个字符的单词 cafe 初始化一个新的字符串,然后添加一个 `COMBINING ACTUE ACCENT`(`U+0301`)作为字符串的结尾。最终这个字符串的字符数量仍然是4,因为第四个字符是 é ,而不是 e :
|
||||
|
||||
```swift
|
||||
var word = "cafe"
|
||||
@ -287,9 +291,9 @@ word += "\u{301}" // COMBINING ACUTE ACCENT, U+0301
|
||||
print("the number of characters in \(word) is \(word.characters.count)")
|
||||
// 打印输出 "the number of characters in café is 4"
|
||||
```
|
||||
|
||||
|
||||
> 注意:
|
||||
> 可扩展的字符群集可以组成一个或者多个 Unicode 标量。这意味着不同的字符以及相同字符的不同表示方式可能需要不同数量的内存空间来存储。所以 Swift 中的字符在一个字符串中并不一定占用相同的内存空间数量。因此在没有获取字符串的可扩展的字符群的范围时候,就不能计算出字符串的字符数量。如果您正在处理一个长字符串,需要注意`characters`属性必须遍历全部的 Unicode 标量,来确定字符串的字符数量。
|
||||
> 可扩展的字符群集可以组成一个或者多个 Unicode 标量。这意味着不同的字符以及相同字符的不同表示方式可能需要不同数量的内存空间来存储。所以 Swift 中的字符在一个字符串中并不一定占用相同的内存空间数量。因此在没有获取字符串的可扩展的字符群的范围时候,就不能计算出字符串的字符数量。如果您正在处理一个长字符串,需要注意`characters`属性必须遍历全部的 Unicode 标量,来确定字符串的字符数量。
|
||||
> 另外需要注意的是通过`characters`返回的字符数量并不总是与包含相同字符的`NSString`的`length`属性相同。`NSString`的`length`属性是利用 UTF-16 表示的十六位代码单元数字,而不是 Unicode 可扩展的字符群集。
|
||||
|
||||
|
||||
@ -361,24 +365,24 @@ let range = advance(welcome.endIndex, -6)..<welcome.endIndex
|
||||
welcome.removeRange(range)
|
||||
// welcome 现在等于 "hello"
|
||||
```
|
||||
|
||||
|
||||
<a name="comparing_strings"></a>
|
||||
## 比较字符串 (Comparing Strings)
|
||||
|
||||
Swift 提供了三种方式来比较文本值:字符串字符相等、前缀相等和后缀相等。
|
||||
|
||||
<a name="string_and_character_equality"></a>
|
||||
|
||||
|
||||
<a name="comparing_strings"></a>
|
||||
## 比较字符串 (Comparing Strings)
|
||||
|
||||
Swift 提供了三种方式来比较文本值:字符串字符相等、前缀相等和后缀相等。
|
||||
|
||||
<a name="string_and_character_equality"></a>
|
||||
### 字符串/字符相等 (String and Character Equality)
|
||||
字符串/字符可以用等于操作符(`==`)和不等于操作符(`!=`),详细描述在 [Comparison Operators](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-ID70):
|
||||
|
||||
字符串/字符可以用等于操作符(`==`)和不等于操作符(`!=`),详细描述在[比较运算符](./02_Basic_Operators.html#comparison_operators):
|
||||
|
||||
```swift
|
||||
let quotation = "We're a lot alike, you and I."
|
||||
let sameQuotation = "We're a lot alike, you and I."
|
||||
if quotation == sameQuotation {
|
||||
print("These two strings are considered equal")
|
||||
}
|
||||
// 打印输出 "These two strings are considered equal"
|
||||
// 打印输出 "These two strings are considered equal"
|
||||
```
|
||||
|
||||
如果两个字符串(或者两个字符)的可扩展的字形群集是标准相等的,那就认为它们是相等的。在这个情况下,即使可扩展的字形群集是有不同的 Unicode 标量构成的,只要它们有同样的语言意义和外观,就认为它们标准相等。
|
||||
@ -404,18 +408,18 @@ if latinCapitalLetterA != cyrillicCapitalLetterA {
|
||||
print("These two characters are not equivalent")
|
||||
}
|
||||
// 打印 "These two characters are not equivalent"
|
||||
```
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 在 Swift 中,字符串和字符并不区分区域。
|
||||
|
||||
|
||||
<a name="prefix_and_suffix_equality"></a>
|
||||
### 前缀/后缀相等 (Prefix and Suffix Equality)
|
||||
|
||||
|
||||
<a name="prefix_and_suffix_equality"></a>
|
||||
### 前缀/后缀相等 (Prefix and Suffix Equality)
|
||||
|
||||
通过调用字符串的`hasPrefix(_:)`/`hasSuffix(_:)`方法来检查字符串是否拥有特定前缀/后缀,两个方法均需要以字符串作为参数传入并传出`Boolean`值。
|
||||
下面的例子以一个字符串数组表示莎士比亚话剧《罗密欧与朱丽叶》中前两场的场景位置:
|
||||
|
||||
下面的例子以一个字符串数组表示莎士比亚话剧《罗密欧与朱丽叶》中前两场的场景位置:
|
||||
|
||||
```swift
|
||||
let romeoAndJuliet = [
|
||||
"Act 1 Scene 1: Verona, A public place",
|
||||
@ -429,11 +433,11 @@ let romeoAndJuliet = [
|
||||
"Act 2 Scene 4: A street in Verona",
|
||||
"Act 2 Scene 5: Capulet's mansion",
|
||||
"Act 2 Scene 6: Friar Lawrence's cell"
|
||||
]
|
||||
```
|
||||
|
||||
您可以调用`hasPrefix(_:)`方法来计算话剧中第一幕的场景数:
|
||||
|
||||
]
|
||||
```
|
||||
|
||||
您可以调用`hasPrefix(_:)`方法来计算话剧中第一幕的场景数:
|
||||
|
||||
```swift
|
||||
var act1SceneCount = 0
|
||||
for scene in romeoAndJuliet {
|
||||
@ -442,11 +446,11 @@ for scene in romeoAndJuliet {
|
||||
}
|
||||
}
|
||||
print("There are \(act1SceneCount) scenes in Act 1")
|
||||
// 打印输出 "There are 5 scenes in Act 1"
|
||||
```
|
||||
|
||||
相似地,您可以用`hasSuffix(_:)`方法来计算发生在不同地方的场景数:
|
||||
|
||||
// 打印输出 "There are 5 scenes in Act 1"
|
||||
```
|
||||
|
||||
相似地,您可以用`hasSuffix(_:)`方法来计算发生在不同地方的场景数:
|
||||
|
||||
```swift
|
||||
var mansionCount = 0
|
||||
var cellCount = 0
|
||||
@ -458,37 +462,37 @@ for scene in romeoAndJuliet {
|
||||
}
|
||||
}
|
||||
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
|
||||
// 打印输出 "6 mansion scenes; 2 cell scenes"
|
||||
// 打印输出 "6 mansion scenes; 2 cell scenes"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> `hasPrefix(_:)`和`hasSuffix(_:)`方法都是在每个字符串中一个一个字符的比较其可扩展的字符群集是否标准相等,详细描述在[字符串/字符相等](#string_and_character_equality)。
|
||||
|
||||
|
||||
<a name="unicode_representations_of_strings"></a>
|
||||
|
||||
<a name="unicode_representations_of_strings"></a>
|
||||
## 字符串的 Unicode 表示形式(Unicode Representations of Strings)
|
||||
当一个 Unicode 字符串被写进文本文件或者其他储存时,字符串中的 Unicode 标量会用 Unicode 定义的几种编码格式编码。每一个字符串中的小块编码都被称为代码单元。这些包括 UTF-8 编码格式(编码字符串为8位的代码单元), UTF-16 编码格式(编码字符串位16位的代码单元),以及 UTF-32 编码格式(编码字符串32位的代码单元)。
|
||||
|
||||
当一个 Unicode 字符串被写进文本文件或者其他储存时,字符串中的 Unicode 标量会用 Unicode 定义的几种编码格式编码。每一个字符串中的小块编码都被称为代码单元。这些包括 UTF-8 编码格式(编码字符串为8位的代码单元), UTF-16 编码格式(编码字符串位16位的代码单元),以及 UTF-32 编码格式(编码字符串32位的代码单元)。
|
||||
|
||||
Swift 提供了几种不同的方式来访问字符串的 Unicode 表示形式。
|
||||
您可以利用`for-in`来对字符串进行遍历,从而以 Unicode 可扩展的字符群集的方式访问每一个字符值。
|
||||
该过程在 [使用字符](#working_with_characters) 中进行了描述。
|
||||
|
||||
另外,能够以其他三种 Unicode 兼容的方式访问字符串的值:
|
||||
|
||||
* UTF-8 代码单元集合 (利用字符串的`utf8`属性进行访问)
|
||||
* UTF-16 代码单元集合 (利用字符串的`utf16`属性进行访问)
|
||||
* 21位的 Unicode 标量值集合,也就是字符串的 UTF-32 编码格式 (利用字符串的`unicodeScalars`属性进行访问)
|
||||
|
||||
下面由`D``o``g``‼`(`DOUBLE EXCLAMATION MARK`, Unicode 标量 `U+203C`)和`🐶`(`DOG FACE`,Unicode 标量为`U+1F436`)组成的字符串中的每一个字符代表着一种不同的表示:
|
||||
|
||||
该过程在 [使用字符](#working_with_characters) 中进行了描述。
|
||||
|
||||
另外,能够以其他三种 Unicode 兼容的方式访问字符串的值:
|
||||
|
||||
* UTF-8 代码单元集合 (利用字符串的`utf8`属性进行访问)
|
||||
* UTF-16 代码单元集合 (利用字符串的`utf16`属性进行访问)
|
||||
* 21位的 Unicode 标量值集合,也就是字符串的 UTF-32 编码格式 (利用字符串的`unicodeScalars`属性进行访问)
|
||||
|
||||
下面由`D``o``g``‼`(`DOUBLE EXCLAMATION MARK`, Unicode 标量 `U+203C`)和`🐶`(`DOG FACE`,Unicode 标量为`U+1F436`)组成的字符串中的每一个字符代表着一种不同的表示:
|
||||
|
||||
```swift
|
||||
let dogString = "Dog‼🐶"
|
||||
let dogString = "Dog‼🐶"
|
||||
```
|
||||
|
||||
|
||||
<a name="UTF-8_representation"></a>
|
||||
### UTF-8 表示
|
||||
您可以通过遍历字符串的`utf8`属性来访问它的`UTF-8`表示。
|
||||
|
||||
|
||||
<a name="UTF-8_representation"></a>
|
||||
### UTF-8 表示
|
||||
您可以通过遍历字符串的`utf8`属性来访问它的`UTF-8`表示。
|
||||
其为`String.UTF8View`类型的属性,`UTF8View`是无符号8位 (`UInt8`) 值的集合,每一个`UInt8`值都是一个字符的 UTF-8 表示:
|
||||
|
||||
<body>
|
||||
@ -530,25 +534,25 @@ let dogString = "Dog‼🐶"
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
</body>
|
||||
|
||||
</body>
|
||||
|
||||
```swift
|
||||
for codeUnit in dogString.utf8 {
|
||||
print("\(codeUnit) ", appendNewline: false)
|
||||
}
|
||||
print("")
|
||||
// 68 111 103 226 128 188 240 159 144 182
|
||||
```
|
||||
|
||||
// 68 111 103 226 128 188 240 159 144 182
|
||||
```
|
||||
|
||||
上面的例子中,前三个10进制代码单元值 (68, 111, 103) 代表了字符`D`、`o`和 `g`,它们的 UTF-8 表示与 ASCII 表示相同。
|
||||
接下来的三个10进制代码单元值 (226, 128, 188) 是`DOUBLE EXCLAMATION MARK`的3字节 UTF-8 表示。
|
||||
最后的四个代码单元值 (240, 159, 144, 182) 是`DOG FACE`的4字节 UTF-8 表示。
|
||||
|
||||
|
||||
<a name="UTF-16_representation"></a>
|
||||
### UTF-16 表示
|
||||
|
||||
您可以通过遍历字符串的`utf16`属性来访问它的`UTF-16`表示。
|
||||
|
||||
|
||||
<a name="UTF-16_representation"></a>
|
||||
### UTF-16 表示
|
||||
|
||||
您可以通过遍历字符串的`utf16`属性来访问它的`UTF-16`表示。
|
||||
其为`String.UTF16View`类型的属性,`UTF16View`是无符号16位 (`UInt16`) 值的集合,每一个`UInt16`都是一个字符的 UTF-16 表示:
|
||||
<body>
|
||||
<center>
|
||||
@ -582,28 +586,28 @@ print("")
|
||||
</table>
|
||||
</center>
|
||||
</body>
|
||||
|
||||
|
||||
|
||||
|
||||
```swift
|
||||
for codeUnit in dogString.utf16 {
|
||||
print("\(codeUnit) ", appendNewline: false)
|
||||
}
|
||||
print("")
|
||||
// 68 111 103 8252 55357 56374
|
||||
```
|
||||
|
||||
// 68 111 103 8252 55357 56374
|
||||
```
|
||||
|
||||
同样,前三个代码单元值 (68, 111, 103) 代表了字符`D`、`o`和`g`,它们的 UTF-16 代码单元和 UTF-8 完全相同(因为这些 Unicode 标量表示 ASCII 字符)。
|
||||
第四个代码单元值 (8252) 是一个等于十六进制203C的的十进制值。这个代表了`DOUBLE EXCLAMATION MARK`字符的 Unicode 标量值`U+203C`。这个字符在 UTF-16 中可以用一个代码单元表示。
|
||||
第五和第六个代码单元值 (55357 和 56374) 是`DOG FACE`字符的UTF-16 表示。
|
||||
第一个值为`U+D83D`(十进制值为 55357),第二个值为`U+DC36`(十进制值为 56374)。
|
||||
|
||||
<a name="unicode_scalars_representation"></a>
|
||||
### Unicode 标量表示 (Unicode Scalars Representation)
|
||||
|
||||
您可以通过遍历字符串的`unicodeScalars`属性来访问它的 Unicode 标量表示。
|
||||
其为`UnicodeScalarView`类型的属性, `UnicodeScalarView`是`UnicodeScalar`的集合。
|
||||
`UnicodeScalar`是21位的 Unicode 代码点。
|
||||
|
||||
第一个值为`U+D83D`(十进制值为 55357),第二个值为`U+DC36`(十进制值为 56374)。
|
||||
|
||||
<a name="unicode_scalars_representation"></a>
|
||||
### Unicode 标量表示 (Unicode Scalars Representation)
|
||||
|
||||
您可以通过遍历字符串的`unicodeScalars`属性来访问它的 Unicode 标量表示。
|
||||
其为`UnicodeScalarView`类型的属性, `UnicodeScalarView`是`UnicodeScalar`的集合。
|
||||
`UnicodeScalar`是21位的 Unicode 代码点。
|
||||
|
||||
每一个`UnicodeScalar`拥有一个值属性,可以返回对应的21位数值,用`UInt32`来表示:
|
||||
|
||||
<body>
|
||||
@ -636,22 +640,22 @@ print("")
|
||||
</table>
|
||||
</center>
|
||||
</body>
|
||||
|
||||
|
||||
|
||||
|
||||
```swift
|
||||
for scalar in dogString.unicodeScalars {
|
||||
print("\(scalar.value) ", appendNewline: false)
|
||||
}
|
||||
print("")
|
||||
// 68 111 103 8252 128054
|
||||
```
|
||||
|
||||
// 68 111 103 8252 128054
|
||||
```
|
||||
|
||||
前三个代码单元值 (68, 111, 103) 仍然代表字符`D`、`o`和`g`。
|
||||
第四个代码单元值 (8252) 仍然是一个等于十六进制203C的的十进制值。这个代表了`DOUBLE EXCLAMATION MARK`字符的 Unicode 标量`U+203C`。
|
||||
第五位数值,128054,是一个十六进制1F436的十进制表示。其等同于`DOG FACE`的Unicode 标量`U+1F436`。
|
||||
|
||||
作为查询字符值属性的一种替代方法,每个`UnicodeScalar`值也可以用来构建一个新的字符串值,比如在字符串插值中使用:
|
||||
|
||||
第五位数值,128054,是一个十六进制1F436的十进制表示。其等同于`DOG FACE`的Unicode 标量`U+1F436`。
|
||||
|
||||
作为查询字符值属性的一种替代方法,每个`UnicodeScalar`值也可以用来构建一个新的字符串值,比如在字符串插值中使用:
|
||||
|
||||
```swift
|
||||
for scalar in dogString.unicodeScalars {
|
||||
print("\(scalar) ")
|
||||
@ -660,5 +664,5 @@ for scalar in dogString.unicodeScalars {
|
||||
// o
|
||||
// g
|
||||
// ‼
|
||||
// 🐶
|
||||
```
|
||||
// 🐶
|
||||
```
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
> 翻译:[zqp](https://github.com/zqp), [JackAlan](https://github.com/AlanMelody)
|
||||
> 校对:[shinyzhu](https://github.com/shinyzhu), [stanzhai](https://github.com/stanzhai), [feiin](https://github.com/feiin), [JackAlan](https://github.com/AlanMelody)
|
||||
> 1.0
|
||||
> 翻译:[zqp](https://github.com/zqp),
|
||||
> 校对:[shinyzhu](https://github.com/shinyzhu), [stanzhai](https://github.com/stanzhai), [feiin](https://github.com/feiin)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[JackAlan](https://github.com/AlanMelody)
|
||||
|
||||
# 集合类型 (Collection Types)
|
||||
-----------------
|
||||
@ -18,7 +22,7 @@ Swift 语言提供`Arrays`、`Sets`和`Dictionaries`三种基本的集合类型
|
||||
Swift 语言中的`Arrays`、`Sets`和`Dictionaries`中存储的数据值类型必须明确。这意味着我们不能把不正确的数据类型插入其中。同时这也说明我们完全可以对取回值的类型非常自信。
|
||||
|
||||
> 注意:
|
||||
Swift 的`Arrays`、`Sets`和`Dictionaries`类型被实现为泛型集合。更多关于泛型类型和集合,参见 [泛型](23_Generics.html)章节。
|
||||
Swift 的`Arrays`、`Sets`和`Dictionaries`类型被实现为泛型集合。更多关于泛型类型和集合,参见 [泛型](./23_Generics.html)章节。
|
||||
|
||||
<a name="mutability_of_collections"></a>
|
||||
## 集合的可变性
|
||||
@ -35,7 +39,7 @@ Swift 的`Arrays`、`Sets`和`Dictionaries`类型被实现为泛型集合。更
|
||||
|
||||
> 注意:
|
||||
Swift 的`Array`类型被桥接到`Foundation`中的`NSArray`类。
|
||||
更多关于在`Foundation`和`Cocoa`中使用`Array`的信息,参见 *Using Swift with Cocoa and Obejective-C* 一书。
|
||||
更多关于在`Foundation`和`Cocoa`中使用`Array`的信息,参见 [*Using Swift with Cocoa and Obejective-C*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 一书。
|
||||
|
||||
<a name="array_type_shorthand_syntax"></a>
|
||||
### 数组的简单语法
|
||||
@ -255,14 +259,14 @@ for (index, value) in shoppingList.enumerate() {
|
||||
|
||||
> 注意:
|
||||
> Swift的`Set`类型被桥接到`Fundation`中的`NSSet`类。
|
||||
> 关于使用`Fundation`和`Cocoa`中`Set`的知识,请看 *Using Swift with Cocoa and Objective-C*。
|
||||
> 关于使用`Fundation`和`Cocoa`中`Set`的知识,请看 [*Using Swift with Cocoa and Objective-C*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)。
|
||||
|
||||
<a name="hash_values_for_set_types"></a>
|
||||
#### Set类型的哈希值
|
||||
|
||||
为了存储在集合中,该类型必须是可哈希化的-也就是说,该类型必须提供一个方法来计算它的哈希值。一个哈希值是```Int```类型的,它和其他的对象相同,其被用来比较相等与否,比如```a==b```,它遵循的是```a.hashValue == b.hashValue```。
|
||||
|
||||
Swift 的所有基本类型(比如```String```,```Int```,```Double```和```Bool```)默认都是可哈希化的,它可以作为集合的值或者字典的键值类型。没有关联值的枚举成员值(在[枚举部分](08_Enumerations.html)有讲述)默认也是可哈希化的。
|
||||
Swift 的所有基本类型(比如```String```,```Int```,```Double```和```Bool```)默认都是可哈希化的,它可以作为集合的值或者字典的键值类型。没有关联值的枚举成员值(在[枚举](./08_Enumerations.html)有讲述)默认也是可哈希化的。
|
||||
|
||||
> 注意:
|
||||
> 你可以使用你自定义的类型作为集合的值或者是字典的键值类型,但你需要使你的自定义类型服从Swift标准库中的`Hashable`协议。服从`Hashable`协议的类型需要提供一个类型为`Int`的取值访问器属性`hashValue`。这个由类型的`hashValue`返回的值不需要在同一程序的不同执行周期或者不同程序之间保持相同。
|
||||
@ -272,7 +276,7 @@ Swift 的所有基本类型(比如```String```,```Int```,```Double```和```Bool`
|
||||
* ```a==b```意味着```b==a```(对称性)
|
||||
* ```a==b&&b==c```意味着```a==c```(传递性)
|
||||
|
||||
关于协议遵循的更多信息,请看[协议](22_Protocols.html)
|
||||
关于协议遵循的更多信息,请看[协议](./22_Protocols.html)
|
||||
|
||||
<a name="set_type_syntax"></a>
|
||||
### Set类型语法
|
||||
@ -393,7 +397,7 @@ for genre in favoriteGenres {
|
||||
// Hip hop
|
||||
```
|
||||
|
||||
更多关于`for-in`循环信息,参见[For循环](05_Control_Flow.html#for_loops)。
|
||||
更多关于`for-in`循环信息,参见[For循环](./05_Control_Flow.html#for_loops)。
|
||||
|
||||
Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中值可以使用`sort()`方法,它将根据提供的序列返回一个排序的集合.
|
||||
|
||||
@ -471,7 +475,7 @@ farmAnimals.isDisjointWith(cityAnimals)
|
||||
|
||||
> 注意:
|
||||
> Swiftly 的`Dictionary` 类型被桥接到Foundation的`NSDictionary`类。
|
||||
> 更多关于在`Foundation`和`Cocoa`中使用`Dictionary`类型的信息,参见 *Using Swift with Cocoa and Obejective-C* 一书。
|
||||
> 更多关于在`Foundation`和`Cocoa`中使用`Dictionary`类型的信息,参见 [*Using Swift with Cocoa and Obejective-C*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 一书。
|
||||
|
||||
<a name="dictionary_type_shorthand_syntax"></a>
|
||||
## 字典类型快捷语法
|
||||
@ -640,7 +644,7 @@ for (airportCode, airportName) in airports {
|
||||
// LHR: London Heathrow
|
||||
```
|
||||
|
||||
更多关于`for-in`循环的信息,参见[For 循环](05_Control_Flow.html#for_loops)。
|
||||
更多关于`for-in`循环的信息,参见[For 循环](./05_Control_Flow.html#for_loops)。
|
||||
|
||||
通过访问`keys`或者`values`属性,我们也可以遍历字典的键或者值。
|
||||
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
> 翻译:[vclwei](https://github.com/vclwei), [coverxit](https://github.com/coverxit), [NicePiao](https://github.com/NicePiao), [JackAlan](https://github.com/AlanMelody)
|
||||
> 校对:[coverxit](https://github.com/coverxit), [stanzhai](https://github.com/stanzhai), [JackAlan](https://github.com/AlanMelody)
|
||||
> 1.0
|
||||
> 翻译:[vclwei](https://github.com/vclwei), [coverxit](https://github.com/coverxit), [NicePiao](https://github.com/NicePiao)
|
||||
> 校对:[coverxit](https://github.com/coverxit), [stanzhai](https://github.com/stanzhai)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[JackAlan](https://github.com/AlanMelody)
|
||||
|
||||
# 控制流
|
||||
-----------------
|
||||
@ -87,7 +91,7 @@ for (animalName, legCount) in numberOfLegs {
|
||||
// spiders have 8 legs
|
||||
```
|
||||
|
||||
字典元素的遍历顺序和插入顺序可能不同,字典的内容在内部是无序的,所以遍历元素时不能保证顺序。关于数组和字典,详情参见[集合类型](04_Collection_Types.html)。
|
||||
字典元素的遍历顺序和插入顺序可能不同,字典的内容在内部是无序的,所以遍历元素时不能保证顺序。关于数组和字典,详情参见[集合类型](./04_Collection_Types.html)。
|
||||
|
||||
<a name="for"></a>
|
||||
|
||||
@ -529,8 +533,9 @@ case let (x, y):
|
||||
- `break`
|
||||
- `fallthrough`
|
||||
- `return`
|
||||
- `throw`
|
||||
|
||||
我们将会在下面讨论`continue`、`break`和`fallthrough`语句。`return`语句将会在[函数](06_Functions.html)章节讨论。
|
||||
我们将会在下面讨论`continue`、`break`和`fallthrough`语句。`return`语句将会在[函数](./06_Functions.html)章节讨论,`throw`语句会在[错误抛出](./18_Error_Handling.html#throwing_errors)
|
||||
|
||||
<a name="continue"></a>
|
||||
### Continue
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[honghaoz](https://github.com/honghaoz)
|
||||
> 校对:[LunaticM](https://github.com/LunaticM)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[dreamkidd](https://github.com/dreamkidd)
|
||||
|
||||
# 函数(Functions)
|
||||
-----------------
|
||||
|
||||
@ -116,6 +120,7 @@ print(sayHello("Tim", alreadyGreeted: true))
|
||||
|
||||
当调用超过一个参数的函数时,第一个参数后的参数根据其对应的参数名称标记,函数参数命名在[函数参数名称(Function Parameter Names)](#Function_Parameter_Names)有更详细的描述.
|
||||
|
||||
<a name="functions_without_return_values"></a>
|
||||
### 无返回值函数(Functions Without Return Values)
|
||||
|
||||
函数可以没有返回值。下面是 `sayHello(_:)` 函数的另一个版本,叫 `sayGoodbye(_:)`,这个函数直接输出 `String` 值,而不是返回它:
|
||||
@ -242,6 +247,7 @@ someFunction(1, secondParameterName: 2)
|
||||
|
||||
一般情况下,第一个参数省略其外部参数名,第二个以后的参数使用其本地参数名作为自己的外部参数名.所有参数需要有不同的本地参数名,但可以共享相同的外部参数名.
|
||||
|
||||
<a name="specifying_external_parameter_names"></a>
|
||||
### 指定外部参数名(Specifying External Parameter Names)
|
||||
|
||||
你可以在本地参数名前指定外部参数名,中间以逗号分隔.
|
||||
@ -373,6 +379,7 @@ let paddedString = alignRight(originalString, totalLength: 10, pad: "-")
|
||||
> 注意:
|
||||
> 对变量参数所进行的修改在函数调用结束后便消失了,并且对于函数体外是不可见的。变量参数仅仅存在于函数调用的生命周期中。
|
||||
|
||||
<a name="in_out_parameters"></a>
|
||||
### 输入输出参数(In-Out Parameters)
|
||||
|
||||
变量参数,正如上面所述,仅仅能在函数体内被更改。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为输入输出参数(In-Out Parameters)。
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[wh1100717](https://github.com/wh1100717)
|
||||
> 校对:[lyuka](https://github.com/lyuka)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[100mango](https://github.com/100mango)
|
||||
|
||||
# 闭包(Closures)
|
||||
-----------------
|
||||
|
||||
@ -20,7 +24,7 @@ Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他
|
||||
> 注意:
|
||||
> 如果您不熟悉捕获(capturing)这个概念也不用担心,您可以在 [值捕获](#capturing_values) 章节对其进行详细了解。
|
||||
|
||||
在[函数](../chapter2/06_Functions.html) 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
|
||||
在[函数](./06_Functions.html) 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
|
||||
|
||||
* 全局函数是一个有名字但不会捕获任何值的闭包
|
||||
* 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
|
||||
@ -37,7 +41,7 @@ Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进
|
||||
## 闭包表达式(Closure Expressions)
|
||||
|
||||
|
||||
[嵌套函数](../chapter2/06_Functions.html#nested_function) 是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候撰写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在您处理一些函数并需要将另外一些函数作为该函数的参数时。
|
||||
[嵌套函数](./06_Functions.html#nested_function) 是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候撰写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在您处理一些函数并需要将另外一些函数作为该函数的参数时。
|
||||
|
||||
闭包表达式是一种利用简洁语法构建内联闭包的方式。
|
||||
闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。
|
||||
@ -172,7 +176,7 @@ Swift 的`String`类型定义了关于大于号 (`>`) 的字符串实现,其
|
||||
reversed = names.sort(>)
|
||||
```
|
||||
|
||||
更多关于运算符表达式的内容请查看 [运算符函数](../chapter2/24_Advanced_Operators.html#operator_functions)。
|
||||
更多关于运算符表达式的内容请查看 [运算符函数](./24_Advanced_Operators.html#operator_functions)。
|
||||
|
||||
<a name="trailing_closures"></a>
|
||||
## 尾随闭包(Trailing Closures)
|
||||
@ -246,7 +250,7 @@ let strings = numbers.map {
|
||||
`map`在数组中为每一个元素调用了闭包表达式。
|
||||
您不需要指定闭包的输入参数`number`的类型,因为可以通过要映射的数组类型进行推断。
|
||||
|
||||
闭包`number`参数被声明为一个变量参数(变量的具体描述请参看[常量参数和变量参数](../chapter2/06_Functions.html#constant_and_variable_parameters)),因此可以在闭包函数体内对其进行修改。闭包表达式制定了返回类型为`String`,以表明存储映射值的新数组类型为`String`。
|
||||
闭包`number`参数被声明为一个变量参数(变量的具体描述请参看[常量参数和变量参数](./06_Functions.html#constant_and_variable_parameters)),因此可以在闭包函数体内对其进行修改。闭包表达式制定了返回类型为`String`,以表明存储映射值的新数组类型为`String`。
|
||||
|
||||
闭包表达式在每次被调用的时候创建了一个字符串并返回。
|
||||
其使用求余运算符 (number % 10) 计算最后一位数字并利用`digitNames`字典获取所映射的字符串。
|
||||
@ -296,7 +300,7 @@ func makeIncrementor(forIncrement amount: Int) -> () -> Int {
|
||||
`makeIncrementor`返回类型为`() -> Int`。
|
||||
这意味着其返回的是一个函数,而不是一个简单类型值。
|
||||
该函数在每次调用时不接受参数只返回一个`Int`类型的值。
|
||||
关于函数返回其他函数的内容,请查看[函数类型作为返回类型](../chapter2/06_Functions.html#function_types_as_return_types)。
|
||||
关于函数返回其他函数的内容,请查看[函数类型作为返回类型](./06_Functions.html#function_types_as_return_types)。
|
||||
|
||||
`makeIncrementor`函数定义了一个整型变量`runningTotal`(初始为0) 用来存储当前跑步总数。
|
||||
该值通过`incrementor`返回。
|
||||
@ -352,7 +356,7 @@ incrementByTen()
|
||||
|
||||
> 注意:
|
||||
> 如果您将闭包赋值给一个类实例的属性,并且该闭包通过指向该实例或其成员来捕获了该实例,您将创建一个在闭包和实例间的强引用环。
|
||||
> Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 [闭包引起的循环强引用](../chapter2/16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)。
|
||||
> Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 [闭包引起的循环强引用](./16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)。
|
||||
|
||||
<a name="closures_are_reference_types"></a>
|
||||
## 闭包是引用类型(Closures Are Reference Types)
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[yankuangshi](https://github.com/yankuangshi)
|
||||
> 校对:[shinyzhu](https://github.com/shinyzhu)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[futantan](https://github.com/futantan)
|
||||
|
||||
# 枚举(Enumerations)
|
||||
---
|
||||
|
||||
@ -20,7 +24,7 @@
|
||||
|
||||
在 Swift 中,枚举类型是一等公民(first-class)。它们采用了很多传统上只被类(class)所支持的特征,例如计算型属性(computed properties),用于提供关于枚举当前值的附加信息, 实例方法(instance methods),用于提供和枚举所代表的值相关联的功能。枚举也可以定义构造函数(initializers)来提供一个初始值;可以在原始的实现基础上扩展它们的功能;可以遵守协议(protocols)来提供标准的功能。
|
||||
|
||||
欲了解更多相关信息,请参见[属性(Properties)](10_Properties.html),[方法(Methods)](11_Methods.html),[构造过程(Initialization)](14_Initialization.html),[扩展(Extensions)](20_Extensions.html)和[协议(Protocols)](21_Protocols.html)。
|
||||
欲了解更多相关信息,请参见[属性(Properties)](./10_Properties.html),[方法(Methods)](./11_Methods.html),[构造过程(Initialization)](./14_Initialization.html),[扩展(Extensions)](./20_Extensions.html)和[协议(Protocols)](./21_Protocols.html)。
|
||||
|
||||
<a name="enumeration_syntax"></a>
|
||||
## 枚举语法
|
||||
@ -97,7 +101,7 @@ case .West:
|
||||
|
||||
等等以此类推。
|
||||
|
||||
正如在[控制流(Control Flow)](05_Control_Flow.html)中介绍的那样,在判断一个枚举类型的值时,`switch`语句必须穷举所有情况。如果忽略了`.West`这种情况,上面那段代码将无法通过编译,因为它没有考虑到`CompassPoint`的全部成员。强制性全部穷举的要求确保了枚举成员不会被意外遗漏。
|
||||
正如在[控制流(Control Flow)](./05_Control_Flow.html)中介绍的那样,在判断一个枚举类型的值时,`switch`语句必须穷举所有情况。如果忽略了`.West`这种情况,上面那段代码将无法通过编译,因为它没有考虑到`CompassPoint`的全部成员。强制性全部穷举的要求确保了枚举成员不会被意外遗漏。
|
||||
|
||||
当不需要匹配每个枚举成员的时候,你可以提供一个默认`default`分支来涵盖所有未明确被提出的枚举成员:
|
||||
|
||||
@ -187,7 +191,7 @@ case let .QRCode(productCode):
|
||||
<a name="raw_values"></a>
|
||||
## 原始值(Raw Values)
|
||||
|
||||
在[Associated Values](#raw_values)小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的相关值。作为相关值的另一种选择,枚举成员可以被默认值(称为原始值)赋值,其中这些原始值具有相同的类型。
|
||||
在[相关值](#raw_values)小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的相关值。作为相关值的另一种选择,枚举成员可以被默认值(称为原始值)赋值,其中这些原始值具有相同的类型。
|
||||
|
||||
这里是一个枚举成员存储 ASCII 码的例子:
|
||||
|
||||
@ -199,7 +203,7 @@ enum ASCIIControlCharacter: Character {
|
||||
}
|
||||
```
|
||||
|
||||
在这里,`ASCIIControlCharacter`的枚举类型的原始值类型被定义为字符型`Character`,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符[`Strings and Characters`](03_Strings_and_Characters.html)部分。
|
||||
在这里,`ASCIIControlCharacter`的枚举类型的原始值类型被定义为字符型`Character`,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见[字符串和字符](./03_Strings_and_Characters.html)部分。
|
||||
|
||||
|
||||
原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。
|
||||
@ -261,7 +265,7 @@ let possiblePlanet = Planet(rawValue: 7)
|
||||
|
||||
<!-- TODO 连接 -->
|
||||
>注意:
|
||||
>原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见[Failableinitializers](http://)
|
||||
>原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见[可失败构造器](../chapter3/05_Declarations#failable_initializers)
|
||||
|
||||
如果你试图寻找一个位置为9的行星,通过参数为`rawValue`构造函数返回的可选`Planet`值将是`nil`:
|
||||
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
> 翻译:[JaySurplus](https://github.com/JaySurplus),[SkyJean](https://github.com/SkyJean)
|
||||
> 校对:[sg552](https://github.com/sg552),[SkyJean](https://github.com/SkyJean)
|
||||
> 1.0
|
||||
> 翻译:[JaySurplus](https://github.com/JaySurplus)
|
||||
> 校对:[sg552](https://github.com/sg552)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[SkyJean](https://github.com/SkyJean)
|
||||
|
||||
# 类和结构体
|
||||
|
||||
@ -31,7 +35,7 @@ Swift 中类和结构体有很多共同点。共同处在于:
|
||||
* 通过扩展以增加默认实现的功能
|
||||
* 实现协议以提供某种标准功能
|
||||
|
||||
更多信息请参见 [属性](10_Properties.html),[方法](11_Methods.html),[下标脚本](12_Subscripts.html),[初始过程](14_Initialization.html),[扩展](20_Extensions.html),和[协议](21_Protocols.html)。
|
||||
更多信息请参见 [属性](./10_Properties.html),[方法](./11_Methods.html),[下标脚本](./12_Subscripts.html),[初始过程](./14_Initialization.html),[扩展](./20_Extensions.html),和[协议](./21_Protocols.html)。
|
||||
|
||||
与结构体相比,类还有如下的附加功能:
|
||||
|
||||
@ -40,7 +44,7 @@ Swift 中类和结构体有很多共同点。共同处在于:
|
||||
* 解构器允许一个类实例释放任何其所被分配的资源
|
||||
* 引用计数允许对一个类的多次引用
|
||||
|
||||
更多信息请参见[继承](13_Inheritance.html),[类型转换](20_Type_Casting.html),[析构过程](15_Deinitialization),和[自动引用计数](16_Automatic_Reference_Counting)。
|
||||
更多信息请参见[继承](./13_Inheritance.html),[类型转换](./20_Type_Casting.html),[析构过程](./15_Deinitialization),和[自动引用计数](./16_Automatic_Reference_Counting)。
|
||||
|
||||
> 注意:
|
||||
结构体总是通过被复制的方式在代码中传递,因此请不要使用引用计数。
|
||||
@ -91,7 +95,7 @@ let someResolution = Resolution()
|
||||
let someVideoMode = VideoMode()
|
||||
```
|
||||
|
||||
结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一对空括号,如`Resolution()`或`VideoMode()`。通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值。[构造过程](14_Initialization.html)章节会对类和结构体的初始化进行更详细的讨论。
|
||||
结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一对空括号,如`Resolution()`或`VideoMode()`。通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值。[构造过程](./14_Initialization.html)章节会对类和结构体的初始化进行更详细的讨论。
|
||||
|
||||
### 属性访问
|
||||
|
||||
@ -130,7 +134,7 @@ print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
|
||||
let vga = Resolution(width:640, height: 480)
|
||||
```
|
||||
|
||||
与结构体不同,类实例没有默认的成员逐一构造器。[构造过程](14_Initialization.html)章节会对构造器进行更详细的讨论。
|
||||
与结构体不同,类实例没有默认的成员逐一构造器。[构造过程](./14_Initialization.html)章节会对构造器进行更详细的讨论。
|
||||
|
||||
<a name="structures_and_enumerations_are_value_types"></a>
|
||||
## 结构体和枚举是值类型
|
||||
@ -249,7 +253,7 @@ if tenEighty === alsoTenEighty {
|
||||
* “等价于”表示两个类类型(class type)的常量或者变量引用同一个类实例。
|
||||
* “等于”表示两个实例的值“相等”或“相同”,判定时要遵照类设计者定义定义的评判标准,因此相比于“相等”,这是一种更加合适的叫法。
|
||||
|
||||
当你在定义你的自定义类和结构体的时候,你有义务来决定判定两个实例“相等”的标准。在章节[运算符函数(Operator Functions)](24_Advanced_Operators.html#operator_functions)中将会详细介绍实现自定义“等于”和“不等于”运算符的流程。
|
||||
当你在定义你的自定义类和结构体的时候,你有义务来决定判定两个实例“相等”的标准。在章节[等价操作符](./24_Advanced_Operators.html#equivalence_operators)中将会详细介绍实现自定义“等于”和“不等于”运算符的流程。
|
||||
|
||||
### 指针
|
||||
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[shinyzhu](https://github.com/shinyzhu)
|
||||
> 校对:[pp-prog](https://github.com/pp-prog) [yangsiy](https://github.com/yangsiy)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[yangsiy](https://github.com/yangsiy)
|
||||
|
||||
# 属性 (Properties)
|
||||
---
|
||||
|
||||
@ -23,7 +27,7 @@
|
||||
|
||||
简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字`var`定义),也可以是*常量存储属性*(用关键字`let`定义)。
|
||||
|
||||
可以在定义存储属性的时候指定默认值,请参考[构造过程](../chapter2/14_Initialization.html)一章的[默认属性值](../chapter2/14_Initialization.html#default_property_values)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[构造过程](../chapter2/14_Initialization.html)一章的[在初始化阶段修改常量存储属性](../chapter2/14_Initialization.html#modifying_constant_properties_during_initialization)一节。
|
||||
可以在定义存储属性的时候指定默认值,请参考[默认属性值](./14_Initialization.html#default_property_values)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[在初始化阶段修改常量存储属性](./14_Initialization.html#assigning_constant_properties_during_initialization)一节。
|
||||
|
||||
下面的例子定义了一个名为`FixedLengthRange`的结构体,它描述了一个在创建后无法修改值域宽度的区间:
|
||||
|
||||
@ -87,7 +91,7 @@ class DataManager {
|
||||
}
|
||||
|
||||
let manager = DataManager()
|
||||
manager.data.append("Some data")
|
||||
manager.data.append("Some data")
|
||||
manager.data.append("Some more data")
|
||||
// DataImporter 实例的 importer 属性还没有被创建
|
||||
```
|
||||
@ -104,9 +108,9 @@ manager.data.append("Some more data")
|
||||
print(manager.importer.fileName)
|
||||
// DataImporter 实例的 importer 属性现在被创建了
|
||||
// 输出 "data.txt”
|
||||
```
|
||||
|
||||
> 注意:
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 如果一个被标记为`lazy`的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。
|
||||
|
||||
<a name="stored_properties_and_instance_variables"></a>
|
||||
@ -219,7 +223,7 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
|
||||
|
||||
*属性观察器*监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。
|
||||
|
||||
可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重载请参考[继承](chapter/13_Inheritance.html)一章的[重载](chapter/13_Inheritance.html#overriding)。
|
||||
可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重载请参考[重载](./13_Inheritance.html#overriding)。
|
||||
|
||||
> 注意:
|
||||
> 不需要为非重载的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。
|
||||
@ -233,9 +237,9 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
|
||||
|
||||
类似地,`didSet`观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名`oldValue`。
|
||||
|
||||
> 注意:
|
||||
> 父类的属性在子类的构造器中被赋值时,它在父类中的`willSet`和`didSet`观察器会被调用。
|
||||
> 有关构造器代理的更多信息,请参考[值类型的构造器代理](chapter/14_Initialization.html#initializer_delegation_for_value_types)和[构造器链](chapter/14_Initialization.html#initialization_chain)。
|
||||
> 注意:
|
||||
> 父类的属性在子类的构造器中被赋值时,它在父类中的`willSet`和`didSet`观察器会被调用。
|
||||
> 有关构造器代理的更多信息,请参考[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)和[类的构造器代理规则](./14_Initialization.html#initializer_delegation_for_class_types)。
|
||||
|
||||
这里是一个`willSet`和`didSet`的实际例子,其中定义了一个名为`StepCounter`的类,用来统计当人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。
|
||||
|
||||
@ -322,13 +326,13 @@ enum SomeEnumeration {
|
||||
return 6
|
||||
}
|
||||
}
|
||||
class SomeClass {
|
||||
class SomeClass {
|
||||
static var storedTypeProperty = "Some value."
|
||||
static var computedTypeProperty: Int {
|
||||
return 27
|
||||
}
|
||||
class var overrideableComputedTypeProperty: Int {
|
||||
return 107
|
||||
}
|
||||
class var overrideableComputedTypeProperty: Int {
|
||||
return 107
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -341,12 +345,12 @@ class SomeClass {
|
||||
|
||||
跟实例的属性一样,类型属性的访问也是通过点运算符来进行。但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如:
|
||||
|
||||
```swift
|
||||
```swift
|
||||
print(SomeStructure.storedTypeProperty)
|
||||
// 输出 "Some value."
|
||||
// 输出 "Some value."
|
||||
SomeStructure.storedTypeProperty = "Another value."
|
||||
print(SomeStructure.storedTypeProperty)
|
||||
// 输出 "Another value.”
|
||||
// 输出 "Another value.”
|
||||
print(SomeEnumeration.computedTypeProperty)
|
||||
// 输出 "6"
|
||||
print(SomeClass.computedTypeProperty)
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[pp-prog](https://github.com/pp-prog)
|
||||
> 校对:[zqp](https://github.com/zqp)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[DianQK](https://github.com/DianQK)
|
||||
|
||||
# 方法(Methods)
|
||||
-----------------
|
||||
|
||||
@ -16,7 +20,7 @@
|
||||
<a name="instance_methods"></a>
|
||||
## 实例方法 (Instance Methods)
|
||||
|
||||
**实例方法**是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](../charpter2/06_Functions.md)。
|
||||
**实例方法**是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](./06_Functions.md)。
|
||||
|
||||
实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。
|
||||
|
||||
@ -60,7 +64,7 @@ class Counter {
|
||||
<a name="local_and_external_parameter"></a>
|
||||
### 方法的局部参数名称和外部参数名称(Local and External Parameter Names for Methods)
|
||||
|
||||
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[函数的外部参数名](06_Functions.html)。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。
|
||||
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[指定外部参数名](./06_Functions.html#specifying_external_parameter_names)。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。
|
||||
|
||||
Swift 中的方法和 Objective-C 中的方法极其相似。像在 Objective-C 中一样,Swift 中方法的名称通常用一个介词指向方法的第一个参数,比如:`with`,`for`,`by`等等。前面的`Counter`类的例子中`incrementBy(_:)`方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。
|
||||
|
||||
@ -85,8 +89,8 @@ counter.incrementBy(5, numberOfTimes: 3)
|
||||
// counter 的值现在是 15
|
||||
```
|
||||
|
||||
你不必为第一个参数值再定义一个外部变量名:因为从函数名`incrementBy(_numberOfTimes:)`已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。
|
||||
这种默认行为使上面代码意味着:在 Swift 中定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然表达式的方式被调用。
|
||||
你不必为第一个参数值再定义一个外部变量名:因为从函数名`incrementBy(_numberOfTimes:)`已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。
|
||||
这种默认行为使上面代码意味着:在 Swift 中定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然表达式的方式被调用。
|
||||
|
||||
|
||||
<a name="modifying_external_parameter_name_behavior_for_methods"></a>
|
||||
@ -95,7 +99,7 @@ counter.incrementBy(5, numberOfTimes: 3)
|
||||
有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个显式的外部名称或者用一个井号(`#`)作为第一个参数的前缀来把这个局部名称当作外部名称使用。
|
||||
|
||||
相反,如果你不想为方法的第二个及后续的参数提供一个外部名称,可以通过使用下划线(`_`)作为该参数的显式外部名称,这样做将覆盖默认行为。
|
||||
|
||||
|
||||
|
||||
<a name="the_self_property"></a>
|
||||
### self 属性(The self Property)
|
||||
@ -157,7 +161,7 @@ print("The point is now at (\(somePoint.x), \(somePoint.y))")
|
||||
|
||||
上面的`Point`结构体定义了一个变异方法(mutating method)`moveByX(_:y:)`用来移动点。`moveByX`方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上`mutating`关键字,这才让方法可以修改值类型的属性。
|
||||
|
||||
注意:不能在结构体类型常量上调用变异方法,因为常量的属性不能被改变,即使想改变的是常量的变量属性也不行,详情参见[存储属性和实例变量](10_Properties.html#global_and_local_variables):
|
||||
注意:不能在结构体类型常量上调用变异方法,因为常量的属性不能被改变,即使想改变的是常量的变量属性也不行,详情参见[存储属性和实例变量](./10_Properties.html#global_and_local_variables):
|
||||
|
||||
```swift
|
||||
let fixedPoint = Point(x: 3.0, y: 3.0)
|
||||
@ -234,23 +238,23 @@ SomeClass.someTypeMethod()
|
||||
游戏初始时,所有的游戏等级(除了等级 1)都被锁定。每次有玩家完成一个等级,这个等级就对这个设备上的所有玩家解锁。`LevelTracker`结构体用静态属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。
|
||||
|
||||
```swift
|
||||
struct LevelTracker {
|
||||
static var highestUnlockedLevel = 1
|
||||
static func unlockLevel(level: Int) {
|
||||
if level > highestUnlockedLevel { highestUnlockedLevel = level }
|
||||
}
|
||||
static func levelIsUnlocked(level: Int) -> Bool {
|
||||
return level <= highestUnlockedLevel
|
||||
}
|
||||
var currentLevel = 1
|
||||
mutating func advanceToLevel(level: Int) -> Bool {
|
||||
if LevelTracker.levelIsUnlocked(level) {
|
||||
currentLevel = level
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
struct LevelTracker {
|
||||
static var highestUnlockedLevel = 1
|
||||
static func unlockLevel(level: Int) {
|
||||
if level > highestUnlockedLevel { highestUnlockedLevel = level }
|
||||
}
|
||||
static func levelIsUnlocked(level: Int) -> Bool {
|
||||
return level <= highestUnlockedLevel
|
||||
}
|
||||
var currentLevel = 1
|
||||
mutating func advanceToLevel(level: Int) -> Bool {
|
||||
if LevelTracker.levelIsUnlocked(level) {
|
||||
currentLevel = level
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[siemenliu](https://github.com/siemenliu)
|
||||
> 校对:[zq54zquan](https://github.com/zq54zquan)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[shanksyang](https://github.com/shanksyang)
|
||||
|
||||
|
||||
# 下标脚本(Subscripts)
|
||||
-----------------
|
||||
@ -79,7 +83,7 @@ numberOfLegs["bird"] = 2
|
||||
|
||||
上例定义一个名为`numberOfLegs`的变量并用一个字典字面量初始化出了包含三对键值的字典实例。`numberOfLegs`的字典存放值类型推断为`[String:Int]`。字典实例创建完成之后通过下标脚本的方式将整型值`2`赋值到字典实例的索引为`bird`的位置中。
|
||||
|
||||
更多关于字典(Dictionary)下标脚本的信息请参考[读取和修改字典](../chapter2/04_Collection_Types.html)
|
||||
更多关于字典(Dictionary)下标脚本的信息请参考[读取和修改字典](./04_Collection_Types.html#accessing_and_modifying_a_dictionary)
|
||||
|
||||
> 注意:
|
||||
> Swift 中字典的附属脚本实现中,在`get`部分返回值是`Int?`,上例中的`numberOfLegs`字典通过附属脚本返回的是一个`Int?`或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是`nil`;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为`nil`即可。
|
||||
@ -118,7 +122,7 @@ struct Matrix {
|
||||
}
|
||||
```
|
||||
|
||||
`Matrix`提供了一个两个入参的构造方法,入参分别是`rows`和`columns`,创建了一个足够容纳`rows * columns`个数的`Double`类型数组。通过传入数组长度和初始值0.0到数组的一个构造器,将`Matrix`中每个元素初始值0.0。关于数组的构造方法和析构方法请参考[创建并且构造一个数组](../chapter2/04_Collection_Types.html)。
|
||||
`Matrix`提供了一个两个入参的构造方法,入参分别是`rows`和`columns`,创建了一个足够容纳`rows * columns`个数的`Double`类型数组。通过传入数组长度和初始值0.0到数组的一个构造器,将`Matrix`中每个元素初始值0.0。关于数组的构造方法和析构方法请参考[创建一个空数组](./04_Collection_Types.html#creating_an_empty_array)。
|
||||
|
||||
你可以通过传入合适的`row`和`column`的数量来构造一个新的`Matrix`实例:
|
||||
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[Hawstein](https://github.com/Hawstein)
|
||||
> 校对:[menlongsheng](https://github.com/menlongsheng)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[shanksyang](https://github.com/shanksyang)
|
||||
|
||||
# 继承(Inheritance)
|
||||
-------------------
|
||||
|
||||
@ -25,34 +29,34 @@
|
||||
> 注意:
|
||||
Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。
|
||||
|
||||
下面的例子定义了一个叫`Vehicle`的基类。这个基类声明了一个名为`currentSpeed `,默认值是0.0的存储属性(属性类型推断为`Double `)。`currentSpeed `属性的值被一个`String` 类型的只读计算型属性`description`使用,用来创建车辆的描述。
|
||||
|
||||
下面的例子定义了一个叫`Vehicle`的基类。这个基类声明了一个名为`currentSpeed `,默认值是0.0的存储属性(属性类型推断为`Double `)。`currentSpeed `属性的值被一个`String` 类型的只读计算型属性`description`使用,用来创建车辆的描述。
|
||||
|
||||
`Vehicle`基类也定义了一个名为`makeNoise`的方法。这个方法实际上不为`Vehicle`实例做任何事,但之后将会被`Vehicle`的子类定制:
|
||||
|
||||
```swift
|
||||
class Vehicle {
|
||||
var currentSpeed = 0.0
|
||||
var description: String {
|
||||
return "traveling at \(currentSpeed) miles per hour"
|
||||
}
|
||||
func makeNoise() {
|
||||
// 什么也不做-因为车辆不一定会有噪音
|
||||
}
|
||||
class Vehicle {
|
||||
var currentSpeed = 0.0
|
||||
var description: String {
|
||||
return "traveling at \(currentSpeed) miles per hour"
|
||||
}
|
||||
func makeNoise() {
|
||||
// 什么也不做-因为车辆不一定会有噪音
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
您可以用初始化语法创建一个`Vehicle `的新实例,即类名后面跟一个空括号:
|
||||
```swift
|
||||
let someVehicle = Vehicle()
|
||||
```
|
||||
|
||||
现在已经创建了一个`Vehicle`的新实例,你可以访问它的`description`属性来打印车辆的当前速度。
|
||||
|
||||
```swift
|
||||
print("Vehicle: \(someVehicle.description)")
|
||||
// Vehicle: traveling at 0.0 miles per hour
|
||||
```
|
||||
|
||||
|
||||
您可以用初始化语法创建一个`Vehicle `的新实例,即类名后面跟一个空括号:
|
||||
```swift
|
||||
let someVehicle = Vehicle()
|
||||
```
|
||||
|
||||
现在已经创建了一个`Vehicle`的新实例,你可以访问它的`description`属性来打印车辆的当前速度。
|
||||
|
||||
```swift
|
||||
print("Vehicle: \(someVehicle.description)")
|
||||
// Vehicle: traveling at 0.0 miles per hour
|
||||
```
|
||||
|
||||
`Vehicle`类定义了一个通用特性的车辆类,实际上没什么用处。为了让它变得更加有用,需要改进它能够描述一个更加具体的车辆类。
|
||||
|
||||
<a name="subclassing"></a>
|
||||
@ -68,51 +72,51 @@ class SomeClass: SomeSuperclass {
|
||||
}
|
||||
```
|
||||
|
||||
下一个例子,定义一个叫`Bicycle`的子类,继承成父类`Vehicle`
|
||||
|
||||
```swift
|
||||
class Bicycle: Vehicle {
|
||||
var hasBasket = false
|
||||
}
|
||||
```
|
||||
下一个例子,定义一个叫`Bicycle`的子类,继承成父类`Vehicle`
|
||||
|
||||
新的`Bicycle`类自动获得`Vehicle`类的所有特性,比如 `currentSpeed `和`description`属性,还有它的`makeNoise`方法。
|
||||
|
||||
除了它所继承的特性,`Bicycle`类还定义了一个默认值为`false`的存储型属性`hasBasket`(属性推断为`Bool`)。
|
||||
|
||||
默认情况下,你创建任何新的`Bicycle`实例将不会有一个篮子,创建该实例之后,你可以为特定的`Bicycle`实例设置`hasBasket `属性为`ture`:
|
||||
|
||||
```swift
|
||||
let bicycle = Bicycle()
|
||||
bicycle.hasBasket = true
|
||||
```
|
||||
|
||||
你还可以修改`Bicycle `实例所继承的`currentSpeed `属性,和查询实例所继承的`description `属性:
|
||||
|
||||
```swift
|
||||
bicycle.currentSpeed = 15.0
|
||||
print("Bicycle: \(bicycle.description)")
|
||||
// Bicycle: traveling at 15.0 miles per hour
|
||||
```swift
|
||||
class Bicycle: Vehicle {
|
||||
var hasBasket = false
|
||||
}
|
||||
```
|
||||
|
||||
新的`Bicycle`类自动获得`Vehicle`类的所有特性,比如 `currentSpeed `和`description`属性,还有它的`makeNoise`方法。
|
||||
|
||||
除了它所继承的特性,`Bicycle`类还定义了一个默认值为`false`的存储型属性`hasBasket`(属性推断为`Bool`)。
|
||||
|
||||
默认情况下,你创建任何新的`Bicycle`实例将不会有一个篮子,创建该实例之后,你可以为特定的`Bicycle`实例设置`hasBasket `属性为`ture`:
|
||||
|
||||
```swift
|
||||
let bicycle = Bicycle()
|
||||
bicycle.hasBasket = true
|
||||
```
|
||||
|
||||
你还可以修改`Bicycle `实例所继承的`currentSpeed `属性,和查询实例所继承的`description `属性:
|
||||
|
||||
```swift
|
||||
bicycle.currentSpeed = 15.0
|
||||
print("Bicycle: \(bicycle.description)")
|
||||
// Bicycle: traveling at 15.0 miles per hour
|
||||
```
|
||||
|
||||
子类还可以继续被其它类继承,下面的示例为`Bicycle `创建了一个名为`Tandem `(双人自行车)的子类:
|
||||
|
||||
```swift
|
||||
class Tandem: Bicycle {
|
||||
var currentNumberOfPassengers = 0
|
||||
class Tandem: Bicycle {
|
||||
var currentNumberOfPassengers = 0
|
||||
}
|
||||
```
|
||||
|
||||
`Tandem`从`Bicycle`继承了所有的属性与方法,这又使它同时继承了`Vehicle`的所有属性与方法。`Tandem`也增加了一个新的叫做`currentNumberOfPassengers`的存储型属性,默认值为0。
|
||||
|
||||
`Tandem`从`Bicycle`继承了所有的属性与方法,这又使它同时继承了`Vehicle`的所有属性与方法。`Tandem`也增加了一个新的叫做`currentNumberOfPassengers`的存储型属性,默认值为0。
|
||||
|
||||
如果你创建了一个`Tandem`的实例,你可以使用它所有的新属性和继承的属性,还能查询从`Vehicle`继承来的只读属性`description `:
|
||||
|
||||
```swift
|
||||
let tandem = Tandem()
|
||||
tandem.hasBasket = true
|
||||
tandem.currentNumberOfPassengers = 2
|
||||
tandem.currentSpeed = 22.0
|
||||
print("Tandem: \(tandem.description)")
|
||||
let tandem = Tandem()
|
||||
tandem.hasBasket = true
|
||||
tandem.currentNumberOfPassengers = 2
|
||||
tandem.currentSpeed = 22.0
|
||||
print("Tandem: \(tandem.description)")
|
||||
// Tandem: traveling at 22.0 miles per hour
|
||||
```
|
||||
|
||||
@ -142,18 +146,18 @@ print("Tandem: \(tandem.description)")
|
||||
下面的例子定义了`Vehicle`的一个新的子类,叫`Train `,它重写了从`Vehicle`类继承来的`makeNoise `方法:
|
||||
|
||||
```swift
|
||||
class Train: Vehicle {
|
||||
override func makeNoise() {
|
||||
print("Choo Choo")
|
||||
}
|
||||
class Train: Vehicle {
|
||||
override func makeNoise() {
|
||||
print("Choo Choo")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如果你创建一个`Train `的新实例,并调用了它的`makeNoise `方法,你就会发现`Train `版本的方法被调用:
|
||||
|
||||
```swift
|
||||
let train = Train()
|
||||
train.makeNoise()
|
||||
let train = Train()
|
||||
train.makeNoise()
|
||||
// prints "Choo Choo"
|
||||
```
|
||||
|
||||
@ -173,23 +177,23 @@ train.makeNoise()
|
||||
以下的例子定义了一个新类,叫`Car`,它是`Vehicle `的子类。这个类引入了一个新的存储型属性叫做`gear `,默认为整数1。`Car`类重写了继承自`Vehicle `的description属性,提供自定义的,包含当前档位的描述:
|
||||
|
||||
```swift
|
||||
class Car: Vehicle {
|
||||
var gear = 1
|
||||
override var description: String {
|
||||
return super.description + " in gear \(gear)"
|
||||
}
|
||||
class Car: Vehicle {
|
||||
var gear = 1
|
||||
override var description: String {
|
||||
return super.description + " in gear \(gear)"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
重写的`description `属性,首先要调用`super.description`返回`Vehicle`类的`description`属性。之后,`Car `类版本的`description`在末尾增加了一些额外的文本来提供关于当前档位的信息。
|
||||
|
||||
```
|
||||
|
||||
重写的`description `属性,首先要调用`super.description`返回`Vehicle`类的`description`属性。之后,`Car `类版本的`description`在末尾增加了一些额外的文本来提供关于当前档位的信息。
|
||||
|
||||
如果你创建了`Car `的实例并且设置了它的`gear`和`currentSpeed`属性,你可以看到它的`description`返回了`Car`中定义的`description`:
|
||||
|
||||
```swift
|
||||
let car = Car()
|
||||
car.currentSpeed = 25.0
|
||||
car.gear = 3
|
||||
print("Car: \(car.description)")
|
||||
let car = Car()
|
||||
car.currentSpeed = 25.0
|
||||
car.gear = 3
|
||||
print("Car: \(car.description)")
|
||||
// Car: traveling at 25.0 miles per hour in gear 3
|
||||
```
|
||||
|
||||
@ -203,21 +207,21 @@ print("Car: \(car.description)")
|
||||
下面的例子定义了一个新类叫`AutomaticCar`,它是`Car`的子类。`AutomaticCar`表示自动挡汽车,它可以根据当前的速度自动选择合适的挡位:
|
||||
|
||||
```swift
|
||||
class AutomaticCar: Car {
|
||||
override var currentSpeed: Double {
|
||||
didSet {
|
||||
gear = Int(currentSpeed / 10.0) + 1
|
||||
}
|
||||
}
|
||||
class AutomaticCar: Car {
|
||||
override var currentSpeed: Double {
|
||||
didSet {
|
||||
gear = Int(currentSpeed / 10.0) + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
当你设置`AutomaticCar`的`currentSpeed `属性,属性的`didSet`观察器就会自动地设置`gear`属性,为新的速度选择一个合适的挡位。具体来说就是,属性观察器将新的速度值除以10,然后向下取得最接近的整数值,最后加1来得到档位`gear`的值。例如,速度为10.0时,挡位为1;速度为35.0时,挡位为4:
|
||||
|
||||
```swift
|
||||
let automatic = AutomaticCar()
|
||||
automatic.currentSpeed = 35.0
|
||||
print("AutomaticCar: \(automatic.description)")
|
||||
let automatic = AutomaticCar()
|
||||
automatic.currentSpeed = 35.0
|
||||
print("AutomaticCar: \(automatic.description)")
|
||||
// AutomaticCar: traveling at 35.0 miles per hour in gear 4
|
||||
```
|
||||
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
> 1.0
|
||||
> 翻译:[lifedim](https://github.com/lifedim)
|
||||
> 校对:[lifedim](https://github.com/lifedim),[chenmingbiao](https://github.com/chenmingbiao)
|
||||
> 校对:[lifedim](https://github.com/lifedim)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[chenmingbiao](https://github.com/chenmingbiao)
|
||||
|
||||
# 构造过程(Initialization)
|
||||
|
||||
@ -21,7 +25,7 @@
|
||||
|
||||
构造过程是通过定义构造器(`Initializers`)来实现的,这些构造器可以看做是用来创建特定类型实例的特殊方法。与 Objective-C 中的构造器不同,Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。
|
||||
|
||||
类的实例也可以通过定义析构器(`deinitializer`)在实例释放之前执行特定的清除工作。想了解更多关于析构器的内容,请参考[析构过程](15_Deinitialization.html)。
|
||||
类的实例也可以通过定义析构器(`deinitializer`)在实例释放之前执行特定的清除工作。想了解更多关于析构器的内容,请参考[析构过程](./15_Deinitialization.html)。
|
||||
|
||||
<a name="setting_initial_values_for_stored_properties"></a>
|
||||
## 存储型属性的初始赋值
|
||||
@ -193,6 +197,7 @@ cheeseQuestion.response = "Yes, I do like cheese."
|
||||
|
||||
调查问题在问题提出之后,我们才能得到回答。所以我们将属性回答`response`声明为`String?`类型,或者说是可选字符串类型`optional String`。当`SurveyQuestion`实例化时,它将自动赋值为空`nil`,表明暂时还不存在此字符串。
|
||||
|
||||
<a name="assigning_constant_properties_during_initialization"></a>
|
||||
### 构造过程中常量属性的修改
|
||||
|
||||
只要在构造过程结束前常量的值能确定,你可以在构造过程中的任意时间点修改常量属性的值。
|
||||
@ -237,6 +242,7 @@ var item = ShoppingListItem()
|
||||
|
||||
由于`ShoppingListItem`类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器(尽管代码中没有显式为`name`属性设置默认值,但由于`name`是可选字符串类型,它将默认设置为`nil`)。上面例子中使用默认构造器创造了一个`ShoppingListItem`类的实例(使用`ShoppingListItem()`形式的构造器语法),并将其赋值给变量`item`。
|
||||
|
||||
<a name="memberwise_initializers_for_structure_types"></a>
|
||||
### 结构体的逐一成员构造器
|
||||
|
||||
除上面提到的默认构造器,如果结构体对所有存储型属性提供了默认值且自身没有提供定制的构造器,它们能自动获得一个逐一成员构造器。
|
||||
@ -259,14 +265,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`。
|
||||
|
||||
如果你为某个值类型定义了一个定制的构造器,你将无法访问到默认构造器(如果是结构体,则无法访问逐一对象构造器)。这个限制可以防止你在为值类型定义了一个更复杂的,完成了重要准备构造器之后,别人还是错误的使用了那个自动生成的构造器。
|
||||
|
||||
>注意:
|
||||
假如你想通过默认构造器、逐一对象构造器以及你自己定制的构造器为值类型创建实例,我们建议你将自己定制的构造器写到扩展(`extension`)中,而不是跟值类型定义混在一起。想查看更多内容,请查看[扩展](20_Extensions.html)章节。
|
||||
假如你想通过默认构造器、逐一对象构造器以及你自己定制的构造器为值类型创建实例,我们建议你将自己定制的构造器写到扩展(`extension`)中,而不是跟值类型定义混在一起。想查看更多内容,请查看[扩展](./20_Extensions.html)章节。
|
||||
|
||||
下面例子将定义一个结构体`Rect`,用来代表几何矩形。这个例子需要两个辅助的结构体`Size`和`Point`,它们各自为其所有的属性提供了初始值`0.0`。
|
||||
|
||||
@ -324,7 +330,7 @@ let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
|
||||
构造器`init(center:size:)`可以自己将`origin`和`size`的新值赋值到对应的属性中。然而尽量利用现有的构造器和它所提供的功能来实现`init(center:size:)`的功能,是更方便、更清晰和更直观的方法。
|
||||
|
||||
>注意:
|
||||
如果你想用另外一种不需要自己定义`init()`和`init(origin:size:)`的方式来实现这个例子,请参考[扩展](20_Extensions.html)。
|
||||
如果你想用另外一种不需要自己定义`init()`和`init(origin:size:)`的方式来实现这个例子,请参考[扩展](./20_Extensions.html)。
|
||||
|
||||
<a name="class_inheritance_and_initialization"></a>
|
||||
## 类的继承和构造过程
|
||||
@ -361,8 +367,8 @@ convenience init(parameters) {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="initialization_chain"></a>
|
||||
### 构造器链
|
||||
<a name="initializer_delegation_for_class_types"></a>
|
||||
### 类的构造器代理规则
|
||||
|
||||
为了简化指定构造器和便利构造器之间的调用关系,Swift 采用以下三条规则来限制构造器之间的代理调用:
|
||||
|
||||
@ -465,6 +471,7 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
|
||||
|
||||
最终,一旦子类的指定构造器完成调用,最开始被调用的便利构造器可以执行更多的定制操作。
|
||||
|
||||
<a name="initializer_inheritance_and_overriding"></a>
|
||||
### 构造器的继承和重载
|
||||
|
||||
跟 Objective-C 中的子类不同,Swift 中的子类不会默认继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更专业的子类继承,并被错误的用来创建子类的实例。
|
||||
@ -609,7 +616,7 @@ class RecipeIngredient: Food {
|
||||
|
||||
`RecipeIngredient`也定义了一个便利构造器`init(name: String)`,它只通过`name`来创建`RecipeIngredient`的实例。这个便利构造器假设任意`RecipeIngredient`实例的`quantity`为1,所以不需要显示指明数量即可创建出实例。这个便利构造器的定义可以让创建实例更加方便和快捷,并且避免了使用重复的代码来创建多个`quantity`为 1 的`RecipeIngredient`实例。这个便利构造器只是简单的将任务代理给了同一类里提供的指定构造器。
|
||||
|
||||
注意,`RecipeIngredient`的便利构造器`init(name: String)`使用了跟`Food`中指定构造器`init(name: String)`相同的参数。因为这个便利构造器重写要父类的指定构造器`init(name: String)`,必须在前面使用使用`override`标识。
|
||||
注意,`RecipeIngredient`的便利构造器`init(name: String)`使用了跟`Food`中指定构造器`init(name: String)`相同的参数。因为这个便利构造器重写要父类的指定构造器`init(name: String)`,必须在前面使用使用`override`标识(参见[构造器的继承和重载](#initializer_inheritance_and_overriding))。
|
||||
|
||||
在这个例子中,`RecipeIngredient`的父类是`Food`,它有一个便利构造器`init()`。这个构造器因此也被`RecipeIngredient`继承。这个继承的`init()`函数版本跟`Food`提供的版本是一样的,除了它是将任务代理给`RecipeIngredient`版本的`init(name: String)`而不是`Food`提供的版本。
|
||||
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
> 1.0
|
||||
> 翻译:[bruce0505](https://github.com/bruce0505)
|
||||
> 校对:[fd5788](https://github.com/fd5788),[chenmingbiao](https://github.com/chenmingbiao)
|
||||
> 校对:[fd5788](https://github.com/fd5788)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[chenmingbiao](https://github.com/chenmingbiao)
|
||||
|
||||
# 析构过程(Deinitialization)
|
||||
---------------------------
|
||||
@ -14,7 +18,7 @@
|
||||
<a name="how_deinitialization_works"></a>
|
||||
##析构过程原理
|
||||
|
||||
Swift 会自动释放不再需要的实例以释放资源。如[自动引用计数](16_Automatic_Reference_Counting.html)章节中所讲述,Swift 通过`自动引用计数(ARC)`处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件。
|
||||
Swift 会自动释放不再需要的实例以释放资源。如[自动引用计数](./16_Automatic_Reference_Counting.html)章节中所讲述,Swift 通过`自动引用计数(ARC)`处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件。
|
||||
|
||||
在类的定义中,每个类最多只能有一个析构器,而且析构器不带任何参数,如下所示:
|
||||
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[TimothyYe](https://github.com/TimothyYe)
|
||||
> 校对:[Hawstein](https://github.com/Hawstein)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[Channe](https://github.com/Channe)
|
||||
|
||||
# 自动引用计数
|
||||
-----------------
|
||||
|
||||
@ -23,8 +27,8 @@ Swift 使用自动引用计数(ARC)机制来跟踪和管理你的应用程
|
||||
<a name="how_arc_works"></a>
|
||||
## 自动引用计数的工作机制
|
||||
|
||||
当你每次创建一个类的新的实例的时候,ARC 会分配一大块内存用来储存实例的信息。内存中会包含实例的类型信息,以及这个实例所有相关属性的值。
|
||||
|
||||
当你每次创建一个类的新的实例的时候,ARC 会分配一大块内存用来储存实例的信息。内存中会包含实例的类型信息,以及这个实例所有相关属性的值。
|
||||
|
||||
此外,当实例不再被使用时,ARC 释放实例所占用的内存,并让释放的内存能挪作他用。这确保了不再被使用的实例,不会一直占用内存空间。
|
||||
|
||||
然而,当 ARC 收回和释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你试图访问这个实例,你的应用程序很可能会崩溃。
|
||||
@ -86,8 +90,8 @@ reference3 = reference1
|
||||
```swift
|
||||
reference1 = nil
|
||||
reference2 = nil
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
在你清楚地表明不再使用这个`Person`实例时,即第三个也就是最后一个强引用被断开时,ARC 会销毁它。
|
||||
|
||||
```swift
|
||||
@ -158,7 +162,7 @@ number73!.tenant = john
|
||||
在将两个实例联系在一起之后,强引用的关系如图所示:
|
||||
|
||||

|
||||
|
||||
|
||||
不幸的是,这两个实例关联后会产生一个循环强引用。`Person`实例现在有了一个指向`Apartment`实例的强引用,而`Apartment`实例也有了一个指向`Person`实例的强引用。因此,当你断开`john`和`number73`变量所持有的强引用时,引用计数并不会降为 0,实例也不会被 ARC 销毁:
|
||||
|
||||
```swift
|
||||
@ -184,7 +188,7 @@ Swift 提供了两种办法用来解决你在使用类的属性时所遇到的
|
||||
对于生命周期中会变为`nil`的实例使用弱引用。相反地,对于初始化赋值后再也不会被赋值为`nil`的实例,使用无主引用。
|
||||
|
||||
### 弱引用
|
||||
|
||||
|
||||
弱引用不会对其引用的实例保持强引用,因而不会阻止 ARC 销毁被引用的实例。这个特性阻止了引用变为循环强引用。声明属性或者变量时,在前面加上`weak`关键字表明这是一个弱引用。
|
||||
|
||||
在实例的生命周期中,如果某些时候引用没有值,那么弱引用可以避免循环强引用。如果引用总是有值,则可以使用无主引用,在[无主引用](#2)中有描述。在上面`Apartment`的例子中,一个公寓的生命周期中,有时是没有“居民”的,因此适合使用弱引用来解决循环强引用。
|
||||
@ -297,9 +301,9 @@ class CreditCard {
|
||||
}
|
||||
deinit { print("Card #\(number) is being deinitialized") }
|
||||
}
|
||||
```
|
||||
|
||||
> 注意:
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> `CreditCard`类的`number`属性被定义为`UInt64`类型而不是`Int`类型,以确保`number`属性的存储量在32位和64位系统上都能足够容纳16位的卡号。
|
||||
|
||||
下面的代码片段定义了一个叫`john`的可选类型`Customer`变量,用来保存某个特定客户的引用。由于是可选类型,所以变量被初始化为`nil`。
|
||||
@ -335,6 +339,8 @@ john = nil
|
||||
|
||||
最后的代码展示了在`john`变量被设为`nil`后`Customer`实例和`CreditCard`实例的构造函数都打印出了“销毁”的信息。
|
||||
|
||||
|
||||
<a name="unowned_references_and_implicitly_unwrapped_optional_properties"></a>
|
||||
###无主引用以及隐式解析可选属性
|
||||
|
||||
上面弱引用和无主引用的例子涵盖了两种常用的需要打破循环强引用的场景。
|
||||
@ -373,9 +379,9 @@ class City {
|
||||
|
||||
为了建立两个类的依赖关系,`City`的构造函数有一个`Country`实例的参数,并且将实例保存为`country`属性。
|
||||
|
||||
`Country`的构造函数调用了`City`的构造函数。然而,只有`Country`的实例完全初始化完后,`Country`的构造函数才能把`self`传给`City`的构造函数。([在两段式构造过程中有具体描述](14_Initialization.html))
|
||||
`Country`的构造函数调用了`City`的构造函数。然而,只有`Country`的实例完全初始化完后,`Country`的构造函数才能把`self`传给`City`的构造函数。(在[两段式构造过程](./14_Initialization.html#two_phase_initialization)中有具体描述)
|
||||
|
||||
为了满足这种需求,通过在类型结尾处加上感叹号(`City!`)的方式,将`Country`的`capitalCity`属性声明为隐式解析可选类型的属性。这表示像其他可选类型一样,`capitalCity`属性的默认值为`nil`,但是不需要展开它的值就能访问它。([在隐式解析可选类型中有描述](01_The_Basics.html))
|
||||
为了满足这种需求,通过在类型结尾处加上感叹号(`City!`)的方式,将`Country`的`capitalCity`属性声明为隐式解析可选类型的属性。这表示像其他可选类型一样,`capitalCity`属性的默认值为`nil`,但是不需要展开它的值就能访问它。(在[隐式解析可选类型](./01_The_Basics.html#implicityly_unwrapped_optionals)中有描述)
|
||||
|
||||
由于`capitalCity`默认值为`nil`,一旦`Country`的实例在构造函数中给`name`属性赋值后,整个初始化过程就完成了。这代表一旦`name`属性被赋值后,`Country`的构造函数就能引用并传递隐式的`self`。`Country`的构造函数在赋值`capitalCity`时,就能将`self`作为参数传递给`City`的构造函数。
|
||||
|
||||
@ -456,7 +462,7 @@ print(paragraph!.asHTML())
|
||||
|
||||

|
||||
|
||||
实例的`asHTML`属性持有闭包的强引用。但是,闭包在其闭包体内使用了`self`(引用了`self.name`和`self.text`),因此闭包捕获了`self`,这意味着闭包又反过来持有了`HTMLElement`实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考[值捕获](07_Closures.html))。
|
||||
实例的`asHTML`属性持有闭包的强引用。但是,闭包在其闭包体内使用了`self`(引用了`self.name`和`self.text`),因此闭包捕获了`self`,这意味着闭包又反过来持有了`HTMLElement`实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考[值捕获](./07_Closures.html#capturing_values))。
|
||||
|
||||
>注意:
|
||||
虽然闭包多次使用了`self`,它只捕获`HTMLElement`实例的一个强引用。
|
||||
@ -477,9 +483,9 @@ paragraph = nil
|
||||
>注意:
|
||||
Swift 有如下要求:只要在闭包内使用`self`的成员,就要用`self.someProperty`或者`self.someMethod`(而不只是`someProperty`或`someMethod`)。这提醒你可能会一不小心就捕获了`self`。
|
||||
|
||||
###定义捕获列表
|
||||
###定义捕获列表
|
||||
|
||||
捕获列表中的每一项都由一对元素组成,一个元素是`weak`或`unowned`关键字,另一个元素是类实例的引用(如`self`)或初始化过的变量(如`delegate = self.delegate!`)。这些项在方括号中用逗号分开。
|
||||
捕获列表中的每一项都由一对元素组成,一个元素是`weak`或`unowned`关键字,另一个元素是类实例的引用(如`self`)或初始化过的变量(如`delegate = self.delegate!`)。这些项在方括号中用逗号分开。
|
||||
|
||||
如果闭包有参数列表和返回类型,把捕获列表放在它们前面:
|
||||
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
# Optional Chaining
|
||||
# 可空链式调用
|
||||
|
||||
> 1.0
|
||||
> 翻译:[Jasonbroker](https://github.com/Jasonbroker)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[lyojo](https://github.com/lyojo)
|
||||
|
||||
-----------------
|
||||
可空链式调用(Optional Chaining)是一种可以请求和调用属性、方法及下标的过程,它的可空性体现于请求或调用的目标当前可能为空(nil)。如果可空的目标有值,那么调用就会成功;如果选择的目标为空(nil),那么这种调用将返回空(nil)。多个连续的调用可以被链接在一起形成一个调用链,如果其中任何一个节点为空(nil)将导致整个链调用失败。
|
||||
|
||||
@ -10,166 +14,208 @@
|
||||
注意:
|
||||
Swift 的可空链式调用和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且能够检查调用是否成功。
|
||||
|
||||
##可空链式调用作为强制展开的另一个方案
|
||||
<a name="optional_chaining_as_an_alternative_to_forced_unwrapping"></a>
|
||||
##使用可空链式调用来强制展开
|
||||
通过在想调用非空的属性、方法、或下标的可空值(optional value)后面放一个问号,可以定义一个可空链。这一点很像在可空值后面放一个叹号(!)来强制展开其中值。它们的主要的区别在于当可空值为空时可空链式只是调用失败,然而强制展开将会触发运行时错误。
|
||||
|
||||
为了反映可空链式调用可以在空对象(nil)上调用,不论这个调用的属性、方法、下标等返回的值是不是可空值,它的返回结果都是一个可空值。你可以利用这个返回值来判断你的可空链式调用是否调用成功,如果调用有返回值则说明调用成功,返回nil则说明调用失败。
|
||||
为了反映可空链式调用可以在空对象(nil)上调用,不论这个调用的属性、方法、下标等返回的值是不是可空值,它的返回结果都是一个可空值。你可以利用这个返回值来判断你的可空链式调用是否调用成功,如果调用有返回值则说明调用成功,返回`nil`则说明调用失败。
|
||||
|
||||
特别地,可空链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了一个可空类型值。当可空链式调用成功时,一个本应该返回Int的类型的结果将会返回Int?类型。
|
||||
特别地,可空链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了一个可空类型值。当可空链式调用成功时,一个本应该返回`Int`的类型的结果将会返回`Int?`类型。
|
||||
|
||||
下面几段代码将解释可空链式调用和强制展开的不同。
|
||||
首先定义两个类Person和Residence。
|
||||
首先定义两个类`Person`和`Residence`。
|
||||
|
||||
class Person {
|
||||
```swift
|
||||
class Person {
|
||||
var residence: Residence?
|
||||
}
|
||||
class Residence {
|
||||
}
|
||||
|
||||
class Residence {
|
||||
var numberOfRooms = 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Residence有一个Int类型的属性numberOfRooms,其默认值为1。Person具有一个可空的residence属性,其类型为Residence?。
|
||||
`Residence`有一个`Int`类型的属性`numberOfRooms`,其默认值为1。`Person`具有一个可空的`residence`属性,其类型为`Residence?`。
|
||||
|
||||
如果创建一个新的Person实例,因为它的residence属性是可空的,john属性将初始化为nil:
|
||||
如果创建一个新的`Person`实例,因为它的`residence`属性是可空的,`john`属性将初始化为`nil`:
|
||||
|
||||
let john = Person()
|
||||
```swift
|
||||
let john = Person()
|
||||
```
|
||||
|
||||
如果使用叹号(!)强制展开获得这个john的residence属性中的numberOfRooms值,会触发运行时错误,因为这时没有可以展开的residence:
|
||||
如果使用叹号(!)强制展开获得这个`john`的`residence`属性中的`numberOfRooms`值,会触发运行时错误,因为这时没有可以展开的`residence`:
|
||||
|
||||
let roomCount = john.residence!.numberOfRooms
|
||||
// this triggers a runtime error
|
||||
```swift
|
||||
let roomCount = john.residence!.numberOfRooms
|
||||
// this triggers a runtime error
|
||||
```
|
||||
|
||||
john.residence非空的时候,上面的调用成功,并且把roomCount设置为Int类型的房间数量。正如上面说到的,当residence为空的时候上面这段代码会触发运行时错误。
|
||||
`john.residence`非空的时候,上面的调用成功,并且把`roomCount`设置为`Int`类型的房间数量。正如上面说到的,当`residence`为空的时候上面这段代码会触发运行时错误。
|
||||
|
||||
可空链式调用提供了一种另一种访问numberOfRooms的方法,使用问号(?)来代替原来叹号(!)的位置:
|
||||
可空链式调用提供了一种另一种访问`numberOfRooms`的方法,使用问号(?)来代替原来叹号(!)的位置:
|
||||
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
print("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
print("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// prints "Unable to retrieve the number of rooms."
|
||||
```swift
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
print("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
print("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// prints "Unable to retrieve the number of rooms."
|
||||
```
|
||||
|
||||
在residence后面添加问号之后,Swift就会在residence不为空的情况下访问numberOfRooms。
|
||||
在`residence`后面添加问号之后,Swift就会在`residence`不为空的情况下访问`numberOfRooms`。
|
||||
|
||||
因为访问numberOfRooms有可能失败,可空链式调用会返回Int?类型,或称为“可空的Int”。如上例所示,当residence为nil的时候,可空的Int将会为nil,表明无法访问numberOfRooms。
|
||||
因为访问`numberOfRooms`有可能失败,可空链式调用会返回`Int?`类型,或称为“可空的Int”。如上例所示,当`residence`为`nil`的时候,可空的`Int`将会为`nil`,表明无法访问`numberOfRooms`。
|
||||
|
||||
要注意的是,即使numberOfRooms是不可空的Int时,这一点也成立。只要是通过可空链式调用就意味着最后numberOfRooms返回一个Int?而不是Int。
|
||||
要注意的是,即使`numberOfRooms`是不可空的`Int`时,这一点也成立。只要是通过可空链式调用就意味着最后`numberOfRooms`返回一个`Int?`而不是`Int`。
|
||||
|
||||
通过赋给john.residence一个Residence的实例变量:
|
||||
通过赋给`john.residence`一个`Residence`的实例变量:
|
||||
|
||||
john.residence = Residence()
|
||||
这样john.residence不为nil了。现在就可以正常访问john.residence.numberOfRooms,其值为默认的1,类型为Int?:
|
||||
```swift
|
||||
john.residence = Residence()
|
||||
```
|
||||
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
这样`john.residence`不为`nil`了。现在就可以正常访问`john.residence.numberOfRooms`,其值为默认的1,类型为`Int?`:
|
||||
|
||||
```swift
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
print("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
} else {
|
||||
print("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// prints "John's residence has 1 room(s)."
|
||||
}
|
||||
// prints "John's residence has 1 room(s)."
|
||||
```
|
||||
|
||||
##为可空链式调用定义模型类
|
||||
通过使用可空链式调用可以调用多层属性,方法,和下标。这样可以通过各种模型向下访问各种子属性。并且判断能否访问子属性的属性,方法或下标。
|
||||
|
||||
下面这段代码定义了四个模型类,这些例子包括多层可空链式调用。为了方便说明,在Person和Residence的基础上增加了Room和Address,以及相关的属性,方法以及下标。
|
||||
下面这段代码定义了四个模型类,这些例子包括多层可空链式调用。为了方便说明,在`Person`和`Residence`的基础上增加了`Room`和`Address`,以及相关的属性,方法以及下标。
|
||||
|
||||
Person类定义基本保持不变:
|
||||
|
||||
class Person {
|
||||
var residence: Residence?
|
||||
}
|
||||
Residence类比之前复杂些,增加了一个Room类型的空数组room:
|
||||
```swift
|
||||
class Person {
|
||||
var residence: Residence?
|
||||
}
|
||||
```
|
||||
|
||||
class Residence {
|
||||
var rooms = [Room]()
|
||||
var numberOfRooms: Int {
|
||||
return rooms.count
|
||||
}
|
||||
subscript(i: Int) -> Room {
|
||||
get {
|
||||
return rooms[i]
|
||||
}
|
||||
set {
|
||||
rooms[i] = newValue
|
||||
}
|
||||
}
|
||||
func printNumberOfRooms() {
|
||||
print("The number of rooms is \(numberOfRooms)")
|
||||
}
|
||||
var address: Address?
|
||||
}
|
||||
`Residence`类比之前复杂些,增加了一个`Room`类型的空数组`room`:
|
||||
|
||||
现在Residence有了一个存储Room类型的数组,numberOfRooms属性需要计算,而不是作为单纯的变量。计算后的numberOfRooms返回rooms数组的count属性值。现在的Residence还提供访问rooms数组的快捷方式, 通过可读写的下标来访问指定位置的数组元素。此外,还提供printNumberOfRooms方法,这个方法的作用就是输出这个房子中房间的数量。最后,Residence定义了一个可空属性address,其类型为Address?。Address类的定义在下面会说明。
|
||||
```swift
|
||||
class Residence {
|
||||
var rooms = [Room]()
|
||||
var numberOfRooms: Int {
|
||||
return rooms.count
|
||||
}
|
||||
subscript(i: Int) -> Room {
|
||||
get {
|
||||
return rooms[i]
|
||||
}
|
||||
set {
|
||||
rooms[i] = newValue
|
||||
}
|
||||
}
|
||||
func printNumberOfRooms() {
|
||||
print("The number of rooms is \(numberOfRooms)")
|
||||
}
|
||||
var address: Address?
|
||||
}
|
||||
```
|
||||
|
||||
类Room是一个简单类,只包含一个属性name,以及一个初始化函数:
|
||||
现在`Residence`有了一个存储`Room`类型的数组,`numberOfRooms`属性需要计算,而不是作为单纯的变量。计算后的`numberOfRooms`返回`rooms`数组的`count`属性值。现在的`Residence`还提供访问`rooms`数组的快捷方式, 通过可读写的下标来访问指定位置的数组元素。此外,还提供`printNumberOfRooms`方法,这个方法的作用就是输出这个房子中房间的数量。最后,`Residence`定义了一个可空属性`address`,其类型为`Address?`。`Address`类的定义在下面会说明。
|
||||
|
||||
class Room {
|
||||
let name: String
|
||||
init(name: String) { self.name = name }
|
||||
}
|
||||
最后一个类是Address,这个类有三个String?类型的可空属性。buildingName以及buildingNumber属性表示建筑的名称和号码,用来表示某个特定的建筑。第三个属性表示建筑所在街道的名称:
|
||||
类`Room`是一个简单类,只包含一个属性`name`,以及一个初始化函数:
|
||||
|
||||
class Address {
|
||||
var buildingName: String?
|
||||
var buildingNumber: String?
|
||||
var street: String?
|
||||
func buildingIdentifier() -> String? {
|
||||
if buildingName != nil {
|
||||
return buildingName
|
||||
} else if buildingNumber != nil {
|
||||
return buildingNumber
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
类Address提供buildingIdentifier()方法,返回值为String?。 如果buildingName不为空则返回buildingName, 如果buildingNumber不为空则返回buildingNumber。如果这两个属性都为空则返回nil。
|
||||
```swift
|
||||
class Room {
|
||||
let name: String
|
||||
init(name: String) { self.name = name }
|
||||
}
|
||||
```
|
||||
|
||||
最后一个类是`Address`,这个类有三个`String?`类型的可空属性。`buildingName`以及`buildingNumber`属性表示建筑的名称和号码,用来表示某个特定的建筑。第三个属性表示建筑所在街道的名称:
|
||||
|
||||
```swift
|
||||
class Address {
|
||||
var buildingName: String?
|
||||
var buildingNumber: String?
|
||||
var street: String?
|
||||
func buildingIdentifier() -> String? {
|
||||
if buildingName != nil {
|
||||
return buildingName
|
||||
} else if buildingNumber != nil {
|
||||
return buildingNumber
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
类`Address`提供`buildingIdentifier()`方法,返回值为`String?`。 如果`buildingName`不为空则返回`buildingName`, 如果`buildingNumber`不为空则返回`buildingNumber`。如果这两个属性都为空则返回`nil`。
|
||||
|
||||
##通过可空链式调用访问属性
|
||||
正如[可空链式调用作为强制展开的另一个方案]中所述,可以通过可空链式调用访问属性的可空值,并且判断访问是否成功。
|
||||
正如[使用可空链式调用来强制展开](#optional_chaining_as_an_alternative_to_forced_unwrapping)中所述,可以通过可空链式调用访问属性的可空值,并且判断访问是否成功。
|
||||
|
||||
下面的代码创建了一个Person实例,然后访问numberOfRooms属性:
|
||||
下面的代码创建了一个`Person`实例,然后访问`numberOfRooms`属性:
|
||||
|
||||
let john = Person()
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
print("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
print("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// prints "Unable to retrieve the number of rooms."
|
||||
因为john.residence为nil,所以毫无疑问这个可空链式调用失败。
|
||||
```swift
|
||||
let john = Person()
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
print("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
print("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// prints "Unable to retrieve the number of rooms."
|
||||
```
|
||||
|
||||
因为`john.residence`为`nil`,所以毫无疑问这个可空链式调用失败。
|
||||
|
||||
通过可空链式调用来设定属性值:
|
||||
|
||||
let someAddress = Address()
|
||||
someAddress.buildingNumber = "29"
|
||||
someAddress.street = "Acacia Road"
|
||||
john.residence?.address = someAddress
|
||||
在这个例子中,通过john.residence来设定address属性也是不行的,因为john.residence为nil。
|
||||
```swift
|
||||
let someAddress = Address()
|
||||
someAddress.buildingNumber = "29"
|
||||
someAddress.street = "Acacia Road"
|
||||
john.residence?.address = someAddress
|
||||
```
|
||||
|
||||
在这个例子中,通过`john.residence`来设定`address`属性也是不行的,因为`john.residence`为`nil`。
|
||||
|
||||
##通过可空链式调用来调用方法
|
||||
可以通过可空链式调用来调用方法,并判断是否调用成功,即使这个方法没有返回值。
|
||||
Residence中得printNumberOfRooms()方法输出当前的numberOfRooms值:
|
||||
`Residence`中的`printNumberOfRooms()`方法输出当前的`numberOfRooms`值:
|
||||
|
||||
func printNumberOfRooms() {
|
||||
print("The number of rooms is \(numberOfRooms)")
|
||||
}
|
||||
这个方法没有返回值。但是没有返回值的方法隐式返回Void类型,如[无返回值函数](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-ID163)中所述。这意味着没有返回值的方法也会返回()或者空的元组。
|
||||
```swift
|
||||
func printNumberOfRooms() {
|
||||
print("The number of rooms is \(numberOfRooms)")
|
||||
}
|
||||
```
|
||||
|
||||
如果在可空值上通过可空链式调用来调用这个方法,这个方法的返回类型为Void?,而不是Void,因为通过可空链式调用得到的返回值都是可空的。这样我们就可以使用if语句来判断能否成功调用printNumberOfRooms()方法,即使方法本身没有定义返回值。通过返回值是否为nil可以判断调用是否成功:
|
||||
这个方法没有返回值。但是没有返回值的方法隐式返回`Void`类型,如[无返回值函数](./06_Functions.html#functions_without_return_values)中所述。这意味着没有返回值的方法也会返回()或者空的元组。
|
||||
|
||||
if john.residence?.printNumberOfRooms() != nil {
|
||||
print("It was possible to print the number of rooms.")
|
||||
} else {
|
||||
print("It was not possible to print the number of rooms.")
|
||||
}
|
||||
// prints "It was not possible to print the number of rooms."
|
||||
同样的,可以判断通过可空链式调用来给属性赋值是否成功。在上面的例子中,我们尝试给john.residence中的address属性赋值,即使residence为nil。通过可空链式调用给属性赋值会返回Void?,通过判断返回值是否为nil可以知道赋值是否成功:
|
||||
如果在可空值上通过可空链式调用来调用这个方法,这个方法的返回类型为`Void?`,而不是`Void`,因为通过可空链式调用得到的返回值都是可空的。这样我们就可以使用`if`语句来判断能否成功调用`printNumberOfRooms()`方法,即使方法本身没有定义返回值。通过返回值是否为`nil`可以判断调用是否成功:
|
||||
|
||||
if (john.residence?.address = someAddress) != nil {
|
||||
```swift
|
||||
if john.residence?.printNumberOfRooms() != nil {
|
||||
print("It was possible to print the number of rooms.")
|
||||
} else {
|
||||
print("It was not possible to print the number of rooms.")
|
||||
}
|
||||
// prints "It was not possible to print the number of rooms."
|
||||
```
|
||||
|
||||
同样的,可以判断通过可空链式调用来给属性赋值是否成功。在上面的例子中,我们尝试给`john.residence`中的`address`属性赋值,即使`residence`为`nil`。通过可空链式调用给属性赋值会返回`Void?`,通过判断返回值是否为`nil`可以知道赋值是否成功:
|
||||
|
||||
```swift
|
||||
if (john.residence?.address = someAddress) != nil {
|
||||
print("It was possible to set the address.")
|
||||
} else {
|
||||
} else {
|
||||
print("It was not possible to set the address.")
|
||||
}
|
||||
// prints "It was not possible to set the address."
|
||||
}
|
||||
// prints "It was not possible to set the address."
|
||||
```
|
||||
|
||||
|
||||
##通过可空链式调用来访问下标
|
||||
通过可空链式调用,我们可以用下标来对可空值进行读取或写入,并且判断下标调用是否成功。
|
||||
@ -177,44 +223,57 @@ Residence中得printNumberOfRooms()方法输出当前的numberOfRooms值:
|
||||
注意:
|
||||
当通过可空链式调用访问可空值的下标的时候,应该将问号放在下标方括号的前面而不是后面。可空链式调用的问号一般直接跟在可空表达式的后面。
|
||||
|
||||
下面这个例子用下标访问john.residence中rooms数组中第一个房间的名称,因为john.residence为nil,所以下标调用毫无疑问失败了:
|
||||
下面这个例子用下标访问`john.residence`中`rooms`数组中第一个房间的名称,因为`john.residence`为`nil`,所以下标调用毫无疑问失败了:
|
||||
|
||||
if let firstRoomName = john.residence?[0].name {
|
||||
print("The first room name is \(firstRoomName).")
|
||||
} else {
|
||||
print("Unable to retrieve the first room name.")
|
||||
}
|
||||
// prints "Unable to retrieve the first room name."
|
||||
在这个例子中,问号直接放在john.residence的后面,并且在方括号的前面,因为john.residence是可空值。
|
||||
```swift
|
||||
if let firstRoomName = john.residence?[0].name {
|
||||
print("The first room name is \(firstRoomName).")
|
||||
} else {
|
||||
print("Unable to retrieve the first room name.")
|
||||
}
|
||||
// prints "Unable to retrieve the first room name."
|
||||
```
|
||||
|
||||
|
||||
在这个例子中,问号直接放在`john.residence`的后面,并且在方括号的前面,因为`john.residence`是可空值。
|
||||
|
||||
类似的,可以通过下标,用可空链式调用来赋值:
|
||||
|
||||
```swift
|
||||
john.residence?[0] = Room(name: "Bathroom")
|
||||
```
|
||||
|
||||
|
||||
这次赋值同样会失败,因为`residence`目前是`nil`。
|
||||
|
||||
如果你创建一个Residence实例,添加一些Room实例并赋值给john.residence,那就可以通过可选链和下标来访问数组中的元素:
|
||||
如果你创建一个`Residence`实例,添加一些`Room`实例并赋值给`john.residence`,那就可以通过可选链和下标来访问数组中的元素:
|
||||
|
||||
let johnsHouse = Residence()
|
||||
johnsHouse.rooms.append(Room(name: "Living Room"))
|
||||
johnsHouse.rooms.append(Room(name: "Kitchen"))
|
||||
john.residence = johnsHouse
|
||||
if let firstRoomName = john.residence?[0].name {
|
||||
```swift
|
||||
let johnsHouse = Residence()
|
||||
johnsHouse.rooms.append(Room(name: "Living Room"))
|
||||
johnsHouse.rooms.append(Room(name: "Kitchen"))
|
||||
john.residence = johnsHouse
|
||||
|
||||
if let firstRoomName = john.residence?[0].name {
|
||||
print("The first room name is \(firstRoomName).")
|
||||
} else {
|
||||
} else {
|
||||
print("Unable to retrieve the first room name.")
|
||||
}
|
||||
// prints "The first room name is Living Room."
|
||||
}
|
||||
// prints "The first room name is Living Room."
|
||||
```
|
||||
|
||||
##访问可空类型的下标:
|
||||
如果下标返回可空类型值,比如Swift中Dictionary的key下标。可以在下标的闭合括号后面放一个问号来链接下标的可空返回值:
|
||||
如果下标返回可空类型值,比如Swift中`Dictionary`的`key`下标。可以在下标的闭合括号后面放一个问号来链接下标的可空返回值:
|
||||
|
||||
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
|
||||
testScores["Dave"]?[0] = 91
|
||||
testScores["Bev"]?[0]++
|
||||
testScores["Brian"]?[0] = 72
|
||||
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]
|
||||
上面的例子中定义了一个testScores数组,包含了两个键值对, 把String类型的key映射到一个整形数组。这个例子用可空链式调用把“Dave”数组中第一个元素设为91,把”Bev”数组的第一个元素+1,然后尝试把”Brian”数组中的第一个元素设为72。前两个调用是成功的,因为这两个key存在。但是key“Brian”在字典中不存在,所以第三个调用失败。
|
||||
```swift
|
||||
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
|
||||
testScores["Dave"]?[0] = 91
|
||||
testScores["Bev"]?[0]++
|
||||
testScores["Brian"]?[0] = 72
|
||||
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]
|
||||
```
|
||||
|
||||
上面的例子中定义了一个`testScores`数组,包含了两个键值对, 把`String`类型的`key`映射到一个整形数组。这个例子用可空链式调用把“Dave”数组中第一个元素设为91,把”Bev”数组的第一个元素+1,然后尝试把”Brian”数组中的第一个元素设为72。前两个调用是成功的,因为这两个`key`存在。但是key“Brian”在字典中不存在,所以第三个调用失败。
|
||||
|
||||
##多层链接
|
||||
可以通过多个链接多个可空链式调用来向下访问属性,方法以及下标。但是多层可空链式调用不会添加返回值的可空性。
|
||||
@ -226,58 +285,68 @@ john.residence?[0] = Room(name: "Bathroom")
|
||||
|
||||
因此:
|
||||
|
||||
+ 通过可空链式调用访问一个Int值,将会返回Int?,不过进行了多少次可空链式调用。
|
||||
+ 类似的,通过可空链式调用访问Int?值,并不会变得更加可空。
|
||||
+ 通过可空链式调用访问一个`Int`值,将会返回`Int?`,不过进行了多少次可空链式调用。
|
||||
+ 类似的,通过可空链式调用访问`Int?`值,并不会变得更加可空。
|
||||
|
||||
下面的例子访问john中的residence中的address中的street属性。这里使用了两层可空链式调用,residence以及address,这两个都是可空值。
|
||||
下面的例子访问`john`中的`residence`中的`address`中的`street`属性。这里使用了两层可空链式调用,`residence`以及`address`,这两个都是可空值。
|
||||
|
||||
if let johnsStreet = john.residence?.address?.street {
|
||||
print("John's street name is \(johnsStreet).")
|
||||
} else {
|
||||
print("Unable to retrieve the address.")
|
||||
}
|
||||
// prints "Unable to retrieve the address."
|
||||
```swift
|
||||
if let johnsStreet = john.residence?.address?.street {
|
||||
print("John's street name is \(johnsStreet).")
|
||||
} else {
|
||||
print("Unable to retrieve the address.")
|
||||
}
|
||||
// prints "Unable to retrieve the address."
|
||||
```
|
||||
|
||||
john.residence包含Residence实例,但是john.residence.address为nil。因此,不能访问john.residence?.address?.street。
|
||||
`john.residence`包含`Residence`实例,但是`john.residence.address`为`nil`。因此,不能访问`john.residence?.address?.street`。
|
||||
|
||||
需要注意的是,上面的例子中,street的属性为String?。john.residence?.address?.street的返回值也依然是String?,即使已经进行了两次可空的链式调用。
|
||||
需要注意的是,上面的例子中,`street`的属性为`String?`。`john.residence?.address?.street`的返回值也依然是`String?`,即使已经进行了两次可空的链式调用。
|
||||
|
||||
如果把john.residence.address指向一个实例,并且为address中的street属性赋值,我们就能过通过可空链式调用来访问street属性。
|
||||
如果把`john.residence.address`指向一个实例,并且为`address`中的`street`属性赋值,我们就能过通过可空链式调用来访问`street`属性。
|
||||
|
||||
let johnsAddress = Address()
|
||||
johnsAddress.buildingName = "The Larches"
|
||||
johnsAddress.street = "Laurel Street"
|
||||
john.residence?.address = johnsAddress
|
||||
|
||||
if let johnsStreet = john.residence?.address?.street {
|
||||
print("John's street name is \(johnsStreet).")
|
||||
} else {
|
||||
print("Unable to retrieve the address.")
|
||||
}
|
||||
// prints "John's street name is Laurel Street."
|
||||
在上面的例子中,因为john.residence是一个可用的Residence实例,所以对john.residence的address属性赋值成功。
|
||||
```swift
|
||||
let johnsAddress = Address()
|
||||
johnsAddress.buildingName = "The Larches"
|
||||
johnsAddress.street = "Laurel Street"
|
||||
john.residence?.address = johnsAddress
|
||||
|
||||
if let johnsStreet = john.residence?.address?.street {
|
||||
print("John's street name is \(johnsStreet).")
|
||||
} else {
|
||||
print("Unable to retrieve the address.")
|
||||
}
|
||||
// prints "John's street name is Laurel Street."
|
||||
```
|
||||
|
||||
在上面的例子中,因为`john.residence`是一个可用的`Residence`实例,所以对`john.residence`的`address`属性赋值成功。
|
||||
|
||||
##对返回可空值的函数进行链接
|
||||
上面的例子说明了如何通过可空链式调用来获取可空属性值。我们还可以通过可空链式调用来调用返回可空值的方法,并且可以继续对可空值进行链接。
|
||||
|
||||
在下面的例子中,通过可空链式调用来调用Address的buildingIdentifier()方法。这个方法返回String?类型。正如上面所说,通过可空链式调用的方法的最终返回值还是String?:
|
||||
在下面的例子中,通过可空链式调用来调用`Address`的`buildingIdentifier()`方法。这个方法返回`String?`类型。正如上面所说,通过可空链式调用的方法的最终返回值还是`String?`:
|
||||
|
||||
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
|
||||
print("John's building identifier is \(buildingIdentifier).")
|
||||
}
|
||||
// prints "John's building identifier is The Larches."
|
||||
```swift
|
||||
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
|
||||
print("John's building identifier is \(buildingIdentifier).")
|
||||
}
|
||||
// prints "John's building identifier is The Larches."
|
||||
```
|
||||
|
||||
如果要进一步对方法的返回值进行可空链式调用,在方法buildingIdentifier()的圆括号后面加上问号:
|
||||
如果要进一步对方法的返回值进行可空链式调用,在方法`buildingIdentifier()`的圆括号后面加上问号:
|
||||
|
||||
if let beginsWithThe =
|
||||
```swift
|
||||
if let beginsWithThe =
|
||||
john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
|
||||
if beginsWithThe {
|
||||
print("John's building identifier begins with \"The\".")
|
||||
} else {
|
||||
print("John's building identifier does not begin with \"The\".")
|
||||
}
|
||||
}
|
||||
// prints "John's building identifier begins with "The"."
|
||||
if beginsWithThe {
|
||||
print("John's building identifier begins with \"The\".")
|
||||
} else {
|
||||
print("John's building identifier does not begin with \"The\".")
|
||||
}
|
||||
}
|
||||
// prints "John's building identifier begins with "The"."
|
||||
```
|
||||
|
||||
>
|
||||
注意:
|
||||
在上面的例子中在,在方法的圆括号后面加上问号是因为buildingIdentifier()的返回值是可空值,而不是方法本身是可空的。
|
||||
在上面的例子中在,在方法的圆括号后面加上问号是因为`buildingIdentifier()`的返回值是可空值,而不是方法本身是可空的。
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
> 2.0
|
||||
> 翻译+校对:[lyojo](https://github.com/lyojo)
|
||||
|
||||
# 错误处理
|
||||
-----------------
|
||||
错误处理是响应错误以及从错误中返回的过程。swift提供第一类错误支持,包括在运行时抛出,捕获,传送和控制可回收错误。
|
||||
@ -7,136 +10,167 @@
|
||||
举个例子,考虑到一个从磁盘上的一个文件读取以及处理数据的任务,有几种情况可能会导致这个任务失败,包括指定路径的文件不存在,文件不具有可读属性,或者文件没有被编码成合适的格式。区分这些错误可以让程序解决并且修复这些错误,并且,如果可能的话,把这些错误报告给用户。
|
||||
|
||||
>
|
||||
NOTE:
|
||||
Swift中的错误处理涉及到错误处理样式,这会用到Cocoa中的NSError和Objective-C。更多信息请参见:[Error Handling](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10)中的[Using Swift with Cocoa and Objective-C](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)。
|
||||
注意:
|
||||
Swift中的错误处理涉及到错误处理样式,这会用到Cocoa中的NSError和Objective-C。更多信息请参见:[Using Swift with Cocoa and Objective-C](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)中的[错误处理](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10)。
|
||||
|
||||
###错误的表示:
|
||||
在Swift中,错误用符合ErrorType协议的值表示。
|
||||
Swift枚举特别适合把一系列相关的错误组合在一起,同时可以把一些相关的值和错误关联在一起。因此编译器会为实现ErrorType协议的Swift枚举类型自动实现相应合成。
|
||||
在Swift中,错误用符合`ErrorType`协议的值表示。
|
||||
Swift枚举特别适合把一系列相关的错误组合在一起,同时可以把一些相关的值和错误关联在一起。因此编译器会为实现`ErrorType`协议的Swift枚举类型自动实现相应合成。
|
||||
|
||||
比如说,你可以这样表示操作自动贩卖机会出现的错误:
|
||||
|
||||
enum VendingMachineError: ErrorType {
|
||||
```swift
|
||||
enum VendingMachineError: ErrorType {
|
||||
case InvalidSelection
|
||||
case InsufficientFunds(required: Double)
|
||||
case OutOfStock
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在这种情况下,自动贩卖机可能会因为以下原因失败:
|
||||
请求的物品不存在,用InvalidSelection表示。
|
||||
请求的物品的价格高于已投入金额,用InsufficientFunds表示。相关的双精度值表示还需要多少钱来完成此次交易。
|
||||
请求的物品已经卖完了,用OutOfStock表示。
|
||||
请求的物品不存在,用`InvalidSelection`表示。
|
||||
请求的物品的价格高于已投入金额,用`InsufficientFunds`表示。相关的双精度值表示还需要多少钱来完成此次交易。
|
||||
请求的物品已经卖完了,用`OutOfStock`表示。
|
||||
|
||||
<a name="throwing_errors"></a>
|
||||
错误抛出
|
||||
通过在函数或方法声明的参数后面加上throws关键字,表明这个函数或方法可以抛出错误。如果指定一个返回值,可以把throws关键字放在返回箭头(->)的前面。除非明确地指出,一个函数,方法或者就闭包不能抛出错误。
|
||||
通过在函数或方法声明的参数后面加上`throws`关键字,表明这个函数或方法可以抛出错误。如果指定一个返回值,可以把`throws`关键字放在返回箭头(->)的前面。除非明确地指出,一个函数,方法或者就闭包不能抛出错误。
|
||||
|
||||
func canThrowErrors() throws -> String
|
||||
func cannotThrowErrors() -> String
|
||||
```swift
|
||||
func canThrowErrors() throws -> String
|
||||
|
||||
在抛出函数体的任意一个地方,可以通过throw语句抛出错误。在下面的例子中,如果请求的物品不存在,或者卖完了,或者超出投入金额,vend(itemNamed:)函数会抛出一个错误:
|
||||
func cannotThrowErrors() -> String
|
||||
```
|
||||
|
||||
struct Item {
|
||||
在抛出函数体的任意一个地方,可以通过`throw`语句抛出错误。在下面的例子中,如果请求的物品不存在,或者卖完了,或者超出投入金额,`vend(itemNamed:)`函数会抛出一个错误:
|
||||
|
||||
```swift
|
||||
struct Item {
|
||||
var price: Double
|
||||
var count: Int
|
||||
}
|
||||
var inventory = [
|
||||
}
|
||||
|
||||
var inventory = [
|
||||
"Candy Bar": Item(price: 1.25, count: 7),
|
||||
"Chips": Item(price: 1.00, count: 4),
|
||||
"Pretzels": Item(price: 0.75, count: 11)
|
||||
]
|
||||
var amountDeposited = 1.00
|
||||
func vend(itemNamed name: String) throws {
|
||||
]
|
||||
var amountDeposited = 1.00
|
||||
|
||||
func vend(itemNamed name: String) throws {
|
||||
guard var item = inventory[name] else {
|
||||
throw VendingMachineError.InvalidSelection
|
||||
}
|
||||
throw VendingMachineError.InvalidSelection
|
||||
}
|
||||
|
||||
guard item.count > 0 else {
|
||||
throw VendingMachineError.OutOfStock
|
||||
}
|
||||
throw VendingMachineError.OutOfStock
|
||||
}
|
||||
|
||||
if amountDeposited >= item.price {
|
||||
// Dispense the snack
|
||||
amountDeposited -= item.price
|
||||
--item.count
|
||||
inventory[name] = item
|
||||
} else {
|
||||
let amountRequired = item.price - amountDeposited
|
||||
throw VendingMachineError.InsufficientFunds(required: amountRequired)
|
||||
}
|
||||
// Dispense the snack
|
||||
amountDeposited -= item.price
|
||||
--item.count
|
||||
inventory[name] = item
|
||||
} else {
|
||||
let amountRequired = item.price - amountDeposited
|
||||
throw VendingMachineError.InsufficientFunds(required: amountRequired)
|
||||
}
|
||||
首先,guard语句用来把绑定item常量和count变量到在库存中对应的值。如果物品不在库存中,将会抛出InvalidSelection错误。然后,物品是否可获取有物品的剩余数量决定。如果count小于等于0,将会抛出OutOfStock错误。最后,把请求物品的价格和已经投入的金额进行比较,如果如果投入的金额大于物品的价格,将会从投入的金额从减去物品的价格,然后库存中该物品的数量减1,然后返回请求的物品。否则,将会计算还需要多少钱,然后把这个值作为InsufficientFunds错误的关联值。因为throw语句会马上改变程序流程,当所有的购买条件(物品存在,库存足够以及投入金额足够)都满足的时候,物品才会出售。
|
||||
}
|
||||
```
|
||||
|
||||
当调用一个抛出函数的时候,在调用前面加上try。这个关键字表明函数可以抛出错误,而且在try后面代码将不会执行。
|
||||
首先,`guard`语句用来把绑定`item`常量和`count`变量到在库存中对应的值。如果物品不在库存中,将会抛出`InvalidSelection`错误。然后,物品是否可获取有物品的剩余数量决定。如果`count`小于等于0,将会抛出`OutOfStock`错误。最后,把请求物品的价格和已经投入的金额进行比较,如果如果投入的金额大于物品的价格,将会从投入的金额从减去物品的价格,然后库存中该物品的数量减1,然后返回请求的物品。否则,将会计算还需要多少钱,然后把这个值作为`InsufficientFunds`错误的关联值。因为`throw`语句会马上改变程序流程,当所有的购买条件(物品存在,库存足够以及投入金额足够)都满足的时候,物品才会出售。
|
||||
|
||||
let favoriteSnacks = [
|
||||
当调用一个抛出函数的时候,在调用前面加上`try`。这个关键字表明函数可以抛出错误,而且在`try`后面代码将不会执行。
|
||||
|
||||
```swift
|
||||
let favoriteSnacks = [
|
||||
"Alice": "Chips",
|
||||
"Bob": "Licorice",
|
||||
"Eve": "Pretzels",
|
||||
]
|
||||
func buyFavoriteSnack(person: String) throws {
|
||||
]
|
||||
func buyFavoriteSnack(person: String) throws {
|
||||
let snackName = favoriteSnacks[person] ?? "Candy Bar"
|
||||
try vend(itemNamed: snackName)
|
||||
}
|
||||
buyFavoriteSnack(_:) 函数查找某个人的最喜欢的零食,然后尝试买给他。如果这个人在列表中没有喜欢的零食,就会购买Candy Bar。这个函数会调用vend函数,vend函数可能会抛出错误,所以在vend前面加上了try关键字。因为buyFavoriteSnack函数也是一个抛出函数,所以vend函数抛出的任何错误都会向上传递到buyFavoriteSnack被调用的地方。
|
||||
}
|
||||
```
|
||||
|
||||
`buyFavoriteSnack(_:)` 函数查找某个人的最喜欢的零食,然后尝试买给他。如果这个人在列表中没有喜欢的零食,就会购买`Candy Bar`。这个函数会调用`vend`函数,`vend`函数可能会抛出错误,所以在`vend`前面加上了`try`关键字。因为`buyFavoriteSnack`函数也是一个抛出函数,所以`vend`函数抛出的任何错误都会向上传递到`buyFavoriteSnack`被调用的地方。
|
||||
|
||||
###捕捉和处理错误
|
||||
使用do-catch语句来就捕获和处理错误
|
||||
|
||||
do {
|
||||
|
||||
try function that throws
|
||||
|
||||
statements
|
||||
|
||||
} catch pattern {
|
||||
|
||||
statements
|
||||
```swift
|
||||
do {
|
||||
|
||||
}
|
||||
如果一个错误被抛出了,这个错误会被传递到外部域,直到被一个catch分句处理。一个catch分句包含一个catch关键字,跟着一个pattern来匹配错误和相应的执行语句。
|
||||
try function that throws
|
||||
|
||||
类似switch语句,编译器会检查catch分句是否能够处理全部错误。如果能够处理所有错误情况,就认为这个错误被完全处理。否者,包含这个抛出函数的所在域就要处理这个错误,或者包含这个抛出函数的函数也用throws声明。为了保证错误被处理,用一个带pattern的catch分句来匹配所有错误。如果一个catch分句没有指定样式,这个分句会匹配并且绑定任何错误到一个本地error常量。更多关于pattern的信息,参见[Patterns](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/doc/uid/TP40014097-CH36-ID419)。
|
||||
statements
|
||||
|
||||
} catch pattern {
|
||||
|
||||
do {
|
||||
statements
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
如果一个错误被抛出了,这个错误会被传递到外部域,直到被一个`catch`分句处理。一个`catch`分句包含一个`catch`关键字,跟着一个`pattern`来匹配错误和相应的执行语句。
|
||||
|
||||
类似`switch`语句,编译器会检查`catch`分句是否能够处理全部错误。如果能够处理所有错误情况,就认为这个错误被完全处理。否者,包含这个抛出函数的所在域就要处理这个错误,或者包含这个抛出函数的函数也用`throws`声明。为了保证错误被处理,用一个带`pattern`的`catch`分句来匹配所有错误。如果一个`catch`分句没有指定样式,这个分句会匹配并且绑定任何错误到一个本地`error`常量。更多关于`pattern`的信息,参见[模式](../chapter3/07_Patterns.html)。
|
||||
|
||||
```swift
|
||||
do {
|
||||
try vend(itemNamed: "Candy Bar")
|
||||
// Enjoy delicious snack
|
||||
} catch VendingMachineError.InvalidSelectio {
|
||||
} catch VendingMachineError.InvalidSelectio {
|
||||
print("Invalid Selection")
|
||||
} catch VendingMachineError.OutOfStock {
|
||||
} catch VendingMachineError.OutOfStock {
|
||||
print("Out of Stock.")
|
||||
} catch VendingMachineError.InsufficientFunds(let amountRequired) {
|
||||
} catch VendingMachineError.InsufficientFunds(let amountRequired) {
|
||||
print("Insufficient funds. Please insert an additional $\(amountRequired).")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在上面的例子中,vend(itemNamed:) 函数在try表达式中被调用,因为这个函数会抛出错误。如果抛出了错误,程序执行流程马上转到catch分句,在catch分句中确定错误传递是否继续传送。如果没有抛出错误,将会执行在do语句中剩余的语句。
|
||||
在上面的例子中,`vend(itemNamed:)` 函数在`try`表达式中被调用,因为这个函数会抛出错误。如果抛出了错误,程序执行流程马上转到`catch`分句,在`catch`分句中确定错误传递是否继续传送。如果没有抛出错误,将会执行在`do`语句中剩余的语句。
|
||||
|
||||
###禁止错误传送
|
||||
在运行时,有几种情况抛出函数事实上是不会抛出错误的。在这几种情况下,你可以用forced-try表达式来调用抛出函数或方法,即使用try!来代替try。
|
||||
> 注意:Swift中的错误处理和其他语言中的异常处理很像,使用了`try`、`catch`和`throw`关键字。但是和这些语言——包括Objective-C——不同的是,Swift不会展开调用堆栈,那会带来很大的性能损耗。因此,在Swift中`throw`语句的性能可以做到几乎和`return`语句一样。
|
||||
|
||||
通过try!来调用抛出函数或方法禁止了错误传送,并且把调用包装在运行时断言,这样就不会抛出错误。如果错误真的抛出了,会触发运行时错误。
|
||||
###禁止错误传播
|
||||
在运行时,有几种情况抛出函数事实上是不会抛出错误的。在这几种情况下,你可以用`forced-try`表达式来调用抛出函数或方法,即使用`try!`来代替`try`。
|
||||
|
||||
func willOnlyThrowIfTrue(value: Bool) throws {
|
||||
通过`try!`来调用抛出函数或方法禁止了错误传送,并且把调用包装在运行时断言,这样就不会抛出错误。如果错误真的抛出了,会触发运行时错误。
|
||||
|
||||
```swift
|
||||
func willOnlyThrowIfTrue(value: Bool) throws {
|
||||
if value { throw someError }
|
||||
}
|
||||
do {
|
||||
}
|
||||
|
||||
do {
|
||||
try willOnlyThrowIfTrue(false)
|
||||
} catch {
|
||||
} catch {
|
||||
// Handle Error
|
||||
}
|
||||
try! willOnlyThrowIfTrue(false)
|
||||
}
|
||||
|
||||
try! willOnlyThrowIfTrue(false)
|
||||
```
|
||||
|
||||
###收尾操作
|
||||
使用defer语句来在执行一系列的语句。这样不管有没有错误发生,都可以执行一些必要的收尾操作。包括关闭打开的文件描述符以及释放所有手动分配的内存。
|
||||
|
||||
defer语句把执行推迟到退出当前域的时候。defer语句包括defer关键字以及后面要执行的语句。被推迟的语句可能不包含任何将执行流程转移到外部的代码,比如break或者return语句,或者通过抛出一个错误。被推迟的操作的执行的顺序和他们定义的顺序相反,也就是说,在第一个defer语句中的代码在第二个defer语句中的代码之后执行。
|
||||
`defer`语句把执行推迟到退出当前域的时候。`defer`语句包括`defer`关键字以及后面要执行的语句。被推迟的语句可能不包含任何将执行流程转移到外部的代码,比如`break`或者`return`语句,或者通过抛出一个错误。被推迟的操作的执行的顺序和他们定义的顺序相反,也就是说,在第一个`defer`语句中的代码在第二个`defer`语句中的代码之后执行。
|
||||
|
||||
func processFile(filename: String) throws {
|
||||
```swift
|
||||
func processFile(filename: String) throws {
|
||||
if exists(filename) {
|
||||
let file = open(filename)
|
||||
defer {
|
||||
close(file)
|
||||
}
|
||||
while let line = try file.readline() {
|
||||
/* Work with the file. */
|
||||
}
|
||||
// close(file) is called here, at the end of the scope.
|
||||
let file = open(filename)
|
||||
defer {
|
||||
close(file)
|
||||
}
|
||||
}
|
||||
上面这个例子使用了defer语句来保证open有对应的close。这个调用不管是否有抛出都会执行。
|
||||
while let line = try file.readline() {
|
||||
// Work with the file.
|
||||
}
|
||||
// close(file) is called here, at the end of the scope.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
上面这个例子使用了`defer`语句来保证`open`有对应的`close`。这个调用不管是否有抛出都会执行。
|
||||
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[Lin-H](https://github.com/Lin-H)
|
||||
> 校对:[shinyzhu](https://github.com/shinyzhu)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[SergioChan](https://github.com/SergioChan)
|
||||
|
||||
# 嵌套类型
|
||||
-----------------
|
||||
|
||||
@ -72,7 +76,7 @@ struct BlackjackCard {
|
||||
|
||||
`BlackjackCard`结构体自身有两个属性—`rank`与`suit`,也同样定义了一个计算属性`description`,`description`属性用`rank`和`suit`的中内容来构建对这张扑克牌名字和数值的描述,并用可选类型`second`来检查是否存在第二个值,若存在,则在原有的描述中增加对第二数值的描述。
|
||||
|
||||
因为`BlackjackCard`是一个没有自定义构造函数的结构体,在[Memberwise Initializers for Structure Types](https://github.com/CocoaChina-editors/Welcome-to-Swift/blob/master/The%20Swift%20Programming%20Language/02Language%20Guide/14Initialization.md)中知道结构体有默认的成员构造函数,所以你可以用默认的`initializer`去初始化新的常量`theAceOfSpades`:
|
||||
因为`BlackjackCard`是一个没有自定义构造函数的结构体,在[结构体的逐一成员构造器](./14_Initialization.html#memberwise_initializers_for_structure_types)中知道结构体有默认的成员构造函数,所以你可以用默认的`initializer`去初始化新的常量`theAceOfSpades`:
|
||||
|
||||
```swift
|
||||
let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades)
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
> 1.0
|
||||
> 翻译:[xiehurricane](https://github.com/xiehurricane)
|
||||
> 校对:[happyming](https://github.com/happyming) [yangsiy](https://github.com/yangsiy)
|
||||
> 校对:[happyming](https://github.com/happyming)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[yangsiy](https://github.com/yangsiy)
|
||||
|
||||
# 类型转换(Type Casting)
|
||||
-----------------
|
||||
@ -16,7 +20,7 @@ _类型转换_可以判断实例的类型,也可以将实例看做是其父类
|
||||
|
||||
类型转换在 Swift 中使用 `is` 和 `as` 操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。
|
||||
|
||||
你也可以用它来检查一个类是否实现了某个协议,就像在 [Checking for Protocol Conformance](Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-XID_363)部分讲述的一样。
|
||||
你也可以用它来检查一个类是否实现了某个协议,就像在 [检验协议的一致性](./22_Protocols.html#checking_for_protocol_conformance)部分讲述的一样。
|
||||
|
||||
<a name="defining_a_class_hierarchy_for_type_casting"></a>
|
||||
## 定义一个类层次作为例子
|
||||
@ -155,6 +159,7 @@ Swift为不确定类型提供了两种特殊类型别名:
|
||||
> 注意:
|
||||
> 只有当你明确的需要它的行为和功能时才使用`Any`和`AnyObject`。在你的代码里使用你期望的明确的类型总是更好的。
|
||||
|
||||
<a name="anyobject"></a>
|
||||
### `AnyObject`类型
|
||||
|
||||
当在工作中使用 Cocoa APIs,我们一般会接收一个`[AnyObject]`类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以从 API 提供的信息中清晰地确定数组中对象的类型。
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[lyuka](https://github.com/lyuka)
|
||||
> 校对:[Hawstein](https://github.com/Hawstein)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[shanksyang](https://github.com/shanksyang)
|
||||
|
||||
#扩展(Extensions)
|
||||
----
|
||||
|
||||
@ -25,7 +29,7 @@ Swift 中的扩展可以:
|
||||
- 使一个已有类型符合某个协议
|
||||
|
||||
TODO:
|
||||
在 Swift 中,你甚至可以对一个协议(Procotol)进行扩展,提供协议需要的实现,或者添加额外的功能能够对合适的类型带来额外的好处。你可以从[协议扩展](#)获取更多的细节。
|
||||
在 Swift 中,你甚至可以对一个协议(Procotol)进行扩展,提供协议需要的实现,或者添加额外的功能能够对合适的类型带来额外的好处。你可以从[协议扩展](./Protocols.html#protocol_extensions)获取更多的细节。
|
||||
|
||||
>注意:
|
||||
扩展可以对一个类型添加新的功能,但是不能重写已有的功能。
|
||||
@ -48,7 +52,7 @@ extension SomeType: SomeProtocol, AnotherProctocol {
|
||||
}
|
||||
```
|
||||
|
||||
按照这种方式添加的协议遵循者(protocol conformance)被称之为[在扩展中添加协议遵循者](21_Protocols.html#adding_protocol_conformance_with_an_extension)
|
||||
按照这种方式添加的协议遵循者(protocol conformance)被称之为[在扩展中添加协议遵循者](./21_Protocols.html#adding_protocol_conformance_with_an_extension)
|
||||
|
||||
>注意:
|
||||
如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。
|
||||
@ -101,7 +105,7 @@ print("A marathon is \(aMarathon) meters long")
|
||||
> 注意:
|
||||
如果你使用扩展向一个值类型添加一个构造器,在该值类型已经向所有的存储属性提供默认值,而且没有定义任何定制构造器(custom initializers)时,你可以在值类型的扩展构造器中调用默认构造器(default initializers)和逐一成员构造器(memberwise initializers)。
|
||||
>
|
||||
正如在[值类型的构造器代理](14_Initialization.html#initializer_delegation_for_value_types)中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。
|
||||
正如在[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。
|
||||
|
||||
下面的例子定义了一个用于描述几何矩形的定制结构体`Rect`。这个例子同时定义了两个辅助结构体`Size`和`Point`,它们都把`0.0`作为所有属性的默认值:
|
||||
|
||||
@ -117,7 +121,7 @@ struct Rect {
|
||||
var size = Size()
|
||||
}
|
||||
```
|
||||
因为结构体`Rect`提供了其所有属性的默认值,所以正如[默认构造器](14_Initialization.html#default_initializers)中描述的,它可以自动接受一个默认构造器和一个逐一成员构造器。这些构造器可以用于构造新的`Rect`实例:
|
||||
因为结构体`Rect`提供了其所有属性的默认值,所以正如[默认构造器](./14_Initialization.html#default_initializers)中描述的,它可以自动接受一个默认构造器和一个逐一成员构造器。这些构造器可以用于构造新的`Rect`实例:
|
||||
|
||||
```swift
|
||||
let defaultRect = Rect()
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[geek5nan](https://github.com/geek5nan)
|
||||
> 校对:[dabing1022](https://github.com/dabing1022)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[futantan](https://github.com/futantan)
|
||||
|
||||
# 协议
|
||||
-----------------
|
||||
|
||||
@ -170,7 +174,7 @@ print("And another one: \(generator.random())")
|
||||
<a name="mutating_method_requirements"></a>
|
||||
## 对Mutating方法的规定
|
||||
|
||||
有时需要在方法中改变它的实例。例如,值类型(结构体,枚举)的实例方法中,将`mutating`关键字作为函数的前缀,写在`func`之前,表示可以在该方法中修改它所属的实例及其实例属性的值。这一过程在[Modifyting Value Types from Within Instance Methods](TODO)章节中有详细描述。
|
||||
有时需要在方法中改变它的实例。例如,值类型(结构体,枚举)的实例方法中,将`mutating`关键字作为函数的前缀,写在`func`之前,表示可以在该方法中修改它所属的实例及其实例属性的值。这一过程在[在实例方法中修改值类型](./11_Methods.html#modifying_value_types_from_within_instance_methods)章节中有详细描述。
|
||||
|
||||
如果你在协议中定义了一个方法旨在改变遵循该协议的实例,那么在协议定义时需要在方法前加`mutating`关键字。这使得结构和枚举遵循协议并满足此方法要求。
|
||||
|
||||
@ -234,12 +238,10 @@ class SomeClass: SomeProtocol {
|
||||
|
||||
使用`required`修饰符可以保证:所有的遵循该协议的子类,同样能为构造器规定提供一个显式的实现或继承实现。
|
||||
|
||||
<!--TODO 参考链接-->
|
||||
关于`required`构造器的更多内容,请参考<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_454">`Required`构造器 </a>
|
||||
关于`required`构造器的更多内容,请参考[必要构造器](./14_Initialization.html#required_initializers)。
|
||||
|
||||
<!--TODO 参考链接-->
|
||||
>注意
|
||||
>如果类已经被标记为`final`,那么不需要在协议构造器的实现中使用`required`修饰符。因为final类不能有子类。关于`final`修饰符的更多内容,请参见<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_339">防止重写</a>
|
||||
>如果类已经被标记为`final`,那么不需要在协议构造器的实现中使用`required`修饰符。因为final类不能有子类。关于`final`修饰符的更多内容,请参见[防止重写](./13_Inheritance.html#preventing_overrides)。
|
||||
|
||||
如果一个子类重写了父类的指定构造器,并且该构造器遵循了某个协议的规定,那么该构造器的实现需要被同时标示`required`和`override`修饰符
|
||||
|
||||
@ -266,7 +268,7 @@ class SomeSubClass: SomeSuperClass, SomeProtocol {
|
||||
|
||||
### 可失败构造器的规定
|
||||
|
||||
可以通过给协议`Protocols`中添加可失败构造器来使遵循该协议的类型必须实现该可失败构造器。
|
||||
可以通过给协议`Protocols`中添加[可失败构造器](./14_Initialization.html#failable_initializers)来使遵循该协议的类型必须实现该可失败构造器。
|
||||
|
||||
如果在协议中定义一个可失败构造器,则在遵顼该协议的类型中必须添加同名同参数的可失败构造器或非可失败构造器。如果在协议中定义一个非可失败构造器,则在遵循该协议的类型中必须添加同名同参数的非可失败构造器或隐式解析类型的可失败构造器(`init!`)。
|
||||
|
||||
@ -346,7 +348,7 @@ protocol DiceGameDelegate {
|
||||
|
||||
`DiceGame`协议可以在任意含有骰子的游戏中实现。`DiceGameDelegate`协议可以用来追踪`DiceGame`的游戏过程
|
||||
|
||||
如下所示,`SnakesAndLadders`是`Snakes and Ladders`(译者注:[Control Flow](2)章节有该游戏的详细介绍)游戏的新版本。新版本使用`Dice`作为骰子,并且实现了`DiceGame`和`DiceGameDelegate`协议,后者用来记录游戏的过程:
|
||||
如下所示,`SnakesAndLadders`是`Snakes and Ladders`([Control Flow](./05_Control_Flow.html)章节有该游戏的详细介绍)游戏的新版本。新版本使用`Dice`作为骰子,并且实现了`DiceGame`和`DiceGameDelegate`协议,后者用来记录游戏的过程:
|
||||
|
||||
```swift
|
||||
class SnakesAndLadders: DiceGame {
|
||||
@ -438,7 +440,7 @@ game.play()
|
||||
<a name="adding_protocol_conformance_with_an_extension"></a>
|
||||
## 在扩展中添加协议成员
|
||||
|
||||
即便无法修改源代码,依然可以通过扩展(Extension)来扩充已存在类型(*译者注: 类,结构体,枚举等*)。扩展可以为已存在的类型添加属性,方法,下标脚本,协议等成员。详情请在[扩展](4)章节中查看。
|
||||
即便无法修改源代码,依然可以通过扩展(Extension)来扩充已存在类型(*译者注: 类,结构体,枚举等*)。扩展可以为已存在的类型添加属性,方法,下标脚本,协议等成员。详情请在[扩展](./21_Extensions.html)章节中查看。
|
||||
|
||||
> 注意
|
||||
> 通过扩展为已存在的类型遵循协议时,该类型的所有实例也会随之添加协议中的方法
|
||||
@ -602,7 +604,7 @@ protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
|
||||
<!--TODO 链接-->
|
||||
|
||||
>注意
|
||||
>当协议想要定义的行为,要求(或假设)它的遵循类型必须是引用语义而非值语义时,应该采用类专属协议。关于引用语义,值语义的更多内容,请查看<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-XID_145">结构体和枚举是值类型</a>和<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-XID_146">类是引用类型</a>
|
||||
>当协议想要定义的行为,要求(或假设)它的遵循类型必须是引用语义而非值语义时,应该采用类专属协议。关于引用语义,值语义的更多内容,请查看[结构体和枚举是值类型](./09_Classes_and_Structures.html#structures_and_enumerations_are_value_types)和[类是引用类型](./09_Classes_and_Structures.html#classes_are_reference_types)。
|
||||
|
||||
|
||||
<a name="protocol_composition"></a>
|
||||
@ -644,7 +646,7 @@ wishHappyBirthday(birthdayPerson)
|
||||
## 检验协议的一致性
|
||||
|
||||
<!--TODO 链接-->
|
||||
你可以使用`is`和`as`操作符来检查是否遵循某一协议或强制转化为某一类型。检查和转化的语法和之前相同(*详情查看[Typy Casting章节](5)*):
|
||||
你可以使用`is`和`as`操作符来检查是否遵循某一协议或强制转化为某一类型。检查和转化的语法和之前相同(*详情查看[类型转换](./20_Type_Casting.html)*):
|
||||
|
||||
* `is`操作符用来检查实例是否`遵循`了某个`协议`
|
||||
* `as?`返回一个可选值,当实例`遵循`协议时,返回该协议类型;否则返回`nil`
|
||||
@ -721,7 +723,7 @@ for object in objects {
|
||||
协议可以含有可选成员,其`遵循者`可以选择是否实现这些成员。在协议中使用`optional`关键字作为前缀来定义可选成员。
|
||||
|
||||
<!--TODO 链接-->
|
||||
可选协议在调用时使用`可选链`,因为协议的遵循者可能没有实现可选内容,详细内容在[Optional Chaning](7)章节中查看。
|
||||
可选协议在调用时使用`可选链`,因为协议的遵循者可能没有实现可选内容,详细内容在[可空链式调用](./17_Optional_Chaining.html)章节中查看。
|
||||
|
||||
像`someOptionalMethod?(someArgument)`这样,你可以在可选方法名称后加上`?`来检查该方法是否被实现。可选方法和可选属性都会返回一个`可选值(optional value)`,当其不可访问时,`?`之后语句不会执行,并整体返回`nil`
|
||||
|
||||
@ -876,7 +878,7 @@ extension PrettyTextRepresentable {
|
||||
|
||||
### 为协议扩展添加限制条件
|
||||
|
||||
在扩展协议的时候,可以指定一些限制,只有满足这些限制的协议遵循者,才能获得协议扩展提供的属性和方法。这些限制写在协议名之后,使用`where`关键字来描述限制情况。([Where 子句](TODO))。:
|
||||
在扩展协议的时候,可以指定一些限制,只有满足这些限制的协议遵循者,才能获得协议扩展提供的属性和方法。这些限制写在协议名之后,使用`where`关键字来描述限制情况。([Where语句](./23_Generics.html#where_clauses))。:
|
||||
|
||||
例如,你可以扩展`CollectionType`协议,但是只适用于元素遵循`TextRepresentable`的情况:
|
||||
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
|
||||
> 翻译:[takalard](https://github.com/takalard) [SergioChan](https://github.com/SergioChan)
|
||||
> 1.0
|
||||
> 翻译:[takalard](https://github.com/takalard)
|
||||
> 校对:[lifedim](https://github.com/lifedim)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对: [SergioChan](https://github.com/SergioChan)
|
||||
|
||||
# 泛型
|
||||
|
||||
------
|
||||
@ -35,7 +38,7 @@ func swapTwoInts(inout a: Int, inout _ b: Int) {
|
||||
}
|
||||
```
|
||||
|
||||
这个函数使用写入读出(in-out)参数来交换`a`和`b`的值,请参考[写入读出参数](../chapter2/06_Functions.html)。
|
||||
这个函数使用写入读出(in-out)参数来交换`a`和`b`的值,请参考[输入输出参数](./06_Functions.html#in_out_parameters)。
|
||||
|
||||
`swapTwoInts(_:_:)`函数可以交换`b`的原始值到`a`,也可以交换a的原始值到`b`,你可以调用这个函数交换两个`Int`变量值:
|
||||
|
||||
@ -260,7 +263,7 @@ if let topItem = stackOfStrings.topItem {
|
||||
|
||||
`swapTwoValues(_:_:)`函数和`Stack`类型可以作用于任何类型,不过,有的时候对使用在泛型函数和泛型类型上的类型强制约束为某种特定类型是非常有用的。类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。
|
||||
|
||||
例如,Swift 的`Dictionary`类型对作用于其键的类型做了些限制。在[字典](../chapter2/04_Collection_Types.html)的描述中,字典的键类型必须是*可哈希*,也就是说,必须有一种方法可以使其被唯一的表示。`Dictionary`之所以需要其键是可哈希是为了以便于其检查其是否已经包含某个特定键的值。如无此需求,`Dictionary`既不会告诉是否插入或者替换了某个特定键的值,也不能查找到已经存储在字典里面的给定键值。
|
||||
例如,Swift 的`Dictionary`类型对作用于其键的类型做了些限制。在[字典](./04_Collection_Types.html#dictionaries)的描述中,字典的键类型必须是*可哈希*,也就是说,必须有一种方法可以使其被唯一的表示。`Dictionary`之所以需要其键是可哈希是为了以便于其检查其是否已经包含某个特定键的值。如无此需求,`Dictionary`既不会告诉是否插入或者替换了某个特定键的值,也不能查找到已经存储在字典里面的给定键值。
|
||||
|
||||
这个需求强制加上一个类型约束作用于`Dictionary`的键上,当然其键类型必须遵循`Hashable`协议(Swift 标准库中定义的一个特定协议)。所有的 Swift 基本类型(如`String`,`Int`, `Double`和 `Bool`)默认都是可哈希。
|
||||
|
||||
@ -443,9 +446,9 @@ struct Stack<T>: Container {
|
||||
|
||||
### 扩展一个存在的类型为一指定关联类型
|
||||
|
||||
在[使用扩展来添加协议兼容性](../chapter2/21_Protocols.html)中有描述扩展一个存在的类型添加遵循一个协议。这个类型包含一个关联类型的协议。
|
||||
在[在扩展中添加协议成员](./21_Protocols.html#adding_protocol_conformance_with_an_extension)中有描述扩展一个存在的类型添加遵循一个协议。这个类型包含一个关联类型的协议。
|
||||
|
||||
Swift的`Array`已经提供`append(_:)`方法,一个`count`属性和通过下标来查找一个自己的元素。这三个功能都达到`Container`协议的要求。也就意味着你可以扩展`Array`去遵循`Container`协议,只要通过简单声明`Array`适用于该协议而已。如何实践这样一个空扩展,在[使用扩展来声明协议的采纳](../chapter2/21_Protocols.html)中有描述这样一个实现一个空扩展的行为:
|
||||
Swift的`Array`已经提供`append(_:)`方法,一个`count`属性和通过下标来查找一个自己的元素。这三个功能都达到`Container`协议的要求。也就意味着你可以扩展`Array`去遵循`Container`协议,只要通过简单声明`Array`适用于该协议而已。如何实践这样一个空扩展,在[通过扩展补充协议声明](./21_Protocols.html#declaring_protocol_adoption_with_an_extension)中有描述这样一个实现一个空扩展的行为:
|
||||
|
||||
```swift
|
||||
extension Array: Container {}
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[JaceFu](http://www.devtalking.com/)
|
||||
> 校对:[ChildhoodAndy](http://childhood.logdown.com)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[mmoaay](https://github.com/mmoaay)
|
||||
|
||||
# 访问控制
|
||||
------------------
|
||||
|
||||
@ -288,13 +292,13 @@ public struct TrackedString {
|
||||
|
||||
<a name="initializers"></a>
|
||||
## 初始化
|
||||
我们可以给自定义的初始化方法申明访问级别,但是要不高于它所属类的访问级别。但[必要构造器](TODO)例外,它的访问级别必须和所属类的访问级别相同。
|
||||
我们可以给自定义的初始化方法申明访问级别,但是要不高于它所属类的访问级别。但[必要构造器](./14_Initialization.html#required_initializers)例外,它的访问级别必须和所属类的访问级别相同。
|
||||
|
||||
如同函数或方法参数,初始化方法参数的访问级别也不能低于初始化方法的访问级别。
|
||||
|
||||
<a name="default_initializers"></a>
|
||||
### 默认初始化方法
|
||||
Swift为结构体、类都提供了一个默认的无参初始化方法,用于给它们的所有属性提供赋值操作,但不会给出具体值。默认初始化方法可以参阅[Default Initializers](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_336)。默认初始化方法的访问级别与所属类型的访问级别相同。
|
||||
Swift为结构体、类都提供了一个默认的无参初始化方法,用于给它们的所有属性提供赋值操作,但不会给出具体值。默认初始化方法可以参阅[默认构造器](./14_Initialization.html#default_initializers)。默认初始化方法的访问级别与所属类型的访问级别相同。
|
||||
|
||||
> 注意:如果一个类型被申明为`public`级别,那么默认的初始化方法的访问级别为`internal`。如果你想让无参的初始化方法在其他模块中可以被使用,那么你必须提供一个具有`public`访问级别的无参初始化方法。
|
||||
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[xielingwang](https://github.com/xielingwang)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[buginux](https://github.com/buginux)
|
||||
|
||||
# 高级运算符
|
||||
-----------------
|
||||
|
||||
@ -12,7 +16,7 @@
|
||||
- [运算符函数(Operator Functions)](#operator_functions)
|
||||
- [自定义运算符](#custom_operators)
|
||||
|
||||
除了在之前介绍过的[基本运算符](02_Basic_Operators.html),Swift 中还有许多可以对数值进行复杂操作的高级运算符。这些高级运算符包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算符。
|
||||
除了在之前介绍过的[基本运算符](./02_Basic_Operators.html),Swift 中还有许多可以对数值进行复杂操作的高级运算符。这些高级运算符包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算符。
|
||||
|
||||
与C语言中的算术运算符不同,Swift 中的算术运算符默认是不会溢出的。所有溢出行为都会被捕获并报告为错误。如果想让系统允许溢出行为,可以选择使用 Swift 中另一套默认支持溢出的运算符,比如溢出加法运算符(`&+`)。所有的这些溢出运算符都是以 `&` 开头的。
|
||||
|
||||
@ -393,7 +397,8 @@ let afterIncrement = ++toIncrement
|
||||
> 注意:
|
||||
> 不能对默认的赋值运算符(`=`)进行重载。只有组合赋值符可以被重载。同样地,也无法对三目条件运算符 `a ? b : c` 进行重载。
|
||||
|
||||
### 等价运算符
|
||||
<a name="equivalence_operators"></a>
|
||||
### 等价操作符
|
||||
|
||||
自定义的类和结构体没有对等价操作符(`equivalence operators`)进行默认实现,等价操作符通常被称为“相等”操作符(`==`)与“不等”操作符(`!=`)。对于自定义类型,Swift 无法判断其是否“相等”,因为“相等”的含义取决于这些自定义类型在你的代码中所扮演的角色。
|
||||
|
||||
@ -421,6 +426,7 @@ if twoThree == anotherTwoThree {
|
||||
// prints "These two vectors are equivalent."
|
||||
```
|
||||
|
||||
<a name="custom_operators"></a>
|
||||
### 自定义运算符
|
||||
|
||||
除了实现标准运算符,在 Swift 当中还可以声明和实现自定义运算符(`custom operators`)。可以用来自定义运算符的字符列表请参考[操作符](../chapter3/02_Lexical_Structure.html#operators)
|
||||
|
||||
Reference in New Issue
Block a user