Merge remote-tracking branch 'numbbbbb/gh-pages' into gh-pages

This commit is contained in:
Realank
2016-11-07 11:05:30 -08:00
19 changed files with 932 additions and 706 deletions

View File

@@ -13,7 +13,12 @@
> 校对:[shanks](http://codebuild.me)[overtrue](https://github.com/overtrue)
>
> 2.2
> 校对:[SketchK](https://github.com/SketchK) 2016-05-11
> 校对:[SketchK](https://github.com/SketchK)
>
> 3.0
> 校对:[CMB](https://github.com/chenmingbiao)
>
> 版本日期2016-09-13
本页包含内容:
@@ -45,27 +50,27 @@
- [错误处理](#error_handling)
- [断言](#assertions)
Swift 是一门开发 iOS, OS X 和 watchOS 应用的新语言。然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 Swift 的很多内容都是你熟悉的。
Swift 是一门开发 iOS, macOS, watchOS 和 tvOS 应用的新语言。然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 Swift 的很多内容都是你熟悉的。
Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int`表示整型值;`Double``Float`表示浮点型值;`Bool`是布尔型值;`String`是文本型数据。Swift 还提供了三个基本的集合类型,`Array``Set``Dictionary`,详见[集合类型](04_Collection_Types.html)。
Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int`表示整型值; `Double``Float` 表示浮点型值; `Bool` 是布尔型值;`String` 是文本型数据。 Swift 还提供了三个基本的集合类型,`Array` `Set``Dictionary` ,详见[集合类型](04_Collection_Types.html)。
就像 C 语言一样Swift 使用变量来进行存储并通过变量名来关联值。在 Swift 中,广泛的使用着值不可变的变量,它们就是常量,而且比 C 语言的常量更强大。在 Swift 中,如果你要处理的值不需要改变,那使用常量可以让你的代码更加安全并且更清晰地表达你的意图。
除了我们熟悉的类型Swift 还增加了 Objective-C 中没有的高阶数据类型比如元组Tuple。元组可以让你创建或者传递一组数据比如作为函数的返回值时你可以用一个元组可以返回多个值。
Swift 还增加了可选Optional类型用于处理值缺失的情况。可选表示“那儿有一个值并且它等于 x ”或者“那儿没有值”。可选有点像在 Objective-C 中使用`nil`,但是它可以用在任何类型上,不仅仅是类。可选类型比 Objective-C 中的`nil`指针更加安全也更具表现力,它是 Swift 许多强大特性的重要组成部分。
Swift 还增加了可选Optional类型用于处理值缺失的情况。可选表示 “那儿有一个值,并且它等于 x ” 或者 “那儿没有值” 。可选有点像在 Objective-C 中使用 `nil` ,但是它可以用在任何类型上,不仅仅是类。可选类型比 Objective-C 中的 `nil` 指针更加安全也更具表现力,它是 Swift 许多强大特性的重要组成部分。
Swift 是一门类型安全的语言可选类型就是一个很好的例子。Swift 可以让你清楚地知道值的类型。如果你的代码期望得到一个`String`,类型安全会阻止你不小心传入一个`Int`。你可以在开发阶段尽早发现并修正错误。
Swift 是一门类型安全的语言可选类型就是一个很好的例子。Swift 可以让你清楚地知道值的类型。如果你的代码期望得到一个 `String` ,类型安全会阻止你不小心传入一个 `Int` 。同样的,如果你的代码期望得到一个 `String`,类型安全会阻止你意外传入一个可选的 `String` 。你可以在开发阶段尽早发现并修正错误。
<a name="constants_and_variables"></a>
## 常量和变量
常量和变量把一个名字(比如`maximumNumberOfLoginAttempts`或者`welcomeMessage`)和一个指定类型的值(比如数字`10`或者字符串`"Hello"`)关联起来。常量的值一旦设定就不能改变,而变量的值可以随意更改。
常量和变量把一个名字(比如 `maximumNumberOfLoginAttempts` 或者 `welcomeMessage` )和一个指定类型的值(比如数字 `10` 或者字符串 `"Hello"` )关联起来。常量的值一旦设定就不能改变,而变量的值可以随意更改。
<a name="declaring"></a>
### 声明常量和变量
常量和变量必须在使用前声明,用`let`来声明常量,用`var`来声明变量。下面的例子展示了如何用常量和变量来记录用户尝试登录的次数:
常量和变量必须在使用前声明,用 `let` 来声明常量,用 `var` 来声明变量。下面的例子展示了如何用常量和变量来记录用户尝试登录的次数:
```swift
let maximumNumberOfLoginAttempts = 10
@@ -74,7 +79,7 @@ var currentLoginAttempt = 0
这两行代码可以被理解为:
“声明一个名字是`maximumNumberOfLoginAttempts`的新常量,并给它一个值`10`。然后,声明一个名字是`currentLoginAttempt`的变量并将它的值初始化为`0`。”
“声明一个名字是 `maximumNumberOfLoginAttempts` 的新常量,并给它一个值 `10` 。然后,声明一个名字是 `currentLoginAttempt` 的变量并将它的值初始化为 `0` 。”
在这个例子中,允许的最大尝试登录次数被声明为一个常量,因为这个值不会改变。当前尝试登录次数被声明为一个变量,因为每次尝试登录失败的时候都需要增加这个值。
@@ -84,15 +89,15 @@ var currentLoginAttempt = 0
var x = 0.0, y = 0.0, z = 0.0
```
>注意:
如果你的代码中有不需要改变的值,请使用`let`关键字将它声明为常量。只将需要改变的值声明为变量。
> 注意:
> 如果你的代码中有不需要改变的值,请使用 `let` 关键字将它声明为常量。只将需要改变的值声明为变量。
<a name="type_annotations"></a>
### 类型标注
当你声明常量或者变量的时候可以加上类型标注type annotation说明常量或者变量中要存储的值的类型。如果要添加类型标注需要在常量或者变量名后面加上一个冒号和空格然后加上类型名称。
这个例子给`welcomeMessage`变量添加了类型标注,表示这个变量可以存储`String`类型的值:
这个例子给 `welcomeMessage` 变量添加了类型标注,表示这个变量可以存储 `String` 类型的值:
```swift
var welcomeMessage: String
@@ -100,11 +105,11 @@ var welcomeMessage: String
声明中的冒号代表着“是...类型”,所以这行代码可以被理解为:
“声明一个类型为`String`,名字为`welcomeMessage`的变量。”
“声明一个类型为 `String` ,名字为 `welcomeMessage` 的变量。”
“类型为`String`”的意思是“可以存储任意`String`类型的值。”
“类型为 `String` ”的意思是“可以存储任意 `String` 类型的值。”
`welcomeMessage`变量现在可以被设置成任意字符串:
`welcomeMessage` 变量现在可以被设置成任意字符串:
```swift
welcomeMessage = "Hello"
@@ -117,7 +122,7 @@ var red, green, blue: Double
```
> 注意:
一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值Swift可以推断出这个常量或者变量的类型请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给`welcomeMessage`赋初始值,所以变量`welcomeMessage`的类型是通过一个类型标注指定的,而不是通过初始值推断的。
> 一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值Swift可以推断出这个常量或者变量的类型请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给 `welcomeMessage` 赋初始值,所以变量 `welcomeMessage` 的类型是通过一个类型标注指定的,而不是通过初始值推断的。
<a name="naming"></a>
### 常量和变量的命名
@@ -135,7 +140,7 @@ let 🐶🐮 = "dogcow"
一旦你将常量或者变量声明为确定的类型,你就不能使用相同的名字再次进行声明,或者改变其存储的值的类型。同时,你也不能将常量与变量进行互转。
> 注意:
如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名你可以使用反引号`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。
> 如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名你可以使用反引号`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。
你可以更改现有的变量值为其他同类型的值,在下面的例子中,`friendlyWelcome`的值从`"Hello!"`改为了`"Bonjour!"`:
@@ -163,7 +168,7 @@ print(friendlyWelcome)
// "Bonjour!"
```
`print(_:separator:terminator:)`是一个用来输出一个或多个值到适当输出区的全局函数。如果你用 Xcode`print(_:separator:terminator:)`将会输出内容到“console”面板上。`separator`和`terminator`参数具有默认值,因此你调用这个函数的时候可以忽略它们。默认情况下,该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给`terminator`参数--例如,`print(someValue, terminator:"")`。关于参数默认值的更多信息,请参考[默认参数值](./06_Functions.html#default_parameter_values)。
`print(_:separator:terminator:)` 是一个用来输出一个或多个值到适当输出区的全局函数。如果你用 Xcode`print(_:separator:terminator:)` 将会输出内容到“console”面板上。`separator``terminator` 参数具有默认值,因此你调用这个函数的时候可以忽略它们。默认情况下,该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给 `terminator` 参数--例如,`print(someValue, terminator:"")` 。关于参数默认值的更多信息,请参考[默认参数值](./06_Functions.html#default_parameter_values)。
Swift 用字符串插值string interpolation的方式把常量名或者变量名当做占位符加入到长字符串中Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:
@@ -214,38 +219,39 @@ let cat = "🐱"; print(cat)
<a name="integers"></a>
## 整数
整数就是没有小数部分的数字,比如`42`和`-23`。整数可以是`有符号`(正、负、零)或者`无符号`(正、零)。
整数就是没有小数部分的数字,比如 `42``-23` 。整数可以是 `有符号`(正、负、零)或者 `无符号`(正、零)。
Swift 提供了81632和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像比如8位无符号整数类型是`UInt8`32位有符号整数类型是`Int32`。就像 Swift 的其他类型一样,整数类型采用大写命名法。
Swift 提供了81632和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像比如8位无符号整数类型是`UInt8`32位有符号整数类型是 `Int32` 。就像 Swift 的其他类型一样,整数类型采用大写命名法。
<a name="integer_bounds"></a>
### 整数范围
你可以访问不同整数类型的`min`和`max`属性来获取对应类型的最小值和最大值:
你可以访问不同整数类型的 `min``max` 属性来获取对应类型的最小值和最大值:
```swift
let minValue = UInt8.min // minValue 0 UInt8
let maxValue = UInt8.max // maxValue 255 UInt8
```
`min`和`max`所传回值的类型,正是其所对的整数类型(如上例UInt8, 所传回的类型是UInt8),可用在表达式中相同类型值旁。
`min``max` 所传回值的类型,正是其所对的整数类型(如上例UInt8, 所传回的类型是UInt8),可用在表达式中相同类型值旁。
<a name="Int"></a>
### Int
一般来说你不需要专门指定整数的长度。Swift 提供了一个特殊的整数类型`Int`,长度与当前平台的原生字长相同:
* 在32位平台上`Int`和`Int32`长度相同。
* 在64位平台上`Int`和`Int64`长度相同。
* 在32位平台上`Int``Int32` 长度相同。
* 在64位平台上`Int``Int64` 长度相同。
除非你需要特定长度的整数,一般来说使用`Int`就够了。这可以提高代码一致性和可复用性。即使是在32位平台上`Int`可以存储的整数范围也可以达到`-2,147,483,648`~`2,147,483,647`,大多数时候这已经足够大了。
除非你需要特定长度的整数,一般来说使用 `Int` 就够了。这可以提高代码一致性和可复用性。即使是在32位平台上`Int` 可以存储的整数范围也可以达到 `-2,147,483,648` ~ `2,147,483,647` ,大多数时候这已经足够大了。
<a name="UInt"></a>
### UInt
Swift 也提供了一个特殊的无符号类型`UInt`,长度与当前平台的原生字长相同:
Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台的原生字长相同:
* 在32位平台上`UInt`和`UInt32`长度相同。
* 在64位平台上`UInt`和`UInt64`长度相同。
* 在32位平台上`UInt``UInt32` 长度相同。
* 在64位平台上`UInt``UInt64` 长度相同。
> 注意:
尽量不要使用`UInt`,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用`Int`,即使你要存储的值已知是非负的。统一使用`Int`可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,请参考[类型安全和类型推断](#type_safety_and_type_inference)。
@@ -253,15 +259,15 @@ Swift 也提供了一个特殊的无符号类型`UInt`,长度与当前平台
<a name="floating-point_numbers"></a>
## 浮点数
浮点数是有小数部分的数字,比如`3.14159``0.1`和`-273.15`。
浮点数是有小数部分的数字,比如 `3.14159` `0.1``-273.15`
浮点类型比整数类型表示的范围更大,可以存储比`Int`类型更大或者更小的数字。Swift 提供了两种有符号浮点数类型:
浮点类型比整数类型表示的范围更大,可以存储比 `Int` 类型更大或者更小的数字。Swift 提供了两种有符号浮点数类型:
* `Double`表示64位浮点数。当你需要存储很大或者很高精度的浮点数时请使用此类型。
* `Float`表示32位浮点数。精度要求不高的话可以使用此类型。
> 注意:
`Double`精确度很高至少有15位数字而`Float`只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围。
`Double`精确度很高至少有15位数字`Float`只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围,在两种类型都匹配的情况下,将优先选择 `Double`
<a name="type_safety_and_type_inference"></a>
## 类型安全和类型推断
@@ -274,32 +280,32 @@ Swift 是一个*类型安全type safe*的语言。类型安全的语言可
因为有类型推断,和 C 或者 Objective-C 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。
当你声明常量或者变量并赋初值的时候类型推断非常有用。当你在声明常量或者变量的时候赋给它们一个字面量literal value 或 literal即可触发类型推断。字面量就是会直接出现在你代码中的值比如`42`和`3.14159`。)
当你声明常量或者变量并赋初值的时候类型推断非常有用。当你在声明常量或者变量的时候赋给它们一个字面量literal value 或 literal即可触发类型推断。字面量就是会直接出现在你代码中的值比如 `42``3.14159` 。)
例如,如果你给一个新常量赋值`42`并且没有标明类型Swift 可以推断出常量类型是`Int`,因为你给它赋的初始值看起来像一个整数:
例如,如果你给一个新常量赋值 `42` 并且没有标明类型Swift 可以推断出常量类型是 `Int` ,因为你给它赋的初始值看起来像一个整数:
```swift
let meaningOfLife = 42
// meaningOfLife Int
```
同理如果你没有给浮点字面量标明类型Swift 会推断你想要的是`Double`
同理如果你没有给浮点字面量标明类型Swift 会推断你想要的是 `Double`
```swift
let pi = 3.14159
// pi Double
```
当推断浮点数的类型时Swift 总是会选择`Double`而不是`Float`。
当推断浮点数的类型时Swift 总是会选择 `Double` 而不是`Float`
如果表达式中同时出现了整数和浮点数,会被推断为`Double`类型:
如果表达式中同时出现了整数和浮点数,会被推断为 `Double` 类型:
```swift
let anotherPi = 3 + 0.14159
// anotherPi Double
```
原始值`3`没有显式声明类型,而表达式中出现了一个浮点字面量,所以表达式会被推断为`Double`类型。
原始值 `3` 没有显式声明类型,而表达式中出现了一个浮点字面量,所以表达式会被推断为 `Double` 类型。
<a name="numeric_literals"></a>
## 数值型字面量
@@ -320,13 +326,15 @@ let octalInteger = 0o21 // 八进制的17
let hexadecimalInteger = 0x11 // 17
```
浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是`0x`。小数点两边必须有至少一个十进制数字或者是十六进制的数字。十进制浮点数也可以有一个可选的指数exponent),通过大写或者小写的 `e` 来指定;十六进制浮点数必须有一个指数,通过大写或者小写的 `p` 来指定。
浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是 `0x` 。小数点两边必须有至少一个十进制数字或者是十六进制的数字。十进制浮点数也可以有一个可选的指数exponent),通过大写或者小写的 `e` 来指定;十六进制浮点数必须有一个指数,通过大写或者小写的 `p` 来指定。
如果一个十进制数的指数为 `exp`那这个数相当于基数和10^exp的乘积
如果一个十进制数的指数为`exp`那这个数相当于基数和10^exp的乘积
* `1.25e2` 表示 1.25 × 10^2等于 `125.0`
* `1.25e-2` 表示 1.25 × 10^-2等于 `0.0125`
如果一个十六进制数的指数为`exp`那这个数相当于基数和2^exp的乘积
* `0xFp2` 表示 15 × 2^2等于 `60.0`
* `0xFp-2` 表示 15 × 2^-2等于 `3.75`
@@ -375,9 +383,9 @@ let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
```
现在两个数字的类型都是`UInt16`,可以进行相加。目标常量`twoThousandAndOne`的类型被推断为`UInt16`,因为它是两个`UInt16`值的和。
现在两个数字的类型都是 `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>
### 整数和浮点数转换
@@ -391,16 +399,16 @@ let pi = Double(three) + pointOneFourOneFiveNine
// pi 3.14159 Double
```
这个例子中,常量`three`的值被用来创建一个`Double`类型的值,所以加号两边的数类型须相同。如果不进行转换,两者无法相加。
这个例子中,常量 `three` 的值被用来创建一个 `Double` 类型的值,所以加号两边的数类型须相同。如果不进行转换,两者无法相加。
浮点数到整数的反向转换同样行,整数类型可以用`Double`或者`Float`类型来初始化:
浮点数到整数的反向转换同样行,整数类型可以用 `Double` 或者 `Float` 类型来初始化:
```swift
let integerPi = Int(pi)
// integerPi 3 Int
```
当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说`4.75`会变成`4``-3.9`会变成`-3`
当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说 `4.75` 会变成 `4``-3.9` 会变成 `-3`
> 注意:
结合数字类常量和变量不同于结合数字类字面量。字面量`3`可以直接和字面量`0.14159`相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。
@@ -428,16 +436,16 @@ var maxAmplitudeFound = AudioSample.min
<a name="booleans"></a>
## 布尔值
Swift 有一个基本的布尔Boolean类型叫做`Bool`。布尔值指逻辑上的值因为它们只能是真或者假。Swift 有两个布尔常量,`true`和`false`
Swift 有一个基本的布尔Boolean类型叫做`Bool`。布尔值指逻辑上的值因为它们只能是真或者假。Swift 有两个布尔常量,`true``false`
```swift
let orangesAreOrange = true
let turnipsAreDelicious = false
```
`orangesAreOrange`和`turnipsAreDelicious`的类型会被推断为`Bool`,因为它们的初值是布尔字面量。就像之前提到的`Int`和`Double`一样,如果你创建变量的时候给它们赋值`true`或者`false`,那你不需要将常量或者变量声明为`Bool`类型。初始化常量或者变量的时候如果所赋的值类型已知,就可以触发类型推断,这让 Swift 代码更加简洁并且可读性更高。
`orangesAreOrange``turnipsAreDelicious` 的类型会被推断为 `Bool`,因为它们的初值是布尔字面量。就像之前提到的 `Int``Double` 一样,如果你创建变量的时候给它们赋值 `true` 或者 `false`,那你不需要将常量或者变量声明为 `Bool` 类型。初始化常量或者变量的时候如果所赋的值类型已知,就可以触发类型推断,这让 Swift 代码更加简洁并且可读性更高。
当你编写条件语句比如`if`语句的时候,布尔值非常有用:
当你编写条件语句比如 `if` 语句的时候,布尔值非常有用:
```swift
if turnipsAreDelicious {
@@ -450,7 +458,7 @@ if turnipsAreDelicious {
条件语句,例如`if`,请参考[控制流](./05_Control_Flow.html)。
如果你在需要使用`Bool`类型的地方使用了非布尔值Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误:
如果你在需要使用 `Bool` 类型的地方使用了非布尔值Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误:
```swift
let i = 1
@@ -468,7 +476,7 @@ if i == 1 {
}
```
`i == 1`的比较结果是`Bool`类型,所以第二个例子可以通过类型检查。类似`i == 1`这样的比较,请参考[基本操作符](./05_Control_Flow.html)。
`i == 1` 的比较结果是 `Bool` 类型,所以第二个例子可以通过类型检查。类似 `i == 1` 这样的比较,请参考[基本操作符](./05_Control_Flow.html)。
和 Swift 中的其他类型安全的例子一样,这个方法可以避免错误并保证这块代码的意图总是清晰的。
@@ -477,16 +485,16 @@ if i == 1 {
*元组tuples*把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型。
下面这个例子中,`(404, "Not Found")`是一个描述 *HTTP 状态码HTTP status code*的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个`404 Not Found`状态码。
下面这个例子中,`(404, "Not Found")` 是一个描述 *HTTP 状态码HTTP status code*的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个 `404 Not Found` 状态码。
```swift
let http404Error = (404, "Not Found")
// http404Error (Int, String) (404, "Not Found")
```
`(404, "Not Found")`元组把一个`Int`值和一个`String`值组合起来表示 HTTP 状态码的两个部分:一个数字和一个人类可读的描述。这个元组可以被描述为“一个类型为`(Int, String)`的元组”。
`(404, "Not Found")` 元组把一个 `Int` 值和一个 `String` 值组合起来表示 HTTP 状态码的两个部分:一个数字和一个人类可读的描述。这个元组可以被描述为“一个类型为 `(Int, String)` 的元组”。
你可以把任意顺序的类型组合成一个元组,这个元组可以包含所有类型。只要你想,你可以创建一个类型为`(Int, Int, Int)`或者`(String, Bool)`或者其他任何你想要的组合的元组。
你可以把任意顺序的类型组合成一个元组,这个元组可以包含所有类型。只要你想,你可以创建一个类型为 `(Int, Int, Int)` 或者 `(String, Bool)` 或者其他任何你想要的组合的元组。
你可以将一个元组的内容分解decompose成单独的常量和变量然后你就可以正常使用它们了
@@ -530,7 +538,7 @@ 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)。
@@ -547,11 +555,11 @@ print("The status message is \(http200Status.description)")
* 没有值
> 注意:
C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回`nil``nil`表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型Objective-C 方法一般会返回一个特殊值(比如`NSNotFound`来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而Swift 的可选类型可以让你暗示_任意类型_的值缺失,并不需要一个特殊值。
C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回`nil``nil`表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型Objective-C 方法一般会返回一个特殊值(比如`NSNotFound`来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而Swift 的可选类型可以让你暗示*任意类型*的值缺失,并不需要一个特殊值。
来看一个例子。Swift 的`Int`类型有一种构造器,作用是将一个`String`值转换成一个`Int`值。然而,并不是所有的字符串都可以转换成一个整数。字符串`"123"`可以被转换成数字`123`,但是字符串`"hello, world"`不行。
来看一个例子。Swift 的 `Int` 类型有一种构造器,作用是将一个 `String` 值转换成一个 `Int` 值。然而,并不是所有的字符串都可以转换成一个整数。字符串 `"123"` 可以被转换成数字 `123` ,但是字符串 `"hello, world"` 不行。
下面的例子使用这种构造器来尝试将一个`String`转换成`Int`
下面的例子使用这种构造器来尝试将一个 `String` 转换成 `Int`
```swift
let possibleNumber = "123"
@@ -559,7 +567,7 @@ let convertedNumber = Int(possibleNumber)
// convertedNumber "Int?" "optional Int"
```
因为该构造器可能会失败,所以它返回一个_可选类型_optional`Int`,而不是一个`Int`。一个可选的`Int`被写作`Int?`而不是`Int`。问号暗示包含的值是可选类型,也就是说可能包含`Int`值也可能*不包含值*。(不能包含其他任何值比如`Bool`值或者`String`值。只能是`Int`或者什么都没有。)
因为该构造器可能会失败,所以它返回一个*可选类型*optional`Int`,而不是一个 `Int`。一个可选的 `Int` 被写作 `Int?` 而不是 `Int`。问号暗示包含的值是可选类型,也就是说可能包含 `Int` 值也可能*不包含值*。(不能包含其他任何值比如 `Bool` 值或者 `String` 值。只能是 `Int` 或者什么都没有。)
<a name="nil"></a>
### nil
@@ -576,7 +584,7 @@ serverResponseCode = nil
> 注意:
`nil`不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为`nil`
如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 `nil`
```swift
var surveyAnswer: String?
@@ -584,14 +592,14 @@ var surveyAnswer: String?
```
> 注意:
Swift 的`nil`和 Objective-C 中的`nil`并不一样。在 Objective-C 中,`nil`是一个指向不存在对象的指针。在 Swift 中,`nil`不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为`nil`,不只是对象类型。
Swift 的 `nil` 和 Objective-C 中的 `nil` 并不一样。在 Objective-C 中,`nil` 是一个指向不存在对象的指针。在 Swift 中,`nil` 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 `nil`,不只是对象类型。
<a name="if"></a>
### if 语句以及强制解析
你可以使用`if`语句和`nil`比较来判断一个可选值是否包含值。你可以使用“相等”(`==`)或“不等”(`!=`)来执行比较。
你可以使用 `if` 语句和 `nil` 比较来判断一个可选值是否包含值。你可以使用“相等”(`==`)或“不等”(`!=`)来执行比较。
如果可选类型有值,它将不等于`nil`:
如果可选类型有值,它将不等于 `nil`
```swift
if convertedNumber != nil {
@@ -599,7 +607,8 @@ if convertedNumber != nil {
}
// "convertedNumber contains some integer value."
```
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(`!`来获取值。这个惊叹号表示“我知道这个可选有值请使用它。”这被称为可选值的_强制解析forced unwrapping_
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(`!`)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的*强制解析forced unwrapping*
```swift
if convertedNumber != nil {
@@ -608,17 +617,17 @@ if convertedNumber != nil {
// "convertedNumber has an integer value of 123."
```
更多关于`if`语句的内容,请参考[控制流](05_Control_Flow.html)。
更多关于 `if` 语句的内容,请参考[控制流](05_Control_Flow.html)。
> 注意:
使用`!`来获取一个不存在的可选值会导致运行时错误。使用`!`来强制解析值之前,一定要确定可选包含一个非`nil`的值。
使用 `!` 来获取一个不存在的可选值会导致运行时错误。使用 `!` 来强制解析值之前,一定要确定可选包含一个非 `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`语句中写一个可选绑定:
像下面这样在 `if` 语句中写一个可选绑定:
```swift
if let constantName = someOptional {
@@ -626,7 +635,7 @@ if let constantName = someOptional {
}
```
你可以像上面这样使用可选绑定来重写`possibleNumber`这个[例子](./01_The_Basics.html#optionals)
你可以像上面这样使用可选绑定来重写 `possibleNumber` 这个[例子](./01_The_Basics.html#optionals)
```swift
if let actualNumber = Int(possibleNumber) {
@@ -639,25 +648,37 @@ if let actualNumber = Int(possibleNumber) {
这段代码可以被理解为:
“如果`Int(possibleNumber)`返回的可选`Int`包含一个值,创建一个叫做`actualNumber`的新常量并将可选包含的值赋给它。”
“如果 `Int(possibleNumber)` 返回的可选 `Int` 包含一个值,创建一个叫做 `actualNumber` 的新常量并将可选包含的值赋给它。”
如果转换成功,`actualNumber`常量可以在`if`语句的第一个分支中使用。它已经被可选类型 _包含的_ 值初始化过,所以不需要再使用`!`后缀来获取它的值。在这个例子中,`actualNumber`只被用来输出转换结果。
如果转换成功,`actualNumber` 常量可以在 `if` 语句的第一个分支中使用。它已经被可选类型 *包含的* 值初始化过,所以不需要再使用 `!` 后缀来获取它的值。在这个例子中,`actualNumber` 只被用来输出转换结果。
你可以在可选绑定中使用常量和变量。如果你想在`if`语句的第一个分支中操作`actualNumber`的值,你可以改成`if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。
你可以在可选绑定中使用常量和变量。如果你想在`if`语句的第一个分支中操作 `actualNumber` 的值,你可以改成 `if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。
你可以包含多个可选绑定在`if`语句中,并使用`where`子句做布尔值判断。
你可以包含多个可选绑定或多个布尔条件在一个 `if` 语句中,只要使用逗号分开就行。只要有任意一个可选绑定的值为`nil`,或者任意一个布尔条件为`false`,则整个`if`条件判断为`false`,这时你就需要使用嵌套 `if` 条件语句来处理,如下所示:
```swift
if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber < secondNumber {
print("\(firstNumber) < \(secondNumber)")
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// prints "4 < 42"
// Prints "4 < 42 < 100"
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
// Prints "4 < 42 < 100"
```
> 注意:
> 在 `if` 条件语句中使用常量和变量来创建一个可选绑定,仅在 `if` 语句的句中(`body`)中才能获取到值。相反,在 `guard` 语句中使用常量和变量来创建一个可选绑定,仅在 `guard` 语句外且在语句后才能获取到值,请参考[控制流](./05_Control_Flow#early_exit.html)。
<a name="implicityly_unwrapped_optionals"></a>
### 隐式解析可选类型
如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过`if`语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。
如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过 `if` 语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。
有时候在程序架构中第一次被赋值之后可以确定一个可选类型_总会_有值。在这种情况下每次都要判断和解析可选值是非常低效的因为可以确定它总会有值。
@@ -665,7 +686,7 @@ if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber < seco
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考[无主引用以及隐式解析可选属性](./16_Automatic_Reference_Counting.html#unowned_references_and_implicitly_unwrapped_optional_properties)。
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型`String`和隐式解析可选类型`String`之间的区别:
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型 `String` 和隐式解析可选类型 `String` 之间的区别:
```swift
let possibleString: String? = "An optional string."
@@ -678,7 +699,7 @@ let implicitString: String = assumedString // 不需要感叹号
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。
> 注意:
如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。
> 如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。
你仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:
@@ -699,11 +720,11 @@ if let definiteString = assumedString {
```
> 注意:
如果一个变量之后可能变成`nil`的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是`nil`的话,请使用普通可选类型。
> 如果一个变量之后可能变成`nil`的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是`nil`的话,请使用普通可选类型。
<a name="error_handling"></a>
## 错误处理
你可以使用*错误处理error handling*来应对程序执行中可能会遇到的错误条件。
你可以使用 *错误处理error handling* 来应对程序执行中可能会遇到的错误条件。
相对于可选中运用值的存在与缺失来表达函数的成功与失败,错误处理可以推断失败的原因,并传播至程序的其他部分。
@@ -734,35 +755,35 @@ do {
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch Error.OutOfCleanDishes {
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch Error.MissingIngredients(let ingredients) {
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
```
在此例中,`makeASandwich()`(做一个三明治)函数会抛出一个错误消息如果没有干净的盘子或者某个原料缺失。因为`makeASandwich()`抛出错误,函数调用被包裹在`try`表达式中。将函数包裹在一个`do`语句中,任何被抛出的错误会被传播到提供的`catch`从句中。
在此例中,`makeASandwich()`(做一个三明治)函数会抛出一个错误消息如果没有干净的盘子或者某个原料缺失。因为 `makeASandwich()` 抛出错误,函数调用被包裹在 `try` 表达式中。将函数包裹在一个 `do` 语句中任何被抛出的错误会被传播到提供的 `catch` 从句中。
如果没有错误被抛出, `eatASandwich()`函数会被调用。如果一个匹配`Error.OutOfCleanDishes`的错误被抛出,`washDishes`函数会被调用。如果一个匹配`Error.MissingIngredients`的错误被抛出,`buyGroceries(_:)`函数会被调用,并且使用`catch`所捕捉到的关联值`[String]`作为参数。
如果没有错误被抛出`eatASandwich()` 函数会被调用。如果一个匹配 `SandwichError.outOfCleanDishes` 的错误被抛出`washDishes()` 函数会被调用。如果一个匹配 `SandwichError.missingIngredients` 的错误被抛出,`buyGroceries(_:)` 函数会被调用,并且使用 `catch` 所捕捉到的关联值 `[String]` 作为参数。
抛出,捕捉,以及传播错误会在[错误处理](./18_Error_Handling.html)章节详细说明。
<a name="assertions"></a>
## 断言
可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能没办法继续执行。这时,你可以在你的代码中触发一个*断言assertion*来结束代码运行并通过调试来找到值缺失的原因。
可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能没办法继续执行。这时,你可以在你的代码中触发一个 *断言assertion* 来结束代码运行并通过调试来找到值缺失的原因。
### 使用断言进行调试
断言会在运行时判断一个逻辑条件是否为`true`。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为`true`,代码运行会继续进行;如果条件判断为`false`,代码执行结束,你的应用被终止。
断言会在运行时判断一个逻辑条件是否为 `true`。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为 `true`,代码运行会继续进行;如果条件判断为 `false`,代码执行结束,你的应用被终止。
如果你的代码在调试环境下触发了一个断言,比如你在 Xcode 中构建并运行一个应用,你可以清楚地看到不合法的状态发生在哪里并检查断言被触发时你的应用的状态。此外,断言允许你附加一条调试信息。
你可以使用全局`assert(_:_:file:line:)`函数来写一个断言。向这个函数传入一个结果为`true`或者`false`的表达式以及一条信息,当表达式的结果为`false`的时候这条信息会被显示:
你可以使用全局 `assert(_:_:file:line:)` 函数来写一个断言。向这个函数传入一个结果为 `true` 或者 `false` 的表达式以及一条信息,当表达式的结果为 `false` 的时候这条信息会被显示:
```swift
let age = -3
@@ -770,7 +791,7 @@ assert(age >= 0, "A person's age cannot be less than zero")
// age < 0
```
在这个例子中,只有`age >= 0`为`true`的时候,即`age`的值非负的时候,代码才会继续执行。如果`age`的值是负数,就像代码中那样,`age >= 0`为`false`,断言被触发,终止应用。
在这个例子中,只有 `age >= 0``true` 的时候,即 `age` 的值非负的时候,代码才会继续执行。如果 `age` 的值是负数,就像代码中那样,`age >= 0``false`,断言被触发,终止应用。
如果不需要断言信息,可以省略,就像这样:
@@ -779,7 +800,7 @@ assert(age >= 0)
```
> 注意:
当代码使用优化编译的时候,断言将会被禁用,例如在 Xcode 中,使用默认的 target Release 配置选项来 build 时,断言会被禁用。
> 当代码使用优化编译的时候,断言将会被禁用,例如在 Xcode 中,使用默认的 target Release 配置选项来 build 时,断言会被禁用。
### 何时使用断言
@@ -787,11 +808,9 @@ assert(age >= 0)
* 整数类型的下标索引被传入一个自定义下标实现,但是下标索引值可能太小或者太大。
* 需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。
* 一个可选值现在是`nil`,但是后面的代码运行需要一个非`nil`值。
* 一个可选值现在是 `nil`,但是后面的代码运行需要一个非 `nil` 值。
请参考[下标](./12_Subscripts.html)和[函数](./06_Functions.html)。
> 注意:
断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。
> 断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。

View File

@@ -17,6 +17,8 @@
> 3.0
> 校对:[CMB](https://github.com/chenmingbiao)
>
> 版本日期2016-09-13
本页包含内容:

View File

@@ -12,7 +12,10 @@
> 校对:[shanks](http://codebuild.me)
> 2.2
> 校对:[SketchK](https://github.com/SketchK) 2016-05-11
> 校对:[SketchK](https://github.com/SketchK) 2016-05-11
>
> 3.0
> 校对:[shanks](http://codebuild.me) 2016-10-09
本页包含内容:
@@ -46,7 +49,7 @@ Swift 的`Arrays`、`Sets`和`Dictionaries`类型被实现为*泛型集合*。
> 注意:
Swift 的`Array`类型被桥接到`Foundation`中的`NSArray`类。
更多关于在`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) 一书
更多关于在`Foundation``Cocoa`中使用`Array`的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 3)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分
<a name="array_type_shorthand_syntax"></a>
### 数组的简单语法
@@ -81,7 +84,7 @@ someInts = []
Swift 中的`Array`类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量(`count`)和适当类型的初始值(`repeatedValue`)传入数组构造函数:
```swift
var threeDoubles = [Double](count: 3, repeatedValue:0.0)
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles [Double] [0.0, 0.0, 0.0]
```
@@ -91,7 +94,7 @@ var threeDoubles = [Double](count: 3, repeatedValue:0.0)
我们可以使用加法操作符(`+`)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来:
```swift
var anotherThreeDoubles = [Double](count: 3, repeatedValue: 2.5)
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles [Double] [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
@@ -194,20 +197,20 @@ shoppingList[4...6] = ["Bananas", "Apples"]
不可以用下标访问的形式去在数组尾部添加新项。
调用数组的`insert(_:atIndex:)`方法来在某个具体索引值之前添加数据项:
调用数组的`insert(_:at:)`方法来在某个具体索引值之前添加数据项:
```swift
shoppingList.insert("Maple Syrup", atIndex: 0)
shoppingList.insert("Maple Syrup", at: 0)
// shoppingList 7
// "Maple Syrup"
```
这次`insert(_:atIndex:)`方法调用把值为`"Maple Syrup"`的新数据项插入列表的最开始位置,并且使用`0`作为索引值。
这次`insert(_:at:)`方法调用把值为`"Maple Syrup"`的新数据项插入列表的最开始位置,并且使用`0`作为索引值。
类似的我们可以使用`removeAtIndex(_:)`方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它):
类似的我们可以使用`remove(at:)`方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它):
```swift
let mapleSyrup = shoppingList.removeAtIndex(0)
let mapleSyrup = remove(at: 0)
// 0
// shoppingList 6 Maple Syrup
// mapleSyrup "Maple Syrup"
@@ -222,7 +225,7 @@ firstItem = shoppingList[0]
// firstItem "Six eggs"
```
如果我们只想把数组中的最后一项移除,可以使用`removeLast()`方法而不是`removeAtIndex(_:)`方法来避免我们需要获取数组的`count`属性。就像后者一样,前者也会返回被移除的数据项:
如果我们只想把数组中的最后一项移除,可以使用`removeLast()`方法而不是`remove(at:)`方法来避免我们需要获取数组的`count`属性。就像后者一样,前者也会返回被移除的数据项:
```swift
let apples = shoppingList.removeLast()
@@ -269,7 +272,7 @@ for (index, value) in shoppingList.enumerate() {
> 注意:
> Swift的`Set`类型被桥接到`Foundation`中的`NSSet`类。
> 关于使用`Foundation`和`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)。
> 关于使用`Foundation`和`Cocoa`中`Set`的知识,参见 [*Using Swift with Cocoa and Obejective-C(Swift 3)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分
<a name="hash_values_for_set_types"></a>
#### 集合类型的哈希值
@@ -410,10 +413,10 @@ for genre in favoriteGenres {
更多关于`for-in`循环的信息,参见[For 循环](./05_Control_Flow.html#for_loops)。
Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中的值可以使用`sort()`方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符'<'对元素进行比较的结果来确定.
Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中的值可以使用`sorted()`方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符'<'对元素进行比较的结果来确定.
```swift
for genre in favoriteGenres.sort() {
for genre in favoriteGenres.sorted() {
print("\(genre)")
}
// prints "Classical"
@@ -433,10 +436,10 @@ for genre in favoriteGenres.sort() {
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/setVennDiagram_2x.png)
* 使用`intersect(_:)`方法根据两个集合中都包含的值创建的一个新的集合。
* 使用`exclusiveOr(_:)`方法根据在一个集合中但不在两个集合中的值创建一个新的集合。
* 使用`intersection(_:)`方法根据两个集合中都包含的值创建的一个新的集合。
* 使用`symmetricDifference(_:)`方法根据在一个集合中但不在两个集合中的值创建一个新的集合。
* 使用`union(_:)`方法根据两个集合的值创建一个新的集合。
* 使用`subtract(_:)`方法根据不在该集合中的值创建一个新的集合。
* 使用`subtracting(_:)`方法根据不在该集合中的值创建一个新的集合。
```swift
let oddDigits: Set = [1, 3, 5, 7, 9]
@@ -445,11 +448,11 @@ let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
oddDigits.union(evenDigits).sort()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersect(evenDigits).sort()
oddDigits. intersection(evenDigits).sorted()
// []
oddDigits.subtract(singleDigitPrimeNumbers).sort()
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.exclusiveOr(singleDigitPrimeNumbers).sort()
oddDigits. symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]
```
@@ -461,21 +464,21 @@ oddDigits.exclusiveOr(singleDigitPrimeNumbers).sort()
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/setEulerDiagram_2x.png)
* 使用“是否相等”运算符(`==`)来判断两个集合是否包含全部相同的值。
* 使用`isSubsetOf(_:)`方法来判断一个集合中的值是否也被包含在另外一个集合中。
* 使用`isSupersetOf(_:)`方法来判断一个集合中包含另一个集合中所有的值。
* 使用`isStrictSubsetOf(_:)`或者`isStrictSupersetOf(_:)`方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。
* 使用`isDisjointWith(_:)`方法来判断两个集合是否不含有相同的值(是否没有交集)。
* 使用`isSubset(of:)`方法来判断一个集合中的值是否也被包含在另外一个集合中。
* 使用`isSuperset(of:)`方法来判断一个集合中包含另一个集合中所有的值。
* 使用`isStrictSubset(of:)`或者`isStrictSuperset(of:)`方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。
* 使用`isDisjoint(with:)`方法来判断两个集合是否不含有相同的值(是否没有交集)。
```swift
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]
houseAnimals.isSubsetOf(farmAnimals)
houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSupersetOf(houseAnimals)
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjointWith(cityAnimals)
farmAnimals.isDisjoint(with: cityAnimals)
// true
```
@@ -486,7 +489,7 @@ farmAnimals.isDisjointWith(cityAnimals)
> 注意:
> Swift 的`Dictionary`类型被桥接到`Foundation`的`NSDictionary`类。
> 更多关于在`Foundation`和`Cocoa`中使用`Dictionary`类型的信息,参见 [*Using Swift with Cocoa and Objective-C (Swift 2.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 一书
> 更多关于在`Foundation`和`Cocoa`中使用`Dictionary`类型的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 3)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分
<a name="dictionary_type_shorthand_syntax"></a>
## 字典类型快捷语法
@@ -624,10 +627,10 @@ airports["APL"] = nil
// APL
```
此外,`removeValueForKey(_:)`方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的值或者在没有值的情况下返回`nil`
此外,`removeValue(forKey:)`方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的值或者在没有值的情况下返回`nil`
```swift
if let removedValue = airports.removeValueForKey("DUB") {
if let removedValue = airports. removeValue(forKey: "DUB") {
print("The removed airport's name is \(removedValue).")
} else {
print("The airports dictionary does not contain a value for DUB.")
@@ -676,4 +679,4 @@ let airportNames = [String](airports.values)
// airportNames ["Toronto Pearson", "London Heathrow"]
```
Swift 的字典类型是无序集合类型。为了以特定的顺序遍历字典的键或值,可以对字典的`keys``values`属性使用`sort()`方法。
Swift 的字典类型是无序集合类型。为了以特定的顺序遍历字典的键或值,可以对字典的`keys``values`属性使用`sorted()`方法。

View File

@@ -16,7 +16,8 @@
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-12
> 3.0
> 翻译 [crayygy](https://github.com/crayygy) 2016-09-12
> 翻译: [crayygy](https://github.com/crayygy) 2016-09-12
> 校对: [shanks](http://codebuild.me) 2016-09-27
本页包含内容:
- [函数定义与调用Defining and Calling Functions](#Defining_and_Calling_Functions)
@@ -40,7 +41,7 @@ Swift 统一的函数语法非常的灵活,可以用来表示任何函数,
每个函数有个函数名,用来描述函数执行的任务。要使用一个函数时,用函数名来“调用”这个函数,并传给它匹配的输入值(称作 *实参* *arguments*)。函数的实参必须与函数参数表里参数的顺序一致。
下面例子中的函数的名字是`sayHello(_:)`,之所以叫这个名字,是因为这个函数用一个人的名字当做输入,并返回向这个人问候的语句。为了完成这个任务,你需要定义一个输入参数——一个叫做 `personName``String` 值,和一个包含给这个人问候语的 `String` 类型的返回值:
下面例子中的函数的名字是`greet(_:)`,之所以叫这个名字,是因为这个函数用一个人的名字当做输入,并返回向这个人问候的语句。为了完成这个任务,你需要定义一个输入参数——一个叫做 `personName``String` 值,和一个包含给这个人问候语的 `String` 类型的返回值:
```swift
@@ -61,15 +62,15 @@ print(greet(person: "Brian"))
// "Hello, Brian!"
```
调用 `sayHello(_:)` 函数时,在圆括号中传给它一个 `String` 类型的实参,例如 `sayHello("Anna")`。正如上面所示,因为这个函数返回一个 `String` 类型的值,所以`sayHello` 可以被包含在 `print(_:separator:terminator:)` 的调用中,用来输出这个函数的返回值。
调用 `greet(_:)` 函数时,在圆括号中传给它一个 `String` 类型的实参,例如 `greet("Anna")`。正如上面所示,因为这个函数返回一个 `String` 类型的值,所以`greet ` 可以被包含在 `print(_:separator:terminator:)` 的调用中,用来输出这个函数的返回值。
>注意
`print(_:separator:terminator:)` 函数的第一个参数并没有设置一个标签,而其他的参数因为已经有了默认值,因此是可选的。关于这些函数语法上的变化详见下方关于 函数参数标签和参数名 以及 默认参数值。
`sayHello(_:)` 的函数体中,先定义了一个新的名为 `greeting``String` 常量,同时,把对 `personName` 的问候消息赋值给了 `greeting` 。然后用 `return` 关键字把这个问候返回出去。一旦 `return greeting` 被调用,该函数结束它的执行并返回 `greeting` 的当前值。
`greet(_:)` 的函数体中,先定义了一个新的名为 `greeting``String` 常量,同时,把对 `personName` 的问候消息赋值给了 `greeting` 。然后用 `return` 关键字把这个问候返回出去。一旦 `return greeting` 被调用,该函数结束它的执行并返回 `greeting` 的当前值。
你可以用不同的输入值多次调用 `sayHello(_:)`。上面的例子展示的是用`"Anna"``"Brian"`调用的结果,该函数分别返回了不同的结果。
你可以用不同的输入值多次调用 `greet(_:)`。上面的例子展示的是用`"Anna"``"Brian"`调用的结果,该函数分别返回了不同的结果。
为了简化这个函数的定义,可以将问候消息的创建和返回写成一句:
@@ -120,12 +121,12 @@ print(greet(person: "Tim", alreadyGreeted: true))
// "Hello again, Tim!"
```
你可以通过在括号内使用逗号分隔来传递一个`String`参数值和一个标识为`alreadyGreeted``Bool`值,来调用`sayHello(_:alreadyGreeted:)`函数。注意这个函数和上面`greet(person:)`是不同的。虽然它们都有着同样的名字`greet`,但是`greet(person:alreadyGreeted:)`函数需要两个参数,而`greet(person:)`只需要一个参数。
你可以通过在括号内使用逗号分隔来传递一个`String`参数值和一个标识为`alreadyGreeted``Bool`值,来调用`greet(person:alreadyGreeted:)`函数。注意这个函数和上面`greet(person:)`是不同的。虽然它们都有着同样的名字`greet`,但是`greet(person:alreadyGreeted:)`函数需要两个参数,而`greet(person:)`只需要一个参数。
<a name="functions_without_return_values"></a>
### 无返回值函数 (Functions Without Return Values)
函数可以没有返回值。下面是 `sayHello(_:)` 函数的另一个版本,`sayGoodbye(_:)`这个函数直接打印一个`String`值,而不是返回它:
函数可以没有返回值。下面是 `greet(person:)` 函数的另一个版本,这个函数直接打印一个`String`值,而不是返回它:
```swift
func greet(person: String) {
@@ -138,7 +139,7 @@ greet(person: "Dave")
因为这个函数不需要返回值,所以这个函数的定义中没有返回箭头(->)和返回类型。
>注意
严格上来说,虽然没有返回值被定义,`sayGoodbye(_:)` 函数依然返回了值。没有定义返回类型的函数会返回一个特殊的`Void`值。它其实是一个空的元组tuple没有任何元素可以写成()。
严格上来说,虽然没有返回值被定义,`greet(person:)` 函数依然返回了值。没有定义返回类型的函数会返回一个特殊的`Void`值。它其实是一个空的元组tuple没有任何元素可以写成()。
被调用时,一个函数的返回值可以被忽略:
@@ -157,7 +158,7 @@ printWithoutCounting(string: "hello, world")
// "hello, world"
```
第一个函数 `printAndCount(_:)`,输出一个字符串并返回 `Int` 类型的字符数。第二个函数 `printWithoutCounting`调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。
第一个函数 `printAndCount(string:)`,输出一个字符串并返回 `Int` 类型的字符数。第二个函数 `printWithoutCounting(string:)`调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。
>注意
返回值可以被忽略但定义了有返回值的函数必须返回一个值如果在函数定义底部没有返回任何值将导致编译时错误compile-time error
@@ -168,7 +169,7 @@ printWithoutCounting(string: "hello, world")
你可以用元组tuple类型让多个值作为一个复合值从函数中返回。
下例中定义了一个名为 `minMax(_:)` 的函数,作用是在一个 `Int` 类型的数组中找出最小值与最大值。
下例中定义了一个名为 `minMax(array:)` 的函数,作用是在一个 `Int` 类型的数组中找出最小值与最大值。
```swift
func minMax(array: [Int]) -> (min: Int, max: Int) {
@@ -185,9 +186,9 @@ func minMax(array: [Int]) -> (min: Int, max: Int) {
}
```
`minMax(_:)` 函数返回一个包含两个 `Int` 值的元组,这些值被标记为 `min``max` ,以便查询函数的返回值时可以通过名字访问它们。
`minMax(array:)` 函数返回一个包含两个 `Int` 值的元组,这些值被标记为 `min``max` ,以便查询函数的返回值时可以通过名字访问它们。
`minMax(_:)` 的函数体中,在开始的时候设置两个工作变量 `currentMin``currentMax` 的值为数组中的第一个数。然后函数会遍历数组中剩余的值并检查该值是否比 `currentMin``currentMax` 更小或更大。最后数组中的最小值与最大值作为一个包含两个 `Int` 值的元组返回。
`minMax(array:)` 的函数体中,在开始的时候设置两个工作变量 `currentMin``currentMax` 的值为数组中的第一个数。然后函数会遍历数组中剩余的值并检查该值是否比 `currentMin``currentMax` 更小或更大。最后数组中的最小值与最大值作为一个包含两个 `Int` 值的元组返回。
因为元组的成员值已被命名,因此可以通过 `.` 语法来检索找到的最小值与最大值:
@@ -208,9 +209,9 @@ print("min is \(bounds.min) and max is \(bounds.max)")
可选元组类型如 `(Int, Int)?` 与元组包含可选类型如 `(Int?, Int?)` 是不同的.可选的元组类型,整个元组是可选的,而不只是元组中的每个元素值。
前面的 `minMax(_:)` 函数返回了一个包含两个 `Int` 值的元组。但是函数不会对传入的数组执行任何安全检查,如果 `array` 参数是一个空数组,如上定义的 `minMax(_:)` 在试图访问 `array[0]` 时会触发一个运行时错误(runtime error)。
前面的 `minMax(array:)` 函数返回了一个包含两个 `Int` 值的元组。但是函数不会对传入的数组执行任何安全检查,如果 `array` 参数是一个空数组,如上定义的 `minMax(array:)` 在试图访问 `array[0]` 时会触发一个运行时错误(runtime error)。
为了安全地处理这个“空数组”问题,将 `minMax(_:)` 函数改写为使用可选元组返回类型,并且当数组为空时返回 `nil`
为了安全地处理这个“空数组”问题,将 `minMax(array:)` 函数改写为使用可选元组返回类型,并且当数组为空时返回 `nil`
```swift
@@ -229,7 +230,7 @@ func minMax(array: [Int]) -> (min: Int, max: Int)? {
}
```
你可以使用可选绑定来检查 `minMax(_:)` 函数返回的是一个存在的元组值还是 `nil`
你可以使用可选绑定来检查 `minMax(array:)` 函数返回的是一个存在的元组值还是 `nil`
```swift
if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
@@ -296,7 +297,7 @@ someFunction(1, secondParameterName: 2)
<a name="default_parameter_values"></a>
### 默认参数值 (Default Parameter Values)
你可以在函数体中通过给参数赋值来为任意一个参数定义默认值Deafult Values)。当默认值被定义后,调用这个函数时可以忽略这个参数。
你可以在函数体中通过给参数赋值来为任意一个参数定义*默认值Deafult Value*。当默认值被定义后,调用这个函数时可以忽略这个参数。
```swift
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
@@ -312,7 +313,7 @@ someFunction(parameterWithoutDefault: 4) // parameterWithDefault is 12
<a name="variadic_parameters"></a>
### 可变参数 (Variadic Parameters)
一个可变参数variadic parameter可以接受零个或多个值。函数调用时你可以用可变参数来指定函数参数可以被传入不确定数量的输入值。通过在变量类型名后面加入`...`)的方式来定义可变参数。
一个*可变参数variadic parameter*可以接受零个或多个值。函数调用时,你可以用可变参数来指定函数参数可以被传入不确定数量的输入值。通过在变量类型名后面加入(`...`)的方式来定义可变参数。
可变参数的传入值在函数体中变为此类型的一个数组。例如,一个叫做 `numbers``Double...` 型可变参数,在函数体内可以当做一个叫 `numbers``[Double]` 型的数组常量。
@@ -338,7 +339,7 @@ arithmeticMean(3, 8.25, 18.75)
<a name="in_out_parameters"></a>
### 输入输出参数In-Out Parameters
函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误(compile-time error)。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值并且想要在这些修改在函数调用结束后仍然存在那么就应该把这个参数定义为输入输出参数In-Out Parameters
函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误(compile-time error)。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为*输入输出参数In-Out Parameters*
定义一个输入输出参数时,在参数定义前加 `inout` 关键字。一个`输入输出参数`有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看`输入输出参数`一节。
@@ -470,7 +471,7 @@ printMathResult(addTwoInts, 3, 5)
你可以用函数类型作为另一个函数的返回类型。你需要做的是在返回箭头(->)后写一个完整的函数类型。
下面的这个例子中定义了两个简单函数,分别是 `stepForward``stepBackward``stepForward`函数返回一个比输入值大 `1` 的值。`stepBackward` 函数返回一个比输入值小 `1` 的值。这两个函数的类型都是 `(Int) -> Int`
下面的这个例子中定义了两个简单函数,分别是 `stepForward(_:)``stepBackward(_:)``stepForward(_:)`函数返回一个比输入值大 `1` 的值。`stepBackward(_:)` 函数返回一个比输入值小 `1` 的值。这两个函数的类型都是 `(Int) -> Int`
```swift
func stepForward(_ input: Int) -> Int {
@@ -481,7 +482,7 @@ func stepBackward(_ input: Int) -> Int {
}
```
如下名为 `chooseStepFunction(_:)` 的函数,它的返回类型是 `(Int) -> Int` 类型的函数。`chooseStepFunction(_:)` 根据布尔值 `backwards` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数:
如下名为 `chooseStepFunction(backward:)` 的函数,它的返回类型是 `(Int) -> Int` 类型的函数。`chooseStepFunction(backward:)` 根据布尔值 `backwards` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数:
```swift
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
@@ -489,7 +490,7 @@ func chooseStepFunction(backward: Bool) -> (Int) -> Int {
}
```
你现在可以用 `chooseStepFunction(_:)` 来获得两个函数其中的一个:
你现在可以用 `chooseStepFunction(backward:)` 来获得两个函数其中的一个:
```swift
var currentValue = 3
@@ -519,11 +520,11 @@ print("zero!")
<a name="Nested_Functions"></a>
## 嵌套函数 (Nested Functions)
到目前为止本章中你所见到的所有函数都叫`全局`函数global functions它们定义在`全局域`中。你也可以把函数定义在别的函数体中,称作 `嵌套函数`nested functions
到目前为止本章中你所见到的所有函数都叫*全局函数global functions*,它们定义在全局域中。你也可以把函数定义在别的函数体中,称作 *嵌套函数nested functions*
默认情况下嵌套函数是对外界不可见的但是可以被它们的外围函数enclosing function调用。一个外围函数也可以返回它的某一个嵌套函数使得这个函数可以在其他域中被使用。
你可以用返回嵌套函数的方式重写 `chooseStepFunction(_:)` 函数:
你可以用返回嵌套函数的方式重写 `chooseStepFunction(backward:)` 函数:
```swift
func chooseStepFunction(backward: Bool) -> (Int) -> Int {

View File

@@ -14,6 +14,9 @@
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-12
>
> 3.0
> 翻译:[Lanford](https://github.com/LanfordCai) 2016-09-19
本页包含内容:
@@ -21,15 +24,15 @@
- [尾随闭包Trailing Closures](#trailing_closures)
- [值捕获Capturing Values](#capturing_values)
- [闭包是引用类型Closures Are Reference Types](#closures_are_reference_types)
- [逃逸闭包(Nonescaping Closures) ](#nonescaping_closures)
- [逃逸闭包(Escaping Closures) ](#escaping_closures)
- [自动闭包Autoclosures](#autoclosures)
闭包是自包含的函数代码块可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块blocks以及其他一些编程语言中的匿名函数比较相似。
*闭包*是自包含的函数代码块可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块blocks以及其他一些编程语言中的匿名函数比较相似。
闭包可以捕获和存储其所在上下文中任意常量和变量的引用。这就是所谓的闭合包裹着这些常量和变量,俗称闭包。Swift 会为管理在捕获过程中涉及到的所有内存操作。
闭包可以捕获和存储其所在上下文中任意常量和变量的引用。*闭合包裹*常量和变量,所谓闭包。Swift 会为管理在捕获过程中涉及到的所有内存操作。
> 注意
> 如果不熟悉捕获capturing这个概念也不用担心可以在[值捕获](#capturing_values)章节对其进行详细了解。
> 注意
> 如果不熟悉捕获capturing这个概念也不用担心可以在[值捕获](#capturing_values)章节对其进行详细了解。
在[函数](./06_Functions.html)章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
@@ -40,7 +43,7 @@
Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下:
* 利用上下文推断参数和返回值类型
* 隐式返回单表达式闭包,即单表达式闭包可以省略`return`关键字
* 隐式返回单表达式闭包,即单表达式闭包可以省略 `return` 关键字
* 参数名称缩写
* 尾随Trailing闭包语法
@@ -48,43 +51,43 @@ Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进
## 闭包表达式Closure Expressions
[嵌套函数](./06_Functions.html#nested_function)是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在处理一些函数并需要将另外一些函数作为该函数的参数时。
[嵌套函数](./06_Functions.html#nested_function)是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在处理一些函数并需要将另外一些函数作为该函数的参数时。
闭包表达式是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。下面闭包表达式的例子通过使用几次迭代展示了`sort(_:)`方法定义和语法优化的方式。每一次迭代都用更简洁的方式描述了相同的功能。
*闭包表达式*是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。下面闭包表达式的例子通过使用几次迭代展示了 `sorted(by:)` 方法定义和语法优化的方式。每一次迭代都用更简洁的方式描述了相同的功能。
<a name="the_sorted_function"></a>
### sort 方法The Sort Method
### sorted 方法The Sorted Method
Swift 标准库提供了名为`sort`的方法,会根据提供的用于排序的闭包函数将已知类型数组中的值进行排序。一旦排序完成,`sort(_:)`方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被`sort(_:)`方法修改。
Swift 标准库提供了名为 `sorted(by:)` 的方法,会根据你所提供的用于排序的闭包函数将已知类型数组中的值进行排序。一旦排序完成,`sorted(by:)` 方法会返回一个与原数组大小相同包含同类型元素且元素已正确排序的新数组。原数组不会被 `sorted(by:)` 方法修改。
下面的闭包表达式示例使用`sort(_:)`方法对一个`String`类型的数组进行字母逆序排序.以下是初始数组
下面的闭包表达式示例使用 `sorted(by:)` 方法对一个 `String` 类型的数组进行字母逆序排序以下是初始数组:
```swift
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
```
`sort(_:)`方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回`true`,反之返回`false`
`sorted(by:)` 方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值*前面*,排序闭包函数需要返回`true`,反之返回`false`
该例子对一个`String`类型的数组进行排序,因此排序闭包函数类型需为`(String, String) -> Bool`
该例子对一个 `String` 类型的数组进行排序,因此排序闭包函数类型需为 `(String, String) -> Bool`
提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`sort(_:)`方法的参数传入:
提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为 `sorted(by:)` 方法的参数传入:
```swift
func backwards(s1: String, s2: String) -> Bool {
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversed = names.sort(backwards)
// reversed ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
var reversedNames = names.sorted(by: backward)
// reversedNames ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
```
如果第一个字符串(`s1`)大于第二个字符串(`s2``backwards(_:_:)`函数返回`true`,表示在新的数组中`s1`应该出现在`s2`前。对于字符串中的字符来说,“大于”表示“按照字母顺序较晚出现”。这意味着字母`"B"`大于字母`"A"`,字符串`"Tom"`大于字符串`"Tim"`。该闭包将进行字母逆序排序,`"Barry"`将会排在`"Alex"`之前。
如果第一个字符串(`s1`)大于第二个字符串(`s2``backward(_:_:)` 函数返回 `true`,表示在新的数组中 `s1` 应该出现在 `s2` 前。对于字符串中的字符来说,“大于”表示“按照字母顺序较晚出现”。这意味着字母 `"B"` 大于字母 `"A"` ,字符串 `"Tom"` 大于字符串 `"Tim"`。该闭包将进行字母逆序排序,`"Barry"` 将会排在 `"Alex"` 之前。
然而,这是一个相当冗长的方式,本质上只是写了一个单表达式函数 (`a > b`)。在下面的例子中,利用闭包表达式语法可以更好地构造一个内联排序闭包。
然而,以这种方式来编写一个实际上很简单的表达式(`a > b`),确实太过繁琐了。对于这个例子来说,利用闭包表达式语法可以更好地构造一个内联排序闭包。
<a name="closure_expression_syntax"></a>
### 闭包表达式语法Closure Expression Syntax
闭包表达式语法有如下一般形式:
闭包表达式语法有如下一般形式:
```swift
{ (parameters) -> returnType in
@@ -92,81 +95,80 @@ var reversed = names.sort(backwards)
}
```
闭包表达式语法可以使用常量、变量和`inout`类型作为参数,不能提供默认值。也可以在参数列表的最后使用可变参数。元组也可以作为参数和返回值。
闭包表达式的参数可以是inout参数,不能设定默认值。也可以使用具名的可变参数(译者注:但是如果可变参数不放在参数列表的最后一位的话,调用闭包的时时编译器将报错。可参考[这里](http://stackoverflow.com/questions/39548852/swift-3-0-closure-expression-what-if-the-variadic-parameters-not-at-the-last-pl)。元组也可以作为参数和返回值。
下面的例子展示了之前`backwards(_:_:)`函数对应的闭包表达式版本的代码:
下面的例子展示了之前 `backward(_:_:)` 函数对应的闭包表达式版本的代码:
```swift
reversed = names.sort({ (s1: String, s2: String) -> Bool in
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
```
需要注意的是内联闭包参数和返回值类型声明与`backwards(_:_:)`函数类型声明相同。在这两种方式中,都写成了`(s1: String, s2: String) -> Bool`。然而在内联闭包表达式中,函数和返回值类型都写在大括号内,而不是大括号外。
需要注意的是内联闭包参数和返回值类型声明与 `backward(_:_:)` 函数类型声明相同。在这两种方式中,都写成了 `(s1: String, s2: String) -> Bool`。然而在内联闭包表达式中,函数和返回值类型都写在*大括号内*,而不是大括号外。
闭包的函数体部分由关键字`in`引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。
由于这个闭包的函数体部分如此短,以至于可以将其改写成一行代码:
```swift
reversed = names.sort( { (s1: String, s2: String) -> Bool in return s1 > s2 } )
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
```
该例中`sort(_:)`方法的整体调用保持不变,一对圆括号仍然包裹住了方法的整个参数。然而,参数现在变成了内联闭包。
该例中 `sorted(by:)` 方法的整体调用保持不变,一对圆括号仍然包裹住了方法的整个参数。然而,参数现在变成了内联闭包。
<a name="inferring_type_from_context"></a>
### 根据上下文推断类型Inferring Type From Context
因为排序闭包函数是作为`sort(_:)`方法的参数传入的Swift 可以推断其参数和返回值的类型。`sort(_:)`方法被一个字符串数组调用,因此其参数必须是`(String, String) -> Bool`类型的函数。这意味着`(String, String)``Bool`类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(`->`)和围绕在参数周围的括号也可以被省略:
因为排序闭包函数是作为 `sorted(by:)` 方法的参数传入的Swift 可以推断其参数和返回值的类型。`sorted(by:)` 方法被一个字符串数组调用,因此其参数必须是 `(String, String) -> Bool` 类型的函数。这意味着 `(String, String)``Bool` 类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(`->`)和围绕在参数周围的括号也可以被省略:
```swift
reversed = names.sort( { s1, s2 in return s1 > s2 } )
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
```
实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数或方法时,都可以推断出闭包的参数和返回值类型。
这意味着闭包作为函数或者方法的参数时,您几乎不需要利用完整格式构造内联闭包。
实际上,通过内联闭包表达式构造的闭包作为参数传递给函数或方法时,总是能够推断出闭包的参数和返回值类型。这意味着闭包作为函数或者方法的参数时,你几乎不需要利用完整格式构造内联闭包。
尽管如此,仍然可以明确写出有着完整格式的闭包。如果完整格式的闭包能够提高代码的可读性,则可以采用完整格式的闭包。而在`sort(_:)`方法这个例子里,闭包的目的就是排序。由于这个闭包是为了处理字符串数组的排序,因此读者能够推测出这个闭包是用于字符串处理的。
尽管如此,仍然可以明确写出有着完整格式的闭包。如果完整格式的闭包能够提高代码的可读性,则我们更鼓励采用完整格式的闭包。而在 `sorted(by:)` 方法这个例子里,显然闭包的目的就是排序。由于这个闭包是为了处理字符串数组的排序,因此读者能够推测出这个闭包是用于字符串处理的。
<a name="implicit_returns_from_single_expression_closures"></a>
### 单表达式闭包隐式返回Implicit Return From Single-Expression Clossures
### 单表达式闭包隐式返回Implicit Returns From Single-Expression Closures
单行表达式闭包可以通过省略`return`关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
单行表达式闭包可以通过省略 `return` 关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
```swift
reversed = names.sort( { s1, s2 in s1 > s2 } )
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
```
在这个例子中,`sort(_:)`方法的参数类型明确了闭包必须返回一个`Bool`类型值。因为闭包函数体只包含了一个单一表达式(`s1 > s2`),该表达式返回`Bool`类型值,因此这里没有歧义,`return`关键字可以省略。
在这个例子中,`sorted(by:)` 方法的参数类型明确了闭包必须返回一个 `Bool` 类型值。因为闭包函数体只包含了一个单一表达式(`s1 > s2`),该表达式返回 `Bool` 类型值,因此这里没有歧义,`return` 关键字可以省略。
<a name="shorthand_argument_names"></a>
### 参数名称缩写Shorthand Argument Names
Swift 自动为内联闭包提供了参数名称缩写功能,可以直接通过`$0``$1``$2`来顺序调用闭包的参数,以此类推。
Swift 自动为内联闭包提供了参数名称缩写功能,可以直接通过 `$0``$1``$2` 来顺序调用闭包的参数,以此类推。
如果在闭包表达式中使用参数名称缩写,可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。`in`关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
如果在闭包表达式中使用参数名称缩写,可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。`in`关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
```swift
reversed = names.sort( { $0 > $1 } )
reversedNames = names.sorted(by: { $0 > $1 } )
```
在这个例子中,`$0``$1`表示闭包中第一个和第二个`String`类型的参数。
在这个例子中,`$0``$1`表示闭包中第一个和第二个 `String` 类型的参数。
<a name="operator_functions"></a>
### 运算符函数Operator Functions
<a name="operator_methods"></a>
### 运算符方法Operator Methods
实际上还有一种更简短的方式来写上面例子中的闭包表达式。Swift 的`String`类型定义了关于大于号(`>`)的字符串实现,其作为一个函数接受两个`String`类型的参数并返回`Bool`类型的值。而这正好与`sort(_:)`方法的参数需要的函数类型相符合。因此,可以简单地传递一个大于号Swift 可以自动推断出想使用大于号的字符串函数实现:
实际上还有一种更简短的方式来写上面例子中的闭包表达式。Swift 的 `String` 类型定义了关于大于号(`>`)的字符串实现,其作为一个函数接受两个 `String` 类型的参数并返回 `Bool` 类型的值。而这正好与 `sorted(by:)` 方法的参数需要的函数类型相符合。因此,可以简单地传递一个大于号Swift 可以自动推断出想使用大于号的字符串函数实现:
```swift
reversed = names.sort(>)
reversedNames = names.sorted(by: >)
```
更多关于运算符表达式的内容请查看[运算符函数](./25_Advanced_Operators.html#operator_functions)。
更多关于运算符方法的内容请查看[运算符方法](./25_Advanced_Operators.html#operator_methods)。
<a name="trailing_closures"></a>
## 尾随闭包Trailing Closures
如果需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用*尾随闭包*来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用:
如果需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用*尾随闭包*来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签
```swift
func someFunctionThatTakesAClosure(closure: () -> Void) {
@@ -174,7 +176,7 @@ func someFunctionThatTakesAClosure(closure: () -> Void) {
}
// 使
someFunctionThatTakesAClosure({
someFunctionThatTakesAClosure(closure: {
//
})
@@ -184,23 +186,23 @@ someFunctionThatTakesAClosure() {
}
```
在[闭包表达式语法](#closure_expression_syntax)一节中作为`sort(_:)`方法参数的字符串排序闭包可以改写为:
在[闭包表达式语法](#closure_expression_syntax)一节中作为 `sorted(by:)` 方法参数的字符串排序闭包可以改写为:
```swift
reversed = names.sort() { $0 > $1 }
reversedNames = names.sorted() { $0 > $1 }
```
如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,甚至可以把`()`省略掉:
如果闭包表达式是函数或方法的唯一参数,则当你使用尾随闭包时,甚至可以把 `()` 省略掉:
```swift
reversed = names.sort { $0 > $1 }
reversedNames = names.sorted { $0 > $1 }
```
当闭包非常长以至于不能在一行中进行书写时尾随闭包变得非常有用。举例来说Swift 的`Array`类型有一个`map(_:)`方法,获取一个闭包表达式作为其唯一参数。该闭包函数会为数组中的每一个元素调用一次,并返回该元素所映射的值。具体的映射方式和返回值类型由闭包来指定。
当闭包非常长以至于不能在一行中进行书写时尾随闭包变得非常有用。举例来说Swift 的 `Array` 类型有一个 `map(_:)` 方法,这个方法获取一个闭包表达式作为其唯一参数。该闭包函数会为数组中的每一个元素调用一次,并返回该元素所映射的值。具体的映射方式和返回值类型由闭包来指定。
当提供给数组的闭包应用于每个数组元素后,`map(_:)`方法将返回一个新的数组,数组中包含了与原数组中的元素一一对应的映射后的值。
当提供给数组的闭包应用于每个数组元素后,`map(_:)` 方法将返回一个新的数组,数组中包含了与原数组中的元素一一对应的映射后的值。
下例介绍了如何在`map(_:)`方法中使用尾随闭包将`Int`类型数组`[16, 58, 510]`转换为包含对应`String`类型的值的数组`["OneSix", "FiveEight", "FiveOneZero"]`
下例介绍了如何在 `map(_:)` 方法中使用尾随闭包将 `Int` 类型数组 `[16, 58, 510]` 转换为包含对应 `String` 类型的值的数组`["OneSix", "FiveEight", "FiveOneZero"]`
```swift
let digitNames = [
@@ -210,92 +212,90 @@ let digitNames = [
let numbers = [16, 58, 510]
```
如上代码创建了一个数位和它们英文版本名字相映射的字典。同时还定义了一个准备转换为字符串数组的整型数组。
如上代码创建了一个整型数位和它们英文版本名字相映射的字典。同时还定义了一个准备转换为字符串数组的整型数组。
现在可以通过传递一个尾随闭包给`numbers``map(_:)`方法来创建对应的字符串版本数组:
现在可以通过传递一个尾随闭包给 `numbers` 数组的 `map(_:)` 方法来创建对应的字符串版本数组:
```swift
let strings = numbers.map {
(number) -> String in
var number = number
var output = ""
while number > 0 {
repeat {
output = digitNames[number % 10]! + output
number /= 10
}
} while number > 0
return output
}
// strings [String]
// ["OneSix", "FiveEight", "FiveOneZero"]
```
`map(_:)`为数组中每一个元素调用了闭包表达式。不需要指定闭包的输入参数`number`的类型,因为可以通过要映射的数组类型进行推断。
`map(_:)` 为数组中每一个元素调用了一次闭包表达式。不需要指定闭包的输入参数 `number` 的类型,因为可以通过要映射的数组类型进行推断。
在该例中,局部变量`number`的值由闭包中的`number`参数获得,因此可以在闭包函数体内对其进行修改,(闭包或者函数的参数总是常量),闭包表达式指定了返回类型为`String`,以表明存储映射值的新数组类型为`String`
在该例中,局部变量 `number` 的值由闭包中的 `number` 参数获得因此可以在闭包函数体内对其进行修改,(闭包或者函数的参数总是常量)闭包表达式指定了返回类型为 `String`,以表明存储映射值的新数组类型为 `String`
闭包表达式在每次被调用的时候创建了一个叫做`output`的字符串并返回。其使用求余运算符(`number % 10`)计算最后一位数字并利用`digitNames`字典获取所映射的字符串。
闭包表达式在每次被调用的时候创建了一个叫做 `output` 的字符串并返回。其使用求余运算符(`number % 10`)计算最后一位数字并利用 `digitNames` 字典获取所映射的字符串。这个闭包能够用于创建任意正整数的字符串表示。
> 注意
> 字典`digitNames`下标后跟着一个叹号(`!`因为字典下标返回一个可选值optional value表明该键不存在时会查找失败。在上例中由于可以确定`number % 10`总是`digitNames`字典的有效下标,因此叹号可以用于强制解包 (force-unwrap) 存储在下标的可选类型的返回值中的`String`类型的值。
> 注意
> 字典 `digitNames` 下标后跟着一个叹号(`!`因为字典下标返回一个可选值optional value表明该键不存在时会查找失败。在上例中由于可以确定 `number % 10` 总是 `digitNames` 字典的有效下标,因此叹号可以用于强制解包 (force-unwrap) 存储在下标的可选类型的返回值中的`String`类型的值。
`digitNames`字典中获取的字符串被添加到`output`前部,逆序建立了一个字符串版本的数字。(在表达式`number % 10`中,如果`number``16`,则返回`6``58`返回`8``510`返回`0`。)
`digitNames` 字典中获取的字符串被添加到 `output` 的*前部*,逆序建立了一个字符串版本的数字。(在表达式 `number % 10` 中,如果 `number``16`,则返回 `6``58` 返回 `8``510` 返回 `0`。)
`number`变量之后除以`10`。因为其是整数,在计算过程中未除尽部分被忽略。因此`16`变成了`1``58`变成了`5``510`变成了`51`
`number` 变量之后除以 `10`。因为其是整数,在计算过程中未除尽部分被忽略。因此 `16` 变成了 `1``58` 变成了 `5``510` 变成了 `51`
整个过程重复进行,直到`number /= 10``0`,这时闭包会将字符串`output`返回,而`map(_:)`方法则会将字符串添加到映射数组中。
整个过程重复进行,直到 `number /= 10``0`,这时闭包会将字符串 `output` 返回,而 `map(_:)` 方法则会将字符串添加到映射数组中。
在上面的例子中,通过尾随闭包语法,优雅地在函数后封装了闭包的具体功能,而不再需要将整个闭包包裹在`map(_:)`方法的括号内。
在上面的例子中,通过尾随闭包语法,优雅地在函数后封装了闭包的具体功能,而不再需要将整个闭包包裹在 `map(_:)` 方法的括号内。
<a name="capturing_values"></a>
## 捕获Capturing Values
## 捕获Capturing Values
闭包可以在其被定义的上下文中*捕获*常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
Swift 中,可以捕获值的闭包的最简单形式是嵌套函数,也就是定义在其他函数的函数体内的函数。嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。
举个例子,这有一个叫做`makeIncrementor`的函数,其包含了一个叫做`incrementor`的嵌套函数。嵌套函数`incrementor()`从上下文中捕获了两个值,`runningTotal``amount`。捕获这些值之后,`makeIncrementor``incrementor`作为闭包返回。每次调用`incrementor`时,其会以`amount`作为增量增加`runningTotal`的值。
举个例子,这有一个叫做 `makeIncrementor` 的函数,其包含了一个叫做 `incrementor` 的嵌套函数。嵌套函数 `incrementor()` 从上下文中捕获了两个值,`runningTotal``amount`。捕获这些值之后,`makeIncrementor``incrementor` 作为闭包返回。每次调用 `incrementor` 时,其会以 `amount` 作为增量增加 `runningTotal` 的值。
```swift
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
return incrementer
}
```
`makeIncrementor`返回类型为`() -> Int`。这意味着其返回的是一个函数,而不是一个简单类型的值。该函数在每次调用时不接受参数,只返回一个`Int`类型的值。关于函数返回其他函数的内容,请查看[函数类型作为返回类型](./06_Functions.html#function_types_as_return_types)。
`makeIncrementor` 返回类型为 `() -> Int`。这意味着其返回的是一个*函数*,而一个简单类型的值。该函数在每次调用时不接受参数,只返回一个 `Int` 类型的值。关于函数返回其他函数的内容,请查看[函数类型作为返回类型](./06_Functions.html#function_types_as_return_types)。
`makeIncrementer(forIncrement:)`函数定义了一个初始值为`0`的整型变量`runningTotal`,用来存储当前跑步总数。该值通过`incrementor`返回。
`makeIncrementer(forIncrement:)` 函数定义了一个初始值为 `0` 的整型变量 `runningTotal`,用来存储当前总计数值。该值`incrementor`返回
`makeIncrementer(forIncrement:)`有一个`Int`类型的参数,其外部参数名为`forIncrement`,内部参数名为`amount`,该参数表示每次`incrementor`被调用时`runningTotal`将要增加的量。
`makeIncrementer(forIncrement:)` 有一个 `Int` 类型的参数,其外部参数名为 `forIncrement`,内部参数名为 `amount`,该参数表示每次 `incrementor` 被调用时 `runningTotal` 将要增加的量。`makeIncrementer` 函数还定义了一个嵌套函数 `incrementor`,用来执行实际的增加操作。该函数简单地使 `runningTotal` 增加 `amount`,并将其返回。
嵌套函数`incrementor`用来执行实际的增加操作。该函数简单地使`runningTotal`增加`amount`,并将其返回。
如果我们单独看这个函数,会发现看上去不同寻常:
如果我们单独考虑嵌套函数 `incrementer()`,会发现它有些不同寻常:
```swift
func incrementor() -> Int {
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
```
`incrementer()`函数并没有任何参数,但是在函数体内访问了`runningTotal``amount`变量。这是因为它从外围函数捕获了`runningTotal``amount`变量的引用。捕获引用保证了`runningTotal``amount`变量在调用完`makeIncrementer`后不会消失,并且保证了在下一次执行`incrementer`函数时,`runningTotal`依旧存在。
`incrementer()` 函数并没有任何参数,但是在函数体内访问了 `runningTotal``amount` 变量。这是因为它从外围函数捕获了 `runningTotal``amount` 变量的*引用*。捕获引用保证了 `runningTotal``amount` 变量在调用完 `makeIncrementer` 后不会消失,并且保证了在下一次执行 `incrementer` 函数时,`runningTotal` 依旧存在。
> 注意
> 为了优化,如果一个值是不可变的Swift 可能会改为捕获并保存一份对值的拷贝。
> 注意
> 为了优化,如果一个值不会被闭包改变,或者在闭包创建后不会改变Swift 可能会改为捕获并保存一份对值的拷贝。
> Swift 也会负责被捕获变量的所有内存管理工作,包括释放不再需要的变量。
下面是一个使用`makeIncrementor`的例子:
下面是一个使用 `makeIncrementor` 的例子:
```swift
let incrementByTen = makeIncrementor(forIncrement: 10)
```
该例子定义了一个叫做`incrementByTen`的常量,该常量指向一个每次调用会将`runningTotal`变量增加`10``incrementor`函数。调用这个函数多次可以得到以下结果:
该例子定义了一个叫做 `incrementByTen` 的常量,该常量指向一个每次调用会将`runningTotal` 变量增加 `10``incrementor` 函数。调用这个函数多次可以得到以下结果:
```swift
incrementByTen()
@@ -306,7 +306,7 @@ incrementByTen()
// 30
```
如果创建了另一个`incrementor`,它会有属于自己的一个全新、独立的`runningTotal`变量的引用
如果创建了另一个 `incrementor`,它会有属于自己的引用,指向一个全新、独立的 `runningTotal` 变量:
```swift
let incrementBySeven = makeIncrementor(forIncrement: 7)
@@ -314,7 +314,7 @@ incrementBySeven()
// 7
```
再次调用原来的`incrementByTen`会在原来的变量`runningTotal`上继续增加值,该变量和`incrementBySeven`中捕获的变量没有任何联系:
再次调用原来的 `incrementByTen` 会继续增加它自己的 `runningTotal` 变量,该变量和 `incrementBySeven` 中捕获的变量没有任何联系:
```swift
incrementByTen()
@@ -322,16 +322,16 @@ incrementByTen()
```
> 注意
> 如果将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,您将创建一个在闭包和该实例间循环强引用。Swift 使用捕获列表来打破这种循环强引用。更多信息,请参考[闭包引起的循环强引用](./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
上面的例子中,`incrementBySeven``incrementByTen`是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量的值。这是因为函数和闭包都是*引用类型*。
上面的例子中,`incrementBySeven``incrementByTen`是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量的值。这是因为函数和闭包都是*引用类型*。
无论将函数或闭包赋值给一个常量还是变量,实际上都是将常量或变量的值设置为对应函数或闭包的引用。上面的例子中,指向闭包的引用`incrementByTen`是一个常量,而并非闭包内容本身。
无论将函数或闭包赋值给一个常量还是变量,实际上都是将常量或变量的值设置为对应函数或闭包的*引用*。上面的例子中,指向闭包的引用 `incrementByTen` 是一个常量,而并非闭包内容本身。
这也意味着如果将闭包赋值给了两个不同的常量或变量,两个值都会指向同一个闭包:
这也意味着如果将闭包赋值给了两个不同的常量或变量,两个值都会指向同一个闭包:
```swift
let alsoIncrementByTen = incrementByTen
@@ -339,121 +339,118 @@ alsoIncrementByTen()
// 50
```
<a name="nonescaping_closures"></a>
## 逃逸闭包(Nonescaping Closures)
<a name="escaping_closures"></a>
## 逃逸闭包(Escaping Closures)
当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中*逃逸*。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注`@noescape`,用来指明这个闭包是允许“逃逸”出这个函数的。将闭包标注`@noescape`能使编译器知道这个闭包的生命周期(译者注:闭包只能在函数体中被执行,不能脱离函数体执行,所以编译器明确知道运行时的上下文),从而可以进行一些比较激进的优化。
当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中*逃逸*。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注 `@escaping`,用来指明这个闭包是允许“逃逸”出这个函数的。
```swift
func someFunctionWithNoescapeClosure(@noescape closure: () -> Void) {
closure()
}
```
一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。举个例子,很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回但是闭包直到异步操作结束后才会被调用。在这种情况下闭包需要“逃逸”出函数因为闭包需要在函数返回之后被调用。例如
举个例子,`sort(_:)`方法接受一个用来进行元素比较的闭包作为参数。这个参数被标注了`@noescape`,因为它确保自己在排序结束之后就没用了。
一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。举个例子,很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回但是闭包直到异步操作结束后才会被调用。在这种情况下闭包需要“逃逸”出函数因为闭包需要在函数返回之后被调用。例如
```swift
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: () -> Void) {
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
```
`someFunctionWithEscapingClosure(_:)`函数接受一个闭包作为参数,该闭包被添加到一个函数外定义的数组中。如果你试图将这个参数标注为`@noescape`,你将会获得一个编译错误。
`someFunctionWithEscapingClosure(_:)` 函数接受一个闭包作为参数,该闭包被添加到一个函数外定义的数组中。如果你将这个参数标记为 `@escaping`,就会得到一个编译错误。
将一个闭包标记为 `@escaping` 意味着你必须在闭包中显式地引用 `self`。比如说,在下面的代码中,传递到 `someFunctionWithEscapingClosure(_:)` 中的闭包是一个逃逸闭包,这意味着它需要显式地引用 `self`。相对的,传递到 `someFunctionWithNonescapingClosure(_:)` 中的闭包是一个非逃逸闭包,这意味着它可以隐式引用 `self`
将闭包标注为`@noescape`使你能在闭包中隐式地引用`self`
```swift
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNoescapeClosure { x = 200 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
// prints "200"
// "200"
completionHandlers.first?()
print(instance.x)
// prints "100"
// "100"
```
<a name="autoclosures"></a>
## 自动闭包Autoclosures
*自动闭包*是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够用一个普通的表达式来代替显式的闭包,从而省略闭包的花括号
*自动闭包*是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包。
我们经常会调用一个接受闭包作为参数的函数,但是很少实现那样的函数。举个例子来说,`assert(condition:message:file:line:)`函数接受闭包作为它的`condition`参数和`message`参数;它的`condition`参数仅会在 debug 模式下被求值,它的`message`参数仅当`condition`参数为`false`时被计算求值。
我们经常会*调用*采用自动闭包的函数,但是很少去*实现*这样的函数。举个例子来说,`assert(condition:message:file:line:)` 函数接受自动闭包作为它的 `condition` 参数和 `message` 参数;它的 `condition` 参数仅会在 debug 模式下被求值,它的 `message` 参数仅当 `condition` 参数为 `false` 时被计算求值。
自动闭包让你能够延迟求值,因为代码段不会被执行直到你调用这个闭包。延迟求值对于那些有副作用Side Effect代价昂贵的代码来说是很有益处的,因为你能控制代码什么时候执行。下面的代码展示了闭包如何延时求值。
自动闭包让你能够延迟求值,因为直到你调用这个闭包,代码段才会被执行。延迟求值对于那些有副作用Side Effect高计算成本的代码来说是很有益处的,因为它使得你能控制代码的执行时机。下面的代码展示了闭包如何延时求值。
```swift
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// prints "5"
let customerProvider = { customersInLine.removeAtIndex(0) }
// "5"
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// prints "5"
// "5"
print("Now serving \(customerProvider())!")
// prints "Now serving Chris!"
// Prints "Now serving Chris!"
print(customersInLine.count)
// prints "4"
// "4"
```
尽管在闭包的代码中,`customersInLine`的第一个元素被移除了,不过在闭包被调用之前,这个元素是不会被移除的。如果这个闭包永远不被调用,那么在闭包里面的表达式将永远不会执行,那意味着列表中的元素永远不会被移除。请注意,`customerProvider`的类型不是`String`,而是`() -> String`,一个没有参数且返回值为`String`的函数。
尽管在闭包的代码中,`customersInLine` 的第一个元素被移除了,不过在闭包被调用之前,这个元素是不会被移除的。如果这个闭包永远不被调用,那么在闭包里面的表达式将永远不会执行,那意味着列表中的元素永远不会被移除。请注意,`customerProvider` 的类型不是 `String`,而是 `() -> String`,一个没有参数且返回值为 `String` 的函数。
将闭包作为参数传递给函数时,你能获得同样的延时求值行为。
将闭包作为参数传递给函数时,你能获得同样的延时求值行为。
```swift
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serveCustomer(customerProvider: () -> String) {
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serveCustomer( { customersInLine.removeAtIndex(0) } )
// prints "Now serving Alex!"
serve(customer: { customersInLine.remove(at: 0) } )
// "Now serving Alex!"
```
`serveCustomer(_:)`接受一个返回顾客名字的显式的闭包。下面这个版本的`serveCustomer(_:)`完成了相同的操作,不过它并没有接受一个显式的闭包,而是通过将参数标记为`@autoclosure`来接收一个自动闭包。现在你可以将该函数当接受`String`类型参数的函数来调用。`customerProvider`参数将自动转化为一个闭包,因为该参数被标记了`@autoclosure`特性。
上面的 `serve(customer:)` 函数接受一个返回顾客名字的显式的闭包。下面这个版本的 `serve(customer:)` 完成了相同的操作,不过它并没有接受一个显式的闭包,而是通过将参数标记为 `@autoclosure` 来接收一个自动闭包。现在你可以将该函数当接受 `String` 类型参数(而非闭包)的函数来调用。`customerProvider` 参数将自动转化为一个闭包,因为该参数被标记了 `@autoclosure` 特性。
```swift
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serveCustomer(@autoclosure customerProvider: () -> String) {
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serveCustomer(customersInLine.removeAtIndex(0))
// prints "Now serving Ewa!"
serve(customer: customersInLine.remove(at: 0))
// "Now serving Ewa!"
```
> 注意
> 过度使用`autoclosures`会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。
> 注意
> 过度使用 `autoclosures` 会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。
`@autoclosure`特性暗含了`@noescape`特性,这个特性在[逃逸闭包](#nonescaping_closures)一节中有描述。如果你想让这个闭包可以“逃逸”,则应该使用`@autoclosure(escaping)`特性.
如果你想让一个自动闭包可以“逃逸”,则应该同时使用 `@autoclosure``@escaping` 属性。`@escaping` 属性的讲解见上面的[逃逸闭包](#escaping_closures)
```swift
// customersInLine is ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(@autoclosure(escaping) customerProvider: () -> String) {
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.removeAtIndex(0))
collectCustomerProviders(customersInLine.removeAtIndex(0))
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))
print("Collected \(customerProviders.count) closures.")
// prints "Collected 2 closures."
// "Collected 2 closures."
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
// prints "Now serving Barry!"
// prints "Now serving Daniella!"
// "Now serving Barry!"
// "Now serving Daniella!"
```
在上面的代码中,`collectCustomerProviders(_:)`函数并没有调用传入的`customerProvider`闭包,而是将闭包追加到了`customerProviders`数组中。这个数组定义在函数作用域范围外,这意味着数组内的闭包将会在函数返回之后被调用。因此,`customerProvider`参数必须允许“逃逸”出函数作用域。
在上面的代码中,`collectCustomerProviders(_:)` 函数并没有调用传入的 `customerProvider` 闭包,而是将闭包追加到了 `customerProviders` 数组中。这个数组定义在函数作用域范围外,这意味着数组内的闭包能够在函数返回之后被调用。因此,`customerProvider` 参数必须允许“逃逸”出函数作用域。

View File

@@ -10,11 +10,15 @@
> 2.1
> 翻译:[Channe](https://github.com/Channe)
> 校对:[shanks](http://codebuild.me)
> 校对:[shanks](http://codebuild.me)
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-13
> 3.0
> 翻译+校对:[shanks](https://codebuild.me) 2016-09-24
本页内容包含:
- [枚举语法Enumeration Syntax](#enumeration_syntax)
@@ -27,11 +31,11 @@
如果你熟悉 C 语言,你会知道在 C 语言中枚举会为一组整型值分配相关联的名称。Swift 中的枚举更加灵活,不必给每一个枚举成员提供一个值。如果给枚举成员提供一个值(称为“原始”值),则该值的类型可以是字符串,字符,或是一个整型值或浮点数。
此外枚举成员可以指定任意类型的关联值存储到枚举成员中就像其他语言中的联合体unions和变体variants。每一个枚举成员都可以有适当类型的关联值。
此外,枚举成员可以指定*任意*类型的关联值存储到枚举成员中就像其他语言中的联合体unions和变体variants你可以在一个枚举中定义一组相关的枚举成员,每一个枚举成员都可以有适当类型的关联值。
在 Swift 中枚举类型是一等first-class类型。它们采用了很多在传统上只被类class所支持的特性例如计算属性computed properties用于提供枚举值的附加信息实例方法instance methods用于提供和枚举值相关联的功能。枚举也可以定义构造函数initializers来提供一个初始值可以在原始实现的基础上扩展它们的功能还可以遵协议protocols来提供标准的功能。
在 Swift 中枚举类型是一等first-class类型。它们采用了很多在传统上只被类class所支持的特性例如计算属性computed properties用于提供枚举值的附加信息实例方法instance methods用于提供和枚举值相关联的功能。枚举也可以定义构造函数initializers来提供一个初始值可以在原始实现的基础上扩展它们的功能还可以遵协议protocols来提供标准的功能。
了解更多相关信息,请参见[属性Properties](./10_Properties.html)[方法Methods](./11_Methods.html)[构造过程Initialization](./14_Initialization.html)[扩展Extensions](./21_Extensions.html)和[协议Protocols](./22_Protocols.html)。
了解更多相关信息,请参见[属性Properties](./10_Properties.html)[方法Methods](./11_Methods.html)[构造过程Initialization](./14_Initialization.html)[扩展Extensions](./21_Extensions.html)和[协议Protocols](./22_Protocols.html)。
<a name="enumeration_syntax"></a>
## 枚举语法
@@ -48,36 +52,36 @@ enum SomeEnumeration {
```swift
enum CompassPoint {
case North
case South
case East
case West
case north
case south
case east
case west
}
```
枚举中定义的值(如 `North``South``East``West`)是这个枚举的*成员值*(或*成员*)。你使用`case`关键字来定义一个新的枚举成员值。
枚举中定义的值(如 `north ``south``east``west`)是这个枚举的*成员值*(或*成员*)。你可以使用`case`关键字来定义一个新的枚举成员值。
> 注意
> 与 C 和 Objective-C 不同Swift 的枚举成员在被创建时不会被赋予一个默认的整型值。在上面的`CompassPoint`例子中,`North``South``East`和`West`不会被隐式地赋值为`0``1``2`和`3`。相反,这些枚举成员本身就是完备的值,这些值的类型是已经明确定义好的`CompassPoint`类型。
> 与 C 和 Objective-C 不同Swift 的枚举成员在被创建时不会被赋予一个默认的整型值。在上面的`CompassPoint`例子中,`north``south``east`和`west`不会被隐式地赋值为`0``1``2`和`3`。相反,这些枚举成员本身就是完备的值,这些值的类型是已经明确定义好的`CompassPoint`类型。
多个成员值可以出现在同一行上,用逗号隔开:
```swift
enum Planet {
case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
```
每个枚举定义了一个全新的类型。像 Swift 中其他类型一样,它们的名字(例如`CompassPoint``Planet`)应该以一个大写字母开头。给枚举类型起一个单数名字而不是复数名字,以便于读起来更加容易理解:
```swift
var directionToHead = CompassPoint.West
var directionToHead = CompassPoint.west
```
`directionToHead`的类型可以在它被`CompassPoint`的某个值初始化时推断出来。一旦`directionToHead`被声明为`CompassPoint`类型,你可以使用更简短的点语法将其设置为另一个`CompassPoint`的值:
```swift
directionToHead = .East
directionToHead = .east
```
`directionToHead`的类型已知时,再次为其赋值可以省略枚举类型名。在使用具有显式类型的枚举值时,这种写法让代码具有更好的可读性。
@@ -88,15 +92,15 @@ directionToHead = .East
你可以使用`switch`语句匹配单个枚举值:
```swift
directionToHead = .South
directionToHead = .south
switch directionToHead {
case .North:
case .north:
print("Lots of planets have a north")
case .South:
case .south:
print("Watch out for penguins")
case .East:
case .east:
print("Where the sun rises")
case .West:
case .west:
print("Where the skies are blue")
}
// "Watch out for penguins
@@ -104,18 +108,18 @@ switch directionToHead {
你可以这样理解这段代码:
“判断`directionToHead`的值。当它等于`.North`,打印`“Lots of planets have a north”`。当它等于`.South`,打印`“Watch out for penguins”`。”
“判断`directionToHead`的值。当它等于`.north`,打印`“Lots of planets have a north”`。当它等于`.south`,打印`“Watch out for penguins”`。”
……以此类推。
正如在[控制流Control Flow](./05_Control_Flow.html)中介绍的那样,在判断一个枚举类型的值时,`switch`语句必须穷举所有情况。如果忽略了`.West`这种情况,上面那段代码将无法通过编译,因为它没有考虑到`CompassPoint`的全部成员。强制穷举确保了枚举成员不会被意外遗漏。
正如在[控制流Control Flow](./05_Control_Flow.html)中介绍的那样,在判断一个枚举类型的值时,`switch`语句必须穷举所有情况。如果忽略了`.west`这种情况,上面那段代码将无法通过编译,因为它没有考虑到`CompassPoint`的全部成员。强制穷举确保了枚举成员不会被意外遗漏。
当不需要匹配每个枚举成员的时候,你可以提供一个`default`分支来涵盖所有未明确处理的枚举成员:
```swift
let somePlanet = Planet.Earth
let somePlanet = Planet.earth
switch somePlanet {
case .Earth:
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
@@ -126,7 +130,7 @@ default:
<a name="associated_values"></a>
## 关联值Associated Values
上一小节的例子演示了如何定义和分类枚举的成员。你可以为`Planet.Earth`设置一个常量或者变量,并在赋值之后查看这个值。然而,有时候能够把其他类型的*关联值*和成员值一起存储起来会很有用。这能让你连同成员值一起存储额外的自定义信息,并且你每次在代码中使用该枚举成员时,还可以修改这个关联值。
上一小节的例子演示了如何定义和分类枚举的成员。你可以为`Planet.earth`设置一个常量或者变量,并在赋值之后查看这个值。然而,有时候能够把其他类型的*关联值*和成员值一起存储起来会很有用。这能让你连同成员值一起存储额外的自定义信息,并且你每次在代码中使用该枚举成员时,还可以修改这个关联值。
你可以定义 Swift 枚举来存储任意类型的关联值如果需要的话每个枚举成员的关联值类型可以各不相同。枚举的这种特性跟其他语言中的可识别联合discriminated unions标签联合tagged unions或者变体variants相似。
@@ -138,46 +142,46 @@ default:
<img width="169" height="169" alt="" src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/barcode_QR_2x.png">
这便于库存跟踪系统用包含四个整型值的元组存储 UPC-A 码,以及用任意长度的字符串储存 QR 码。
这便于库存跟踪系统用包含四个整型值的元组存储 UPC 码,以及用任意长度的字符串储存 QR 码。
在 Swift 中,使用如下方式定义表示两种商品条形码的枚举:
```swift
enum Barcode {
case UPCA(Int, Int, Int, Int)
case QRCode(String)
case upc(Int, Int, Int, Int)
case qrCode(String)
}
```
以上代码可以这么理解:
“定义一个名为`Barcode`的枚举类型,它的一个成员值是具有`(IntIntIntInt)`类型关联值的`UPCA`,另一个成员值是具有`String`类型关联值的`QRCode`。”
“定义一个名为`Barcode`的枚举类型,它的一个成员值是具有`(IntIntIntInt)`类型关联值的`upc`,另一个成员值是具有`String`类型关联值的`qrCode`。”
这个定义不提供任何`Int``String`类型的关联值,它只是定义了,当`Barcode`常量和变量等于`Barcode.UPCA``Barcode.QRCode`时,可以存储的关联值的类型。
这个定义不提供任何`Int``String`类型的关联值,它只是定义了,当`Barcode`常量和变量等于`Barcode.upc``Barcode.qrCode`时,可以存储的关联值的类型。
然后可以使用任意一种条形码类型创建新的条形码,例如:
```swift
var productBarcode = Barcode.UPCA(8, 85909, 51226, 3)
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
```
上面的例子创建了一个名为`productBarcode`的变量,并将`Barcode.UPCA`赋值给它,关联的元组值为`(8, 85909, 51226, 3)`
上面的例子创建了一个名为`productBarcode`的变量,并将`Barcode.upc`赋值给它,关联的元组值为`(8, 85909, 51226, 3)`
同一个商品可以被分配一个不同类型的条形码,例如:
```swift
productBarcode = .QRCode("ABCDEFGHIJKLMNOP")
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
```
这时,原始的`Barcode.UPCA`和其整数关联值被新的`Barcode.QRCode`和其字符串关联值所替代。`Barcode`类型的常量和变量可以存储一个`.UPCA`或者一个`.QRCode`(连同它们的关联值),但是在同一时间只能存储这两个值中的一个。
这时,原始的`Barcode.upc`和其整数关联值被新的`Barcode.qrCode`和其字符串关联值所替代。`Barcode`类型的常量和变量可以存储一个`.upc`或者一个`.qrCode`(连同它们的关联值),但是在同一时间只能存储这两个值中的一个。
像先前那样,可以使用一个 switch 语句来检查不同的条形码类型。然而,这一次,关联值可以被提取出来作为 switch 语句的一部分。你可以在`switch`的 case 分支代码中提取每个关联值作为一个常量(用`let`前缀)或者作为一个变量(用`var`前缀)来使用:
```swift
switch productBarcode {
case .UPCA(let numberSystem, let manufacturer, let product, let check):
print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
case .QRCode(let productCode):
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
// "QR code: ABCDEFGHIJKLMNOP."
@@ -187,9 +191,9 @@ case .QRCode(let productCode):
```swift
switch productBarcode {
case let .UPCA(numberSystem, manufacturer, product, check):
print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
case let .QRCode(productCode):
case let .upc(numberSystem, manufacturer, product, check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
// "QR code: ABCDEFGHIJKLMNOP."
@@ -204,9 +208,9 @@ case let .QRCode(productCode):
```swift
enum ASCIIControlCharacter: Character {
case Tab = "\t"
case LineFeed = "\n"
case CarriageReturn = "\r"
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
```
@@ -229,11 +233,11 @@ enum ASCIIControlCharacter: Character {
```swift
enum Planet: Int {
case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
```
在上面的例子中,`Plant.Mercury`的显式原始值为`1``Planet.Venus`的隐式原始值为`2`,依次类推。
在上面的例子中,`Plant.mercury`的显式原始值为`1``Planet.venus`的隐式原始值为`2`,依次类推。
当使用字符串作为枚举类型的原始值时,每个枚举成员的隐式原始值为该枚举成员的名称。
@@ -241,20 +245,20 @@ enum Planet: Int {
```swift
enum CompassPoint: String {
case North, South, East, West
case north, south, east, west
}
```
上面例子中,`CompassPoint.South`拥有隐式原始值`South`,依次类推。
上面例子中,`CompassPoint.south`拥有隐式原始值`south`,依次类推。
使用枚举成员的`rawValue`属性可以访问该枚举成员的原始值:
```swift
let earthsOrder = Planet.Earth.rawValue
let earthsOrder = Planet.earth.rawValue
// earthsOrder 3
let sunsetDirection = CompassPoint.West.rawValue
// sunsetDirection "West"
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection "west"
```
<a name="initializing_from_a_raw_value"></a>
@@ -262,11 +266,11 @@ let sunsetDirection = CompassPoint.West.rawValue
如果在定义枚举类型的时候使用了原始值,那么将会自动获得一个初始化方法,这个方法接收一个叫做`rawValue`的参数,参数类型即为原始值类型,返回值则是枚举成员或`nil`。你可以使用这个初始化方法来创建一个新的枚举实例。
这个例子利用原始值`7`创建了枚举成员`Uranus`
这个例子利用原始值`7`创建了枚举成员`uranus`
```swift
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet Planet? Planet.Uranus
// possiblePlanet Planet? Planet.uranus
```
然而,并非所有`Int`值都可以找到一个匹配的行星。因此,原始值构造器总是返回一个*可选*的枚举成员。在上面的例子中,`possiblePlanet``Planet?`类型,或者说“可选的`Planet`”。
@@ -274,13 +278,13 @@ let possiblePlanet = Planet(rawValue: 7)
> 注意
> 原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见[可失败构造器](../chapter3/05_Declarations.html#failable_initializers)
如果你试图寻找一个位置为`9`的行星,通过原始值构造器返回的可选`Planet`值将是`nil`
如果你试图寻找一个位置为`11`的行星,通过原始值构造器返回的可选`Planet`值将是`nil`
```swift
let positionToFind = 9
let positionToFind = 11
if let somePlanet = Planet(rawValue: positionToFind) {
switch somePlanet {
case .Earth:
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
@@ -288,10 +292,10 @@ if let somePlanet = Planet(rawValue: positionToFind) {
} else {
print("There isn't a planet at position \(positionToFind)")
}
// "There isn't a planet at position 9
// "There isn't a planet at position 11
```
这个例子使用了可选绑定optional binding试图通过原始值`9`来访问一个行星。`if let somePlanet = Planet(rawValue: 9)`语句创建了一个可选`Planet`,如果可选`Planet`的值存在,就会赋值给`somePlanet`。在这个例子中,无法检索到位置为`9`的行星,所以`else`分支被执行。
这个例子使用了可选绑定optional binding试图通过原始值`11`来访问一个行星。`if let somePlanet = Planet(rawValue: 11)`语句创建了一个可选`Planet`,如果可选`Planet`的值存在,就会赋值给`somePlanet`。在这个例子中,无法检索到位置为`11`的行星,所以`else`分支被执行。
<a name="recursive_enumerations"></a>
## 递归枚举Recursive Enumerations
@@ -303,9 +307,9 @@ if let somePlanet = Planet(rawValue: positionToFind) {
```swift
enum ArithmeticExpression {
case Number(Int)
indirect case Addition(ArithmeticExpression, ArithmeticExpression)
indirect case Multiplication(ArithmeticExpression, ArithmeticExpression)
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
```
@@ -313,31 +317,31 @@ enum ArithmeticExpression {
```swift
indirect enum ArithmeticExpression {
case Number(Int)
case Addition(ArithmeticExpression, ArithmeticExpression)
case Multiplication(ArithmeticExpression, ArithmeticExpression)
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
```
上面定义的枚举类型可以存储三种算术表达式:纯数字、两个表达式相加、两个表达式相乘。枚举成员`Addition``Multiplication`的关联值也是算术表达式——这些关联值使得嵌套表达式成为可能。例如,表达式`(5 + 4) * 2`,乘号右边是一个数字,左边则是另一个表达式。因为数据是嵌套的,因而用来存储数据的枚举类型也需要支持这种嵌套——这意味着枚举类型需要支持递归。下面的代码展示了使用`ArithmeticExpression `这个递归枚举创建表达式`(5 + 4) * 2`
上面定义的枚举类型可以存储三种算术表达式:纯数字、两个表达式相加、两个表达式相乘。枚举成员`addition``multiplication`的关联值也是算术表达式——这些关联值使得嵌套表达式成为可能。例如,表达式`(5 + 4) * 2`,乘号右边是一个数字,左边则是另一个表达式。因为数据是嵌套的,因而用来存储数据的枚举类型也需要支持这种嵌套——这意味着枚举类型需要支持递归。下面的代码展示了使用`ArithmeticExpression `这个递归枚举创建表达式`(5 + 4) * 2`
```swift
let five = ArithmeticExpression.Number(5)
let four = ArithmeticExpression.Number(4)
let sum = ArithmeticExpression.Addition(five, four)
let product = ArithmeticExpression.Multiplication(sum, ArithmeticExpression.Number(2))
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
```
要操作具有递归性质的数据结构,使用递归函数是一种直截了当的方式。例如,下面是一个对算术表达式求值的函数:
```swift
func evaluate(expression: ArithmeticExpression) -> Int {
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case .Number(let value):
case let .number(value):
return value
case .Addition(let left, let right):
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case .Multiplication(let left, let right):
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}

View File

@@ -7,6 +7,9 @@
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-15
>
> 3.0
> 翻译+校对:[shanks](http://codebuild.me) 2016-09-24
本页包含内容:
@@ -14,38 +17,38 @@
- [处理错误](#handling_errors)
- [指定清理操作](#specifying_cleanup_actions)
*错误处理Error handling*是响应错误以及从错误中恢复的过程。Swift 提供了在运行时对可恢复错误的抛出、捕获、传递和操作的一支持。
*错误处理Error handling*是响应错误以及从错误中恢复的过程。Swift 提供了在运行时对可恢复错误的抛出、捕获、传递和操作的一等公民支持。
某些操作无法保证总是执行完所有代码或总是生成有用的结果。可选类型可用来表示值缺失,但是当某个操作失败时,最好能得知失败的原因,从而可以作出相应的应对。
举个例子,假如有个从磁盘上的某个文件读取数据并进行处理的任务,该任务会有多种可能失败的情况,包括指定路径下文件并不存在,文件不具有可读权限,或者文件编码格式不兼容。区分这些不同的失败情况可以让程序解决并处理某些错误,然后把它解决不了的错误报告给用户。
> 注意
Swift 中的错误处理涉及到错误处理模式,这会用到 Cocoa 和 Objective-C 中的`NSError`。关于这个类的更多信息请参见 [Using Swift with Cocoa and Objective-C (Swift 2.2)](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 中的错误处理涉及到错误处理模式,这会用到 Cocoa 和 Objective-C 中的`NSError`。关于这个类的更多信息请参见 [Using Swift with Cocoa and Objective-C (Swift 3)](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)。
<a name="representing_and_throwing_errors"></a>
##表示并抛出错误
## 表示并抛出错误
在 Swift 中,错误用符合`ErrorType`协议的类型的值来表示。这个空协议表明该类型可以用于错误处理。
在 Swift 中,错误用符合`Error`协议的类型的值来表示。这个空协议表明该类型可以用于错误处理。
Swift 的枚举类型尤为适合构建一组相关的错误状态,枚举的关联值还可以提供错误状态的额外信息。例如,你可以这样表示在一个游戏中操作自动贩卖机时可能会出现的错误状态:
```swift
enum VendingMachineError: ErrorType {
case InvalidSelection //
case InsufficientFunds(coinsNeeded: Int) //
case OutOfStock //
enum VendingMachineError: Error {
case invalidSelection //
case insufficientFunds(coinsNeeded: Int) //
case outOfStock //
}
```
抛出一个错误可以让你表明有意外情况发生,导致正常的执行流程无法继续执行。抛出错误使用`throw`关键字。例如,下面的代码抛出一个错误,提示贩卖机还需要`5`个硬币:
```swift
throw VendingMachineError.InsufficientFunds(coinsNeeded: 5)
throw VendingMachineError. insufficientFunds(coinsNeeded: 5)
```
<a name="handling_errors"></a>
##处理错误
## 处理错误
某个错误被抛出时,附近的某部分代码必须负责处理这个错误,例如纠正这个问题、尝试另外一种方式、或是向用户报告错误。
@@ -109,7 +112,7 @@ class VendingMachine {
newItem.count -= 1
inventory[name] = newItem
dispenseSnack(name)
print("Dispensing \(name)")
}
}
```
@@ -145,7 +148,7 @@ struct PurchasedSnack {
```
###用 Do-Catch 处理错误
### 用 Do-Catch 处理错误
可以使用一个`do-catch`语句运行一段闭包代码来处理错误。如果在`do`子句中的代码抛出了一个错误,这个错误会与`catch`子句做匹配,从而决定哪条子句能处理它。
@@ -183,7 +186,7 @@ do {
上面的例子中,`buyFavoriteSnack(_:vendingMachine:)`函数在一个`try`表达式中调用,因为它能抛出错误。如果错误被抛出,相应的执行会马上转移到`catch`子句中,并判断这个错误是否要被继续传递下去。如果没有错误抛出,`do`子句中余下的语句就会被执行。
###将错误转换成可选值
### 将错误转换成可选值
可以使用`try?`通过将错误转换成一个可选值来处理错误。如果在评估`try?`表达式时一个错误被抛出,那么表达式的值就是`nil`。例如,在下面的代码中,`x``y`有着相同的数值和等价的含义:
@@ -225,7 +228,7 @@ let photo = try! loadImage("./Resources/John Appleseed.jpg")
```
<a name="specifying_cleanup_actions"></a>
##指定清理操作
## 指定清理操作
可以使用`defer`语句在即将离开当前代码块时执行一系列语句。该语句让你能执行一些必要的清理工作,不管是以何种方式离开当前代码块的——无论是由于抛出错误而离开,还是由于诸如`return`或者`break`的语句。例如,你可以用`defer`语句来确保文件描述符得以关闭,以及手动分配的内存得以释放。

View File

@@ -10,10 +10,15 @@
> 2.1
> 翻译:[小铁匠Linus](https://github.com/kevin833752)
> 校对:[shanks](http://codebuild.me)2015-11-01
> 校对:[shanks](http://codebuild.me)
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-16
> 翻译+校对:[SketchK](https://github.com/SketchK)
>
> 3.0
> 校对:[CMB](https://github.com/chenmingbiao)
>
> 版本日期2016-09-13
本页包含内容:
@@ -614,7 +619,7 @@ protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
<a name="protocol_composition"></a>
## 协议合成
有时候需要同时采纳多个协议,你可以将多个协议采用 `protocol<SomeProtocol, AnotherProtocol>` 这样的格式进行组合,称为 *协议合成protocol composition*。你可以`<>`罗列任意多个你想要采纳的协议,以逗号分隔。
有时候需要同时采纳多个协议,你可以将多个协议采用 `SomeProtocol & AnotherProtocol` 这样的格式进行组合,称为 *协议合成protocol composition*。你可以罗列任意多个你想要采纳的协议,以与符号(`&`)分隔。
下面的例子中,将 `Named``Aged` 两个协议按照上述语法组合成一个协议,作为函数参数的类型:
@@ -629,17 +634,17 @@ struct Person: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(celebrator: protocol<Named, Aged>) {
print("Happy birthday \(celebrator.name) - you're \(celebrator.age)!")
func wishHappyBirthday(to celebrator: Named & Aged) {
print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(birthdayPerson)
wishHappyBirthday(to: birthdayPerson)
// Happy birthday Malcolm - you're 21!
```
`Named` 协议包含 `String` 类型的 `name` 属性。`Aged` 协议包含 `Int` 类型的 `age` 属性。`Person` 结构体采纳了这两个协议。
`wishHappyBirthday(_:)` 函数的参数 `celebrator` 的类型为 `protocol<NamedAged>`。这意味着它不关心参数的具体类型,只要参数符合这两个协议即可。
`wishHappyBirthday(_:)` 函数的参数 `celebrator` 的类型为 `Named & Aged`。这意味着它不关心参数的具体类型,只要参数符合这两个协议即可。
上面的例子创建了一个名为 `birthdayPerson``Person` 的实例,作为参数传递给了 `wishHappyBirthday(_:)` 函数。因为 `Person` 同时符合这两个协议,所以这个参数合法,函数将打印生日问候语。

View File

@@ -13,6 +13,9 @@
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-17
>
> 3.0
> 翻译+校对:[mmoaay](https://github.com/mmoaay) 2016-09-20
本页内容包括:
@@ -320,12 +323,15 @@ signedOverflow = signedOverflow &- 1
struct Vector2D {
var x = 0.0, y = 0.0
}
func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
extension Vector2D {
static func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
}
```
该运算符函数被定义为一个全局函数,并且函数的名字与它要进行重载的 `+` 名字一致。因为算术加法运算符是双目运算符,所以这个运算符函数接收两个类型为 `Vector2D` 的参数,同时有一个 `Vector2D` 类型的返回值。
该运算符函数被定义为 `Vector2D` 上的一个类方法,并且函数的名字与它要进行重载的 `+` 名字一致。因为加法运算并不是一个向量必需的功能,所以这个类方法被定义在 `Vector2D` 的一个扩展中,而不是 `Vector2D` 结构体声明内。而算术加法运算符是双目运算符,所以这个运算符函数接收两个类型为 `Vector2D` 的参数,同时有一个 `Vector2D` 类型的返回值。
在这个实现中,输入参数分别被命名为 `left``right`,代表在 `+` 运算符左边和右边的两个 `Vector2D` 实例。函数返回了一个新的 `Vector2D` 实例,这个实例的 `x``y` 分别等于作为参数的两个实例的 `x``y` 的值之和。
@@ -350,8 +356,10 @@ let combinedVector = vector + anotherVector
要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 `func` 关键字之前指定 `prefix` 或者 `postfix` 修饰符:
```swift
prefix func - (vector: Vector2D) -> Vector2D {
return Vector2D(x: -vector.x, y: -vector.y)
extension Vector2D {
static prefix func - (vector: Vector2D) -> Vector2D {
return Vector2D(x: -vector.x, y: -vector.y)
}
}
```
@@ -372,8 +380,10 @@ let alsoPositive = -negative
复合赋值运算符将赋值运算符(`=`)与其它运算符进行结合。例如,将加法与赋值结合成加法赋值运算符(`+=`)。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接被修改。
```swift
func += (inout left: Vector2D, right: Vector2D) {
left = left + right
extension Vector2D {
static func += (left: inout Vector2D, right: Vector2D) {
left = left + right
}
}
```
@@ -398,11 +408,13 @@ original += vectorToAdd
为了使用等价运算符能对自定义的类型进行判等运算,需要为其提供自定义实现,实现的方法与其它中缀运算符一样:
```swift
func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
func != (left: Vector2D, right: Vector2D) -> Bool {
return !(left == right)
extension Vector2D {
static func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
static func != (left: Vector2D, right: Vector2D) -> Bool {
return !(left == right)
}
}
```
@@ -430,18 +442,17 @@ if twoThree == anotherTwoThree {
prefix operator +++ {}
```
上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有意义,因此我们针对 `Vector2D` 的实例来定义它的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法运算符来让矩阵对自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y` 属性的值翻倍:
上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有意义,因此我们针对 `Vector2D` 的实例来定义它的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法运算符来让矩阵对自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y` 属性的值翻倍。实现 `+++` 运算符的方式如下
```swift
prefix func +++ (inout vector: Vector2D) -> Vector2D {
vector += vector
return vector
extension Vector2D {
static prefix func +++ (vector: inout Vector2D) -> Vector2D {
vector += vector
return vector
}
}
```
`Vector2D``+++` 的实现和 `++` 的实现很相似,唯一不同的是前者对自身进行相加,而后者是与另一个值为 `(1.0, 1.0)` 的向量相加。
```swift
var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
let afterDoubling = +++toBeDoubled
// toBeDoubled (2.0, 8.0)
@@ -449,20 +460,20 @@ let afterDoubling = +++toBeDoubled
```
<a name="precedence_and_associativity_for_custom_infix_operators"></a>
### 自定义中缀运算符的优先级和结合性
### 自定义中缀运算符的优先级
自定义中缀运算符也可以指定优先级和结合性。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。
每个自定义中缀运算符都属于某个优先级组。这个优先级组指定了这个运算符和其他中缀运算符的优先级和结合性。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。
结合性可取的值有` left``right``none`。当左结合运算符跟其他相同优先级的左结合运算符写在一起时,会跟左边的值进行结合。同理,当右结合运算符跟其他相同优先级的右结合运算符写在一起时,会跟右边的值进行结合。而非结合运算符不能跟其他相同优先级的运算符写在一起
而没有明确放入优先级组的自定义中缀运算符会放到一个默认的优先级组内,其优先级高于三元运算符
结合性的默认值是 `none`,优先级的默认值 `100`
以下例子定义了一个新的中缀运算符 `+-`,此运算符的结合性为 `left`,并且它的优先级为 `140`
以下例子定义了一个新的自定义中缀运算符 `+-`,此运算符属于 `AdditionPrecedence` 优先组:
```swift
infix operator +- { associativity left precedence 140 }
func +- (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y - right.y)
infix operator +-: AdditionPrecedence
extension Vector2D {
static func +- (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y - right.y)
}
}
let firstVector = Vector2D(x: 1.0, y: 2.0)
let secondVector = Vector2D(x: 3.0, y: 4.0)
@@ -470,7 +481,7 @@ let plusMinusVector = firstVector +- secondVector
// plusMinusVector Vector2D (4.0, -2.0)
```
这个运算符把两个向量的 `x` 值相加,同时用第一个向量的 `y` 值减去第二个向量的 `y` 值。因为它本质上是属于“相加型”运算符,所以将它的结合性和优先级被分别设置为 `left``140`,这与 `+``-` 等默认的中缀“相加型”运算符相同的。关于 Swift 标准库提供的运算符的结合性与优先级,请参考 [Swift Standard Library Operators Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。
这个运算符把两个向量的 `x` 值相加,同时用第一个向量的 `y` 值减去第二个向量的 `y` 值。因为它本质上是属于“相加型”运算符,所以将它放置 `+``-` 等默认的中缀“相加型”运算符相同的优先级组中。关于 Swift 标准库提供的运算符,以及完整的运算符优先级组和结合性设置,请参考 [Swift Standard Library Operators Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。而更多关于优先级组以及自定义操作符和优先级组的语法,请参考[运算符声明](#operator_declaration)
> 注意
> 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。