Merge branch 'numbbbbb/gh-pages' into develop

This commit is contained in:
Channe
2015-07-02 22:27:11 +08:00
14 changed files with 953 additions and 745 deletions

View File

@ -41,35 +41,35 @@ Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进
闭包表达式是一种利用简洁语法构建内联闭包的方式。 闭包表达式是一种利用简洁语法构建内联闭包的方式。
闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。 闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。
下面闭包表达式的例子通过使用几次迭代展示了`sorted`函数定义和语法优化的方式。 下面闭包表达式的例子通过使用几次迭代展示了`sort(_:)`方法定义和语法优化的方式。
每一次迭代都用更简洁的方式描述了相同的功能。 每一次迭代都用更简洁的方式描述了相同的功能。
<a name="the_sorted_function"></a> <a name="the_sorted_function"></a>
### sorted 函数The Sorted Function ### sort 函数The Sort Function
Swift 标准库提供了`sorted`函数,会根据您提供的基于输出类型排序的闭包函数将已知类型数组中的值进行排序。 Swift 标准库提供了名为`sort`函数,会根据您提供的用于排序的闭包函数将已知类型数组中的值进行排序。
一旦排序完成,函数会返回一个与原数组大小相同的新数组,该数组中包含已经正确排序的同类型元素 一旦排序完成,`sort(_:)`方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被`sort(_:)`方法修改
下面的闭包表达式示例使用`sorted`函数对一个`String`类型的数组进行字母逆序排序,以下是初始数组值: 下面的闭包表达式示例使用`sort(_:)`方法对一个String类型的数组进行字母逆序排序以下是初始数组值
```swift ```swift
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
``` ```
`sorted`函数需要传入两个参数: `sort(_:)`方法需要传入两个参数:
* 已知类型的数组 * 已知类型的数组
* 闭包函数,该闭包函数需要传入与数组类型相同的两个值,并返回一个布尔类型值来告诉`sorted`函数当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回`true`,反之返回`false` * 闭包函数,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回`true`,反之返回`false`
该例子对一个`String`类型的数组进行排序,因此排序闭包函数类型需为`(String, String) -> Bool` 该例子对一个`String`类型的数组进行排序,因此排序闭包函数类型需为`(String, String) -> Bool`
提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`sort`函数的第二个参数传入: 提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`ssort(_:)`方法的参数传入:
```swift ```swift
func backwards(s1: String, s2: String) -> Bool { func backwards(s1: String, s2: String) -> Bool {
return s1 > s2 return s1 > s2
} }
var reversed = sorted(names, backwards) var reversed = names.sort(backwards)
// reversed 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"] // reversed 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
``` ```
@ -99,7 +99,7 @@ var reversed = sorted(names, backwards)
下面的例子展示了之前`backwards`函数对应的闭包表达式版本的代码: 下面的例子展示了之前`backwards`函数对应的闭包表达式版本的代码:
```swift ```swift
reversed = sorted(names, { (s1: String, s2: String) -> Bool in reversed = names.sort({ (s1: String, s2: String) -> Bool in
return s1 > s2 return s1 > s2
}) })
``` ```
@ -114,34 +114,36 @@ reversed = sorted(names, { (s1: String, s2: String) -> Bool in
因为这个闭包的函数体部分如此短以至于可以将其改写成一行代码: 因为这个闭包的函数体部分如此短以至于可以将其改写成一行代码:
```swift ```swift
reversed = sorted(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } ) reversed = names.sort( { (s1: String, s2: String) -> Bool in return s1 > s2 } )
``` ```
这说明`sorted`函数的整体调用保持不变,一对圆括号仍然包裹住了函数中整个参数集合。而其中一个参数现在变成了内联闭包(相比于`backwards`版本的代码) 这说明`sort(_:)`方法的整体调用保持不变,一对圆括号仍然包裹住了函数中整个参数集合。而其中一个参数现在变成了内联闭包(相比于`backwards`版本的代码)
<a name="inferring_type_from_context"></a> <a name="inferring_type_from_context"></a>
### 根据上下文推断类型Inferring Type From Context ### 根据上下文推断类型Inferring Type From Context
因为排序闭包函数是作为`sorted`函数的参数进行传入的Swift可以推断其参数和返回值的类型。 因为排序闭包函数是作为`sort(_:)`方法的参数进行传入的Swift可以推断其参数和返回值的类型。
`sorted`期望第二个参数是类型为`(String, String) -> Bool`的函数,因此实际上`String`,`String``Bool`类型并不需要作为闭包表达式定义中的一部分。 `sorted`期望第二个参数是类型为`(String, String) -> Bool`的函数,因此实际上`String`,`String``Bool`类型并不需要作为闭包表达式定义中的一部分。
因为所有的类型都可以被正确推断,返回箭头 (`->`) 和围绕在参数周围的括号也可以被省略: 因为所有的类型都可以被正确推断,返回箭头 (`->`) 和围绕在参数周围的括号也可以被省略:
```swift ```swift
reversed = sorted(names, { s1, s2 in return s1 > s2 } ) reversed = names.sort( { s1, s2 in return s1 > s2 } )
``` ```
实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数时,都可以推断出闭包的参数和返回值类型,这意味着您几乎不需要利用完整格式构造任何内联闭包。 实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数时,都可以推断出闭包的参数和返回值类型,这意味着您几乎不需要利用完整格式构造任何内联闭包。
然而您仍然可以明确写出有着完整格式的闭包。如果完整格式的闭包能够提高代码的可读性,则可以采用完整格式的闭包。而在`sort(_:)`方法这个例子里,闭包的目的就是排序,读者能够推测除这个闭包是用于字符串处理的,因为这个闭包是为了处理字符串数组的排序。
<a name="implicit_returns_from_single_expression_closures"></a> <a name="implicit_returns_from_single_expression_closures"></a>
### 单表达式闭包隐式返回Implicit Return From Single-Expression Clossures ### 单表达式闭包隐式返回Implicit Return From Single-Expression Clossures
单行表达式闭包可以通过隐藏`return`关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为: 单行表达式闭包可以通过隐藏`return`关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
```swift ```swift
reversed = sorted(names, { s1, s2 in s1 > s2 } ) reversed = names.sort( { s1, s2 in s1 > s2 } )
``` ```
在这个例子中,`sorted`函数的第二个参数函数类型明确了闭包必须返回一个`Bool`类型值。 在这个例子中,`sort(_:)`方法的第二个参数函数类型明确了闭包必须返回一个`Bool`类型值。
因为闭包函数体只包含了一个单一表达式 (`s1 > s2`),该表达式返回`Bool`类型值,因此这里没有歧义,`return`关键字可以省略。 因为闭包函数体只包含了一个单一表达式 (`s1 > s2`),该表达式返回`Bool`类型值,因此这里没有歧义,`return`关键字可以省略。
<a name="shorthand_argument_names"></a> <a name="shorthand_argument_names"></a>
@ -153,7 +155,7 @@ Swift 自动为内联函数提供了参数名称缩写功能,您可以直接
`in`关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成: `in`关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
```swift ```swift
reversed = sorted(names, { $0 > $1 } ) reversed = names.sort( { $0 > $1 } )
``` ```
在这个例子中,`$0``$1`表示闭包中第一个和第二个`String`类型的参数。 在这个例子中,`$0``$1`表示闭包中第一个和第二个`String`类型的参数。
@ -163,11 +165,11 @@ reversed = sorted(names, { $0 > $1 } )
实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。 实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。
Swift 的`String`类型定义了关于大于号 (`>`) 的字符串实现,其作为一个函数接受两个`String`类型的参数并返回`Bool`类型的值。 Swift 的`String`类型定义了关于大于号 (`>`) 的字符串实现,其作为一个函数接受两个`String`类型的参数并返回`Bool`类型的值。
而这正好与`sorted`函数的第二个参数需要的函数类型相符合。 而这正好与`sort(_:)`方法的第二个参数需要的函数类型相符合。
因此您可以简单地传递一个大于号Swift可以自动推断出您想使用大于号的字符串函数实现 因此您可以简单地传递一个大于号Swift可以自动推断出您想使用大于号的字符串函数实现
```swift ```swift
reversed = sorted(names, >) reversed = names.sort(>)
``` ```
更多关于运算符表达式的内容请查看 [运算符函数](../chapter2/24_Advanced_Operators.html#operator_functions)。 更多关于运算符表达式的内容请查看 [运算符函数](../chapter2/24_Advanced_Operators.html#operator_functions)。
@ -180,7 +182,7 @@ reversed = sorted(names, >)
尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。 尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。
```swift ```swift
func someFunctionThatTakesAClosure(closure: () -> ()) { func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函数体部分 // 函数体部分
} }
@ -201,7 +203,7 @@ someFunctionThatTakesAClosure() {
在上例中作为`sorted`函数参数的字符串排序闭包可以改写为: 在上例中作为`sorted`函数参数的字符串排序闭包可以改写为:
```swift ```swift
reversed = sorted(names) { $0 > $1 } reversed = names.sort() { $0 > $1 }
``` ```
当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。 当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。
@ -281,7 +283,7 @@ Swift最简单的闭包形式是嵌套函数也就是定义在其他函数的
每次调用`incrementor`时,其会以`amount`作为增量增加`runningTotal`的值。 每次调用`incrementor`时,其会以`amount`作为增量增加`runningTotal`的值。
```swift ```swift
func makeIncrementor(forIncrement amount: Int) -> () -> Int { func makeIncrementor(forIncrement amount: Int) -> Void -> Int {
var runningTotal = 0 var runningTotal = 0
func incrementor() -> Int { func incrementor() -> Int {
runningTotal += amount runningTotal += amount
@ -291,7 +293,7 @@ func makeIncrementor(forIncrement amount: Int) -> () -> Int {
} }
``` ```
`makeIncrementor`返回类型为`() -> Int` `makeIncrementor`返回类型为`Void -> Int`
这意味着其返回的是一个函数,而不是一个简单类型值。 这意味着其返回的是一个函数,而不是一个简单类型值。
该函数在每次调用时不接受参数只返回一个`Int`类型的值。 该函数在每次调用时不接受参数只返回一个`Int`类型的值。
关于函数返回其他函数的内容,请查看[函数类型作为返回类型](../chapter2/06_Functions.html#function_types_as_return_types)。 关于函数返回其他函数的内容,请查看[函数类型作为返回类型](../chapter2/06_Functions.html#function_types_as_return_types)。

View File

@ -11,20 +11,20 @@
- [相关值Associated Values](#associated_values) - [相关值Associated Values](#associated_values)
- [原始值Raw Values](#raw_values) - [原始值Raw Values](#raw_values)
枚举定义了一个通用类型的一组相关值,使你可以在你的代码中以一安全的方式来使用这些值。 *枚举*定义了一个通用类型的一组相关值,使你可以在你的代码中以一安全的方式来使用这些值。
如果你熟悉 C 语言,你就会知道,在 C 语言中枚举指定相关名称为一组整型值。Swift 中的枚举更加灵活,不必给每一个枚举成员提供一个值。如果一个值(被认为是“原始”值)被提供给每个枚举成员,则该值可以是一个字符串,一个字符,或是一个整型值或浮点 如果你熟悉 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](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> <a name="enumeration_syntax"></a>
## 枚举语法 ## 枚举语法
使用`enum`关键词并且把它们的整个定义放在一对大括号内: 使用`enum`关键词来创建枚举并且把它们的整个定义放在一对大括号内:
```swift ```swift
enum SomeEnumeration { enum SomeEnumeration {
@ -43,10 +43,10 @@ enum CompassPoint {
} }
``` ```
一个枚举中被定义的值(例如 `North``South``East``West`)是枚举的***成员值***(或者***成员***)。`case`关键词表明新的一行成员值将被定义。 一个枚举中被定义的值(例如 `North``South``East``West`)是枚举的*成员值*(或者*成员*)。`case`关键词表明新的一行成员值将被定义。
> 注意: > 注意:
> 不像 C 和 Objective-C 一样Swift 的枚举成员在被创建时不会被赋予一个默认的整值。在上面的`CompassPoints`例子中,`North``South``East`和`West`不隐式的等于`0``1``2`和`3`。相反的,这些不同的枚举成员在`CompassPoint`的一种显示定义中拥有各自不同的值。 > C 和 Objective-C 不同Swift 的枚举成员在被创建时不会被赋予一个默认的整值。在上面的`CompassPoints`例子中,`North``South``East`和`West`不隐式地赋值为了`0``1``2`和`3`。相反的,这些不同的枚举成员在`CompassPoint`的一种显示定义中拥有各自不同的值。
多个成员值可以出现在同一行上,用逗号隔开: 多个成员值可以出现在同一行上,用逗号隔开:
@ -62,51 +62,51 @@ enum Planet {
var directionToHead = CompassPoint.West var directionToHead = CompassPoint.West
``` ```
`directionToHead`的类型被推断当它被`CompassPoint`的一个可能值初始化。一旦`directionToHead`被声明为一个`CompassPoint`,你可以使用更短的点.语法将其设置为另一个`CompassPoint`的值: `directionToHead`的类型可以在它被`CompassPoint`的一个可能值初始化时推断出来。一旦`directionToHead`被声明为一个`CompassPoint`,你可以使用一个缩写语法.)将其设置为另一个`CompassPoint`的值:
```swift ```swift
directionToHead = .East directionToHead = .East
``` ```
`directionToHead`的类型已知时,当设定它的值时,你可以不再写类型名。使用显式类型的枚举值可以让代码具有更好的可读性。 `directionToHead`的类型已知时,再次为其赋值可以省略枚举名。使用显式类型的枚举值可以让代码具有更好的可读性。
<a name="matching_enumeration_values_with_a_switch_statement"></a> <a name="matching_enumeration_values_with_a_switch_statement"></a>
## 匹配枚举值和`Switch`语句 ## 匹配枚举值和`Switch`语句
你可以匹配单个枚举值和`switch`语句: 你可以使用`switch`语句匹配单个枚举值
```swift ```swift
directionToHead = .South directionToHead = .South
switch directionToHead { switch directionToHead {
case .North: case .North:
println("Lots of planets have a north") print("Lots of planets have a north")
case .South: case .South:
println("Watch out for penguins") print("Watch out for penguins")
case .East: case .East:
println("Where the sun rises") print("Where the sun rises")
case .West: case .West:
println("Where the skies are blue") print("Where the skies are blue")
} }
// 输出 "Watch out for penguins” // 输出 "Watch out for penguins”
``` ```
你可以如此理解这段代码: 你可以这样理解这段代码:
考虑`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`分支来涵盖所有未明确被提出的任何成员: 当不需要匹配每个枚举成员的时候,你可以提供一个默认`default`分支来涵盖所有未明确被提出的枚举成员:
```swift ```swift
let somePlanet = Planet.Earth let somePlanet = Planet.Earth
switch somePlanet { switch somePlanet {
case .Earth: case .Earth:
println("Mostly harmless") print("Mostly harmless")
default: default:
println("Not a safe place for humans") print("Not a safe place for humans")
} }
// 输出 "Mostly harmless” // 输出 "Mostly harmless”
``` ```
@ -114,11 +114,11 @@ default:
<a name="associated_values"></a> <a name="associated_values"></a>
## 相关值Associated Values ## 相关值Associated Values
上一小节的例子演示了一个枚举的成员是如何定义(分类)。你可以为`Planet.Earth`设置一个常量或变量,并且在之后查看这个值。不管怎样,如果有时候能够把其他类型的相关值和成员值一起存储起来会很有用。这能让你存储成员值之外的自定义信息,并且当你每次在代码中使用该成员时允许这个信息产生变化。 上一小节的例子演示了如何定义(分类)枚举的成员。你可以为`Planet.Earth`设置一个常量或变量,并且在赋值之后查看这个值。不管怎样,如果有时候能够把其他类型的*相关值*和成员值一起存储起来会很有用。这能让你存储成员值之外的自定义信息,并且当你每次在代码中使用该成员时允许这个信息产生变化。
你可以定义 Swift 的枚举存储任何类型的相关值如果需要的话每个成员的数据类型可以是各不相同的。枚举的这种特性跟其他语言中的可辨识联合discriminated unions标签联合tagged unions或者变体variants相似。 你可以定义 Swift 的枚举存储任何类型的相关值如果需要的话每个成员的数据类型可以是各不相同的。枚举的这种特性跟其他语言中的可辨识联合discriminated unions标签联合tagged unions或者变体variants相似。
例如,假设一个库存跟踪系统需要利用两种不同类型的条形码来跟踪商品。有些商品上标有 UPC-A 格式的一维码,它使用数字 0 到 9。每一个条形码都有一个代表“数字系统”的数字该数字后接 10 个代表“标识符”的数字。最后一个数字是“检查”位,用来验证代码是否被正确扫描: 例如,假设一个库存跟踪系统需要利用两种不同类型的条形码来跟踪商品。有些商品上标有 UPC-A 格式的一维t条形码,它使用数字 0 到 9。每一个条形码都有一个代表“数字系统”的数字该数字后接 5 个代表“生产代码”的数字接下来是5位“产品代码”。最后一个数字是“检查”位,用来验证代码是否被正确扫描:
<img width="252" height="120" alt="" src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/barcode_UPC_2x.png"> <img width="252" height="120" alt="" src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/barcode_UPC_2x.png">
@ -128,28 +128,28 @@ default:
对于库存跟踪系统来说,能够把 UPC-A 码作为三个整型值的元组,和把 QR 码作为一个任何长度的字符串存储起来是方便的。 对于库存跟踪系统来说,能够把 UPC-A 码作为三个整型值的元组,和把 QR 码作为一个任何长度的字符串存储起来是方便的。
在 Swift 中,用来定义两种商品条码的枚举是这样子的 在 Swift 中,使用如下方式定义两种商品条码的枚举:
```swift ```swift
enum Barcode { enum Barcode {
case UPCA(Int, Int, Int) case UPCA(Int, Int, Int, Int)
case QRCode(String) case QRCode(String)
} }
``` ```
以上代码可以这么理解: 以上代码可以这么理解:
“定义一个名为`Barcode`的枚举类型,它可以是`UPCA`的一个相关值(`Int``Int``Int`),或者`QRCode`的一个字符串类型(`String`)相关值。” “定义一个名为`Barcode`的枚举类型,它可以是`UPCA`的一个相关值(`Int``Int``Int``Int`),或者`QRCode`的一个字符串类型(`String`)相关值。”
这个定义不提供任何`Int``String`的实际值,它只是定义了,当`Barcode`常量和变量等于`Barcode.UPCA``Barcode.QRCode`时,相关值的类型。 这个定义不提供任何`Int``String`的实际值,它只是定义了,当`Barcode`常量和变量等于`Barcode.UPCA``Barcode.QRCode`时,相关值的类型。
然后可以使用任何一种条码类型创建新的条码,如: 然后可以使用任何一种条码类型创建新的条码,如:
```swift ```swift
var productBarcode = Barcode.UPCA(8, 85909_51226, 3) var productBarcode = Barcode.UPCA(8, 85909, 51226, 3)
``` ```
以上例子创建了一个名为`productBarcode`变量,并且赋给它一个`Barcode.UPCA`的相关元组值`(8, 8590951226, 3)`提供的“标识符”值在整数字中有一个下划线,使其便于阅读条形码。 以上例子创建了一个名为`productBarcode`的变量,并且赋给它一个`Barcode.UPCA`的相关元组值`(8, 85909, 51226, 3)`
同一个商品可以被分配给一个不同类型的条形码,如: 同一个商品可以被分配给一个不同类型的条形码,如:
@ -163,32 +163,32 @@ productBarcode = .QRCode("ABCDEFGHIJKLMNOP")
```swift ```swift
switch productBarcode { switch productBarcode {
case .UPCA(let numberSystem, let identifier, let check): case .UPCA(let numberSystem, let manufacturer, let product, let check):
println("UPC-A with value of \(numberSystem), \(identifier), \(check).") print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
case .QRCode(let productCode): case .QRCode(let productCode):
println("QR code with value of \(productCode).") print("QR code: \(productCode).")
} }
// 输出 "QR code with value of ABCDEFGHIJKLMNOP. // 输出 "QR code: ABCDEFGHIJKLMNOP."
``` ```
如果一个枚举成员的所有相关值被提取为常量,或者它们全部被提取为变量,为了简洁,你可以只放置一个`var`或者`let`标注在成员名称前: 如果一个枚举成员的所有相关值被提取为常量,或者它们全部被提取为变量,为了简洁,你可以只放置一个`var`或者`let`标注在成员名称前:
```swift ```swift
switch productBarcode { switch productBarcode {
case let .UPCA(numberSystem, identifier, check): case let .UPCA(numberSystem, manufacturer, product, check):
println("UPC-A with value of \(numberSystem), \(identifier), \(check).") print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
case let .QRCode(productCode): case let .QRCode(productCode):
println("QR code with value of \(productCode).") print("QR code: \(productCode).")
} }
// 输出 "QR code with value of ABCDEFGHIJKLMNOP." // 输出 "QR code: ABCDEFGHIJKLMNOP."
``` ```
<a name="raw_values"></a> <a name="raw_values"></a>
## 原始值Raw Values ## 原始值Raw Values
在[Associated Values](#raw_values)小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的相关值。作为相关值的替代,枚举成员可以被默认值(称为原始值)预先填充,其中这些原始值具有相同的类型。 在[Associated Values](#raw_values)小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的相关值。作为相关值的另一种选择,枚举成员可以被默认值(称为原始值)赋值,其中这些原始值具有相同的类型。
这里是一个枚举成员存储原始 ASCII 的例子: 这里是一个枚举成员存储 ASCII 的例子:
```swift ```swift
enum ASCIIControlCharacter: Character { enum ASCIIControlCharacter: Character {
@ -198,11 +198,11 @@ enum ASCIIControlCharacter: Character {
} }
``` ```
在这里,称为`ASCIIControlCharacter`的枚举的原始值类型被定义为字符型`Character`,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符[`Strings and Characters`](03_Strings_and_Characters.html)部分。 在这里,`ASCIIControlCharacter`的枚举类型的原始值类型被定义为字符型`Character`,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符[`Strings and Characters`](03_Strings_and_Characters.html)部分。
注意,原始值和相关值是不相同的。当你开始在你的代码中定义枚举的时候原始值是被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。相关值是当你在创建一个基于枚举成员的常量或变量时才会被设置,并且每次当你这么做得时候,它的值可以是不同的。 注意,原始值和相关值是不相同的。原始值是当你开始定义枚举的时候被预先赋予的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。相关值你在创建一个基于枚举成员的常量或变量时才会被设置,并且每次当你创建的时候,它的值可以是不同的。
原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。当整型值被用于原始值,如果其他枚举成员没有值,它们会自动递增。 原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。当使用整型值作为原始值,如果其他枚举成员没有值,它们会自动递增。
下面的枚举是对之前`Planet`这个枚举的一个细化,利用原始整型值来表示每个 planet 在太阳系中的顺序: 下面的枚举是对之前`Planet`这个枚举的一个细化,利用原始整型值来表示每个 planet 在太阳系中的顺序:
@ -212,7 +212,7 @@ enum Planet: Int {
} }
``` ```
自动递增意味着`Planet.Venus`的原始值是`2`依次类推。 自动递增意味着`Planet.Venus`的原始值是`2`以此类推。
使用枚举成员的`rawValue`属性可以访问该枚举成员的原始值: 使用枚举成员的`rawValue`属性可以访问该枚举成员的原始值:
@ -220,15 +220,22 @@ enum Planet: Int {
let earthsOrder = Planet.Earth.rawValue let earthsOrder = Planet.Earth.rawValue
// earthsOrder is 3 // earthsOrder is 3
``` ```
### 使用原始值来初始化(Initializing from a Raw Value)
通过参数为`rawValue`构造函数创建特定原始值的枚举。这个例子通过原始值`7`识别`Uranus` 如果你使用原始值的方式创建一个枚举类型,这个枚举将自动获得一个包含原始值参数(参数名为rawValue)的构造器并返回相应的枚举类型或者nil。你可以使用这个构造器来创建新的枚举成员。
下面这个例子通过原始值`7`创建了`Uranus`枚举类型:
```swift ```swift
let possiblePlanet = Planet(rawValue: 7) let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.Uranus // possiblePlanet is of type Planet? and equals Planet.Uranus
``` ```
然而,并非所有可能的`Int`值都可以找到一个匹配的行星。正因为如此,构造函数可以返回一个***可选***的枚举成员。在上面的例子中,`possiblePlanet``Planet?`类型,或“可选的`Planet`”。 然而,并非所有可能的`Int`值都可以找到一个匹配的行星。正因为如此,构造函数可以返回一个*可选*的枚举成员。在上面的例子中,`possiblePlanet``Planet?`类型,或称为“可选的`Planet`”。
> 注意:
> 使用原始值构造器是可失败构造器,因为并不是所有的原始值都会返回一个对应的枚举成员。欲了解更多相关信息,请参见[可失败构造器Failable Initializers](TODO)
如果你试图寻找一个位置为9的行星通过参数为`rawValue`构造函数返回的可选`Planet`值将是`nil` 如果你试图寻找一个位置为9的行星通过参数为`rawValue`构造函数返回的可选`Planet`值将是`nil`
@ -237,15 +244,14 @@ let positionToFind = 9
if let somePlanet = Planet(rawValue: positionToFind) { if let somePlanet = Planet(rawValue: positionToFind) {
switch somePlanet { switch somePlanet {
case .Earth: case .Earth:
println("Mostly harmless") print("Mostly harmless")
default: default:
println("Not a safe place for humans") print("Not a safe place for humans")
} }
} else { } else {
println("There isn't a planet at position \(positionToFind)") print("There isn't a planet at position \(positionToFind)")
} }
// 输出 "There isn't a planet at position 9 // 输出 "There isn't a planet at position 9
``` ```
这个范例使用可选绑定optional binding通过原始值`9`试图访问一个行星。`if let somePlanet = Planet(rawValue: 9)`语句获得一个可选`Planet`,如果可选`Planet`可以被获得,把`somePlanet`设置成该可选`Planet`的内容。在这个范例中,无法检索到位置为`9`的行星,所以`else`分支被执行。 这个范例使用可选绑定optional binding通过原始值`9`试图取得一个行星的引用`if let somePlanet = Planet(rawValue: 9)`语句获得一个可选`Planet`,如果可选`Planet`可以被获得,把`somePlanet`设置成该可选`Planet`的内容。在这个范例中,无法检索到位置为`9`的行星,所以`else`分支被执行。

View File

@ -1,5 +1,5 @@
> 翻译:[shinyzhu](https://github.com/shinyzhu) > 翻译:[shinyzhu](https://github.com/shinyzhu)
> 校对:[pp-prog](https://github.com/pp-prog) > 校对:[pp-prog](https://github.com/pp-prog) [yangsiy](https://github.com/yangsiy)
# 属性 (Properties) # 属性 (Properties)
--- ---
@ -12,16 +12,16 @@
- [全局变量和局部变量Global and Local Variables](#global_and_local_variables) - [全局变量和局部变量Global and Local Variables](#global_and_local_variables)
- [类型属性Type Properties](#type_properties) - [类型属性Type Properties](#type_properties)
**属性**将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,计算属性计算(不是存储)一个值。计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。 *属性*将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,计算属性计算(不是存储)一个值。计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。
存储属性和计算属性通常用于特定类型的实例但是,属性也可以直接用于类型本身,这种属性称为类型属性。 存储属性和计算属性通常特定类型的实例关联。但是,属性也可以直接用于类型本身,这种属性称为类型属性。
另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己的存储属性上,也可以添加到从父类继承的属性上。 另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己定义的存储属性上,也可以添加到从父类继承的属性上。
<a name="stored_properties"></a> <a name="stored_properties"></a>
## 存储属性 ## 存储属性
简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量存储属性可以是*变量存储属性*(用关键字`var`定义),也可以是*常量存储属性*(用关键字`let`定义)。 简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量存储属性可以是*变量存储属性*(用关键字`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)一节。 可以在定义存储属性的时候指定默认值,请参考[构造过程](../chapter2/14_Initialization.html)一章的[默认属性值](../chapter2/14_Initialization.html#default_property_values)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[构造过程](../chapter2/14_Initialization.html)一章的[在初始化阶段修改常量存储属性](../chapter2/14_Initialization.html#modifying_constant_properties_during_initialization)一节。
@ -38,12 +38,12 @@ rangeOfThreeItems.firstValue = 6
// 该区间现在表示整数678 // 该区间现在表示整数678
``` ```
`FixedLengthRange`的实例包含一个名为`firstValue`的变量存储属性和一个名为`length`的常量存储属性。在上面的例子中,`length`在创建实例的时候被赋值,因为它是一个常量存储属性,所以之后无法修改它的值。 `FixedLengthRange`的实例包含一个名为`firstValue`的变量存储属性和一个名为`length`的常量存储属性。在上面的例子中,`length`在创建实例的时候被初始化,因为它是一个常量存储属性,所以之后无法修改它的值。
<a name="stored_properties_of_constant_structure_instances"></a> <a name="stored_properties_of_constant_structure_instances"></a>
### 常量存储属性 ### 常量结构体的存储属性
如果创建了一个结构体的实例并赋值给一个常量,则无法修改实例的任何属性,即使定义了变量存储属性: 如果创建了一个结构体的实例并将其赋值给一个常量,则无法修改实例的任何属性,即使定义了变量存储属性:
```swift ```swift
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4) let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
@ -52,11 +52,11 @@ rangeOfFourItems.firstValue = 6
// 尽管 firstValue 是个变量属性,这里还是会报错 // 尽管 firstValue 是个变量属性,这里还是会报错
``` ```
因为`rangeOfFourItems`声明成了常量(用`let`关键字),即使`firstValue`是一个变量属性,也无法再修改它了。 因为`rangeOfFourItems`声明成了常量(用`let`关键字),即使`firstValue`是一个变量属性,也无法再修改它了。
这种行为是由于结构体struct属于*值类型*。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。 这种行为是由于结构体struct属于*值类型*。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。
属于*引用类型*的类class则不一样把一个引用类型的实例赋给一个常量后,仍然可以修改实例的变量属性。 属于*引用类型*的类class则不一样把一个引用类型的实例赋给一个常量后,仍然可以修改实例的变量属性。
<a name="lazy_stored_properties"></a> <a name="lazy_stored_properties"></a>
### 延迟存储属性 ### 延迟存储属性
@ -64,11 +64,11 @@ rangeOfFourItems.firstValue = 6
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用`lazy`来标示一个延迟存储属性。 延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用`lazy`来标示一个延迟存储属性。
> 注意: > 注意:
> 必须将延迟存储属性声明成变量(使用`var`关键字),因为属性的在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。 > 必须将延迟存储属性声明成变量(使用`var`关键字),因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
延迟属性很有用,当属性的值依赖于在实例的构造过程结束前无法知道具体值的外部因素时,或者当属性的值需要复杂或大量计算时,可以只在需要的时候计算它。 延迟属性很有用,当属性的值依赖于在实例的构造过程结束后才会知道具体值的外部因素时,或者当获得属性的初始值需要复杂或大量计算时,可以只在需要的时候计算它。
下面的例子使用了延迟存储属性来避免复杂类不必要的初始化。例子中定义了`DataImporter``DataManager`两个类,下面是部分代码: 下面的例子使用了延迟存储属性来避免复杂类不必要的初始化。例子中定义了`DataImporter``DataManager`两个类,下面是部分代码:
```swift ```swift
class DataImporter { class DataImporter {
@ -94,30 +94,32 @@ manager.data.append("Some more data")
`DataManager`类包含一个名为`data`的存储属性,初始值是一个空的字符串(`String`)数组。虽然没有写出全部代码,`DataManager`类的目的是管理和提供对这个字符串数组的访问。 `DataManager`类包含一个名为`data`的存储属性,初始值是一个空的字符串(`String`)数组。虽然没有写出全部代码,`DataManager`类的目的是管理和提供对这个字符串数组的访问。
`DataManager`的一个功能是从文件导入数据该功能由`DataImporter`类提供,`DataImporter`需要消耗不少时间完成初始化:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。 `DataManager`的一个功能是从文件导入数据该功能由`DataImporter`类提供,`DataImporter`完成初始化需要消耗不少时间:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。
`DataManager`也可能不从文件中导入数据。所以当`DataManager`的实例被创建时,没必要创建一个`DataImporter`的实例,更明智的是当用到`DataImporter`的时候才去创建它。 `DataManager`也可能不从文件中导入数据就完成了管理数据的功能。所以当`DataManager`的实例被创建时,没必要创建一个`DataImporter`的实例,更明智的是当第一次用到`DataImporter`的时候才去创建它。
由于使用了`lazy``importer`属性只有在第一次被访问的时候才被创建。比如访问它的属性`fileName`时: 由于使用了`lazy``importer`属性只有在第一次被访问的时候才被创建。比如访问它的属性`fileName`时:
```swift ```swift
println(manager.importer.fileName) print(manager.importer.fileName)
// DataImporter 实例的 importer 属性现在被创建了 // DataImporter 实例的 importer 属性现在被创建了
// 输出 "data.txt” // 输出 "data.txt”
``` ```
> 注意:
> 如果一个被标记为`lazy`的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。
<a name="stored_properties_and_instance_variables"></a> <a name="stored_properties_and_instance_variables"></a>
### 存储属性和实例变量 ### 存储属性和实例变量
如果您有过 Objective-C 经验应该知道Objective-C为类实例存储值和引用提供两种方法。对于属性来说也可以使用实例变量作为属性值的后端存储。 如果您有过 Objective-C 经验,应该知道 Objective-C 为类实例存储值和引用提供两种方法。对于属性来说,也可以使用实例变量作为属性值的后端存储。
Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。 Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。一个类型中属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义。
一个类型中属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义。
<a name="computed_properties"></a> <a name="computed_properties"></a>
## 计算属性 ## 计算属性
除存储属性外,类、结构体和枚举可以定义*计算属性*计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值。 除存储属性外,类、结构体和枚举可以定义*计算属性*计算属性不直接存储值,而是提供一个 getter 一个可选的 setter来间接获取和设置其他属性或变量的值。
```swift ```swift
struct Point { struct Point {
@ -130,38 +132,38 @@ struct Rect {
var origin = Point() var origin = Point()
var size = Size() var size = Size()
var center: Point { var center: Point {
get { get {
let centerX = origin.x + (size.width / 2) let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2) let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY) return Point(x: centerX, y: centerY)
} }
set(newCenter) { set(newCenter) {
origin.x = newCenter.x - (size.width / 2) origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2) origin.y = newCenter.y - (size.height / 2)
} }
} }
} }
var square = Rect(origin: Point(x: 0.0, y: 0.0), var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0)) size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0) square.center = Point(x: 15.0, y: 15.0)
println("square.origin is now at (\(square.origin.x), \(square.origin.y))") print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// 输出 "square.origin is now at (10.0, 10.0)” // 输出 "square.origin is now at (10.0, 10.0)”
``` ```
这个例子定义了 3 个几何形状的结构体 这个例子定义了 3 个结构体来描述几何形状:
- `Point`封装了一个`(x, y)`的坐标 - `Point`封装了一个`(x, y)`的坐标
- `Size`封装了一个`width``height` - `Size`封装了一个`width`一个`height`
- `Rect`表示一个有原点和尺寸的矩形 - `Rect`表示一个有原点和尺寸的矩形
`Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点和尺寸来算出,所以不需要将它以显式声明的`Point`来保存。`Rect`的计算属性`center`提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。 `Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点`origin`)和尺寸(`size`算出,所以不需要将它以显式声明的`Point`来保存。`Rect`的计算属性`center`提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。
例子中接下来创建了一个名为`square``Rect`实例,初始值原点是`(0, 0)`,宽度高度都是`10`。如图所示蓝色正方形。 上述例子中创建了一个名为`square``Rect`实例,初始值原点是`(0, 0)`,宽度高度都是`10`。如下图中蓝色正方形所示
`square``center`属性可以通过点运算符(`square.center`)来访问,这会调用 getter 来获取属性的值。跟直接返回已经存在的值不同getter 实际上通过计算然后返回一个新的`Point`来表示`square`的中心点。如代码所示,它正确返回了中心点`(5, 5)` `square``center`属性可以通过点运算符(`square.center`)来访问,这会调用该属性的 getter 来获取的值。跟直接返回已经存在的值不同getter 实际上通过计算然后返回一个新的`Point`来表示`square`的中心点。如代码所示,它正确返回了中心点`(5, 5)`
`center`属性之后被设置了一个新的值`(15, 15)`,表示向右上方移动正方形到如图所示橙色正方形的位置。设置属性`center`的值会调用 setter 来修改属性`origin``x``y`的值,从而实现移动正方形到新的位置。 `center`属性之后被设置了一个新的值`(15, 15)`,表示向右上方移动正方形到如图橙色正方形所示的位置。设置属性`center`的值会调用它的 setter 来修改属性`origin``x``y`的值,从而实现移动正方形到新的位置。
<img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/computedProperties_2x.png" alt="Computed Properties sample" width="388" height="387" /> <img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/computedProperties_2x.png" alt="Computed Properties sample" width="388" height="387" />
@ -175,15 +177,15 @@ struct AlternativeRect {
var origin = Point() var origin = Point()
var size = Size() var size = Size()
var center: Point { var center: Point {
get { get {
let centerX = origin.x + (size.width / 2) let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2) let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY) return Point(x: centerX, y: centerY)
} }
set { set {
origin.x = newValue.x - (size.width / 2) origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2) origin.y = newValue.y - (size.height / 2)
} }
} }
} }
``` ```
@ -193,26 +195,24 @@ struct AlternativeRect {
只有 getter 没有 setter 的计算属性就是*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。 只有 getter 没有 setter 的计算属性就是*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。
> 注意: > 注意:
>
> 必须使用`var`关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。`let`关键字只用来声明常量属性,表示初始化后再也无法修改的值。 > 必须使用`var`关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。`let`关键字只用来声明常量属性,表示初始化后再也无法修改的值。
只读计算属性的声明可以去掉`get`关键字和花括号: 只读计算属性的声明可以去掉`get`关键字和花括号:
```swift ```swift
struct Cuboid { struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0 var width = 0.0, height = 0.0, depth = 0.0
var volume: Double { var volume: Double {
return width * height * depth return width * height * depth
} }
} }
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0) let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// 输出 "the volume of fourByFiveByTwo is 40.0" // 输出 "the volume of fourByFiveByTwo is 40.0"
``` ```
这个例子定义了一个名为`Cuboid`的结构体,表示三维空间的立方体,包含`width``height``depth`属性还有一个名为`volume`的只读计算属性用来返回立方体的体积。设置`volume`的值毫无意义,因为通过`width``height``depth`就能算出`volume`。然而,`Cuboid`提供一个只读计算属性来让外部用户直接获取体积是很有用的。 这个例子定义了一个名为`Cuboid`的结构体,表示三维空间的立方体,包含`width``height``depth`属性。结构体还有一个名为`volume`的只读计算属性用来返回立方体的体积。设置`volume`的值毫无意义,因为无法确定修改`width``height``depth`三者中的哪些值来匹配新的`volume`,从而造成歧义。然而,`Cuboid`提供一个只读计算属性来让外部用户直接获取体积是很有用的。
<a name="property_observers"></a> <a name="property_observers"></a>
## 属性观察器 ## 属性观察器
@ -222,34 +222,34 @@ println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重载请参考[继承](chapter/13_Inheritance.html)一章的[重载](chapter/13_Inheritance.html#overriding)。 可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重载请参考[继承](chapter/13_Inheritance.html)一章的[重载](chapter/13_Inheritance.html#overriding)。
> 注意: > 注意:
> 不需要为无法重载的计算属性添加属性观察器,因为可以通过 setter 直接监控和响应值的变化。 > 不需要为重载的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。
可以为属性添加如下的一个或全部观察器: 可以为属性添加如下的一个或全部观察器:
- `willSet`设置新的值之前调用 - `willSet`在新的值被设置之前调用
- `didSet`在新的值被设置之后立即调用 - `didSet`在新的值被设置之后立即调用
`willSet`观察器会将新的属性值作为固定参数传入,在`willSet`的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称`newValue`表示。 `willSet`观察器会将新的属性值作为常量参数传入,在`willSet`的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称`newValue`表示。
类似地,`didSet`观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名`oldValue` 类似地,`didSet`观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名`oldValue`
> 注意: > 注意:
> > 父类的属性在子类的构造器中被赋值时,它在父类中的`willSet`和`didSet`观察器会被调用。
> `willSet`和`didSet`观察器在属性初始化过程中不会被调用,它们只会当属性的值在初始化之外的地方被设置时被调用 > 有关构造器代理的更多信息,请参考[值类型的构造器代理](chapter/14_Initialization.html#initializer_delegation_for_value_types)和[构造器链](chapter/14_Initialization.html#initialization_chain)
这里是一个`willSet``didSet`的实际例子,其中定义了一个名为`StepCounter`的类,用来统计当人步行时的总步数可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。 这里是一个`willSet``didSet`的实际例子,其中定义了一个名为`StepCounter`的类,用来统计当人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。
```swift ```swift
class StepCounter { class StepCounter {
var totalSteps: Int = 0 { var totalSteps: Int = 0 {
willSet(newTotalSteps) { willSet(newTotalSteps) {
println("About to set totalSteps to \(newTotalSteps)") print("About to set totalSteps to \(newTotalSteps)")
} }
didSet { didSet {
if totalSteps > oldValue { if totalSteps > oldValue {
println("Added \(totalSteps - oldValue) steps") print("Added \(totalSteps - oldValue) steps")
}
} }
}
} }
} }
let stepCounter = StepCounter() let stepCounter = StepCounter()
@ -270,19 +270,19 @@ stepCounter.totalSteps = 896
例子中的`willSet`观察器将表示新值的参数自定义为`newTotalSteps`,这个观察器只是简单的将新的值输出。 例子中的`willSet`观察器将表示新值的参数自定义为`newTotalSteps`,这个观察器只是简单的将新的值输出。
`didSet`观察器在`totalSteps`的值改变后被调用,它把新的值和旧的值进行对比,如果总的步数增加了,就输出一个消息表示增加了多少步。`didSet`没有提供自定义名称,所以默认值`oldValue`表示旧值的参数名。 `didSet`观察器在`totalSteps`的值改变后被调用,它把新的值和旧的值进行对比,如果总的步数增加了,就输出一个消息表示增加了多少步。`didSet`没有为旧的值提供自定义名称,所以默认值`oldValue`表示旧值的参数名。
> 注意: > 注意:
> 如果在`didSet`观察器里为属性赋值,这个值会替换观察器之前设置的值。 > 如果在一个属性的`didSet`观察器里为赋值,这个值会替换观察器之前设置的值。
<a name="global_and_local_variables"></a> <a name="global_and_local_variables"></a>
##全局变量和局部变量 ##全局变量和局部变量
计算属性和属性观察器所描述的模式也可以用于*全局变量*和*局部变量*全局变量是在函数、方法、闭包或任何类型之外定义的变量局部变量是在函数、方法或闭包内部定义的变量。 计算属性和属性观察器所描述的模式也可以用于*全局变量*和*局部变量*全局变量是在函数、方法、闭包或任何类型之外定义的变量局部变量是在函数、方法或闭包内部定义的变量。
前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它提供特定类型的存储空间,并允许读取和写入。 前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它提供特定类型的存储空间,并允许读取和写入。
另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器计算型变量跟计算属性一样,返回一个计算的值而不是存储值,声明格式也完全一样。 另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器计算型变量跟计算属性一样,返回一个计算的值而不是存储值,声明格式也完全一样。
> 注意: > 注意:
> 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记`lazy`特性。 > 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记`lazy`特性。
@ -297,9 +297,7 @@ stepCounter.totalSteps = 896
类型属性用于定义特定类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。 类型属性用于定义特定类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。
对于值类型指结构体和枚举可以定义存储型和计算型类型属性对于类class则只能定义计算型类型属性。 值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样只能定义成变量属性。
值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样定义成变量属性。
> 注意: > 注意:
> 跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。 > 跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。
@ -307,26 +305,30 @@ stepCounter.totalSteps = 896
<a name="type_property_syntax"></a> <a name="type_property_syntax"></a>
###类型属性语法 ###类型属性语法
在 C 或 Objective-C 中,静态常量和静态变量的定义是通过特定类型加上`global`关键字。在 Swift 编程语言中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。 在 C 或 Objective-C 中,与某个类型关联的静态常量和静态变量,是作为全局(*global*)静态变量定义的。但是在 Swift 编程语言中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。
使用关键字`static`来定义类型的类型属性,关键字`class`来为类class定义类型属性。下面的例子演示了存储型和计算型类型属性的语法: 使用关键字`static`来定义类型属性。在为类class定义计算型类型属性时可以使用关键字`class`来支持子类对父类的实现进行重写。下面的例子演示了存储型和计算型类型属性的语法:
```swift ```swift
struct SomeStructure { struct SomeStructure {
static var storedTypeProperty = "Some value." static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int { static var computedTypeProperty: Int {
// 这里返回一个 Int 值 return 1
} }
} }
enum SomeEnumeration { enum SomeEnumeration {
static var storedTypeProperty = "Some value." static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int { static var computedTypeProperty: Int {
// 这里返回一个 Int 值 return 6
} }
} }
class SomeClass { class SomeClass {
class var computedTypeProperty: Int { static var storedTypeProperty = "Some value."
// 这里返回一个 Int 值 static var computedTypeProperty: Int {
return 27
}
class var overrideableComputedTypeProperty: Int {
return 107
} }
} }
``` ```
@ -337,17 +339,18 @@ class SomeClass {
<a name="querying_and_setting_type_properties"></a> <a name="querying_and_setting_type_properties"></a>
###获取和设置类型属性的值 ###获取和设置类型属性的值
跟实例的属性一样,类型属性的访问也是通过点运算符来进行但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如: 跟实例的属性一样,类型属性的访问也是通过点运算符来进行但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如:
```swift ```swift
println(SomeClass.computedTypeProperty) print(SomeStructure.storedTypeProperty)
// 输出 "42" // 输出 "Some value."
println(SomeStructure.storedTypeProperty)
// 输出 "Some value."
SomeStructure.storedTypeProperty = "Another value." SomeStructure.storedTypeProperty = "Another value."
println(SomeStructure.storedTypeProperty) print(SomeStructure.storedTypeProperty)
// 输出 "Another value.” // 输出 "Another value.”
print(SomeEnumeration.computedTypeProperty)
// 输出 "6"
print(SomeClass.computedTypeProperty)
// 输出 "27"
``` ```
下面的例子定义了一个结构体,使用两个存储型类型属性来表示多个声道的声音电平值,每个声道有一个 0 到 10 之间的整数表示声音电平值。 下面的例子定义了一个结构体,使用两个存储型类型属性来表示多个声道的声音电平值,每个声道有一个 0 到 10 之间的整数表示声音电平值。
@ -356,23 +359,23 @@ println(SomeStructure.storedTypeProperty)
<img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/staticPropertiesVUMeter_2x.png" alt="Static Properties VUMeter" width="243" height="357" /> <img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/staticPropertiesVUMeter_2x.png" alt="Static Properties VUMeter" width="243" height="357" />
上面所描述的声道模型使用`AudioChannel`结构体来表示: 上面所描述的声道模型使用`AudioChannel`结构体的实例来表示:
```swift ```swift
struct AudioChannel { struct AudioChannel {
static let thresholdLevel = 10 static let thresholdLevel = 10
static var maxInputLevelForAllChannels = 0 static var maxInputLevelForAllChannels = 0
var currentLevel: Int = 0 { var currentLevel: Int = 0 {
didSet { didSet {
if currentLevel > AudioChannel.thresholdLevel { if currentLevel > AudioChannel.thresholdLevel {
// 将新电平值设置为阀值 // 将新电平值设置为阀值
currentLevel = AudioChannel.thresholdLevel currentLevel = AudioChannel.thresholdLevel
}
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
// 存储当前电平值作为新的最大输入电平
AudioChannel.maxInputLevelForAllChannels = currentLevel
}
} }
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
// 存储当前电平值作为新的最大输入电平
AudioChannel.maxInputLevelForAllChannels = currentLevel
}
}
} }
} }
``` ```
@ -383,10 +386,10 @@ struct AudioChannel {
`AudioChannel`也定义了一个名为`currentLevel`的实例存储属性,表示当前声道现在的电平值,取值为 0 到 10。 `AudioChannel`也定义了一个名为`currentLevel`的实例存储属性,表示当前声道现在的电平值,取值为 0 到 10。
属性`currentLevel`包含`didSet`属性观察器来检查每次新设置后的属性值,有如下两个检查: 属性`currentLevel`包含`didSet`属性观察器来检查每次新设置后的属性值,有如下两个检查:
- 如果`currentLevel`的新值大于允许的阈值`thresholdLevel`,属性观察器将`currentLevel`的值限定为阈值`thresholdLevel` - 如果`currentLevel`的新值大于允许的阈值`thresholdLevel`,属性观察器将`currentLevel`的值限定为阈值`thresholdLevel`
- 如果修正后的`currentLevel`值大于任何之前任意`AudioChannel`实例中的值,属性观察器将新值保存在静态属性`maxInputLevelForAllChannels`中。 - 如果前一个修正后的`currentLevel`值大于任何之前任意`AudioChannel`实例中的值,属性观察器将新值保存在静态类型属性`maxInputLevelForAllChannels`中。
> 注意: > 注意:
> 在第一个检查过程中,`didSet`属性观察器将`currentLevel`设置成了不同的值,但这时不会再次调用属性观察器。 > 在第一个检查过程中,`didSet`属性观察器将`currentLevel`设置成了不同的值,但这时不会再次调用属性观察器。
@ -402,9 +405,9 @@ var rightChannel = AudioChannel()
```swift ```swift
leftChannel.currentLevel = 7 leftChannel.currentLevel = 7
println(leftChannel.currentLevel) print(leftChannel.currentLevel)
// 输出 "7" // 输出 "7"
println(AudioChannel.maxInputLevelForAllChannels) print(AudioChannel.maxInputLevelForAllChannels)
// 输出 "7" // 输出 "7"
``` ```
@ -412,8 +415,8 @@ println(AudioChannel.maxInputLevelForAllChannels)
```swift ```swift
rightChannel.currentLevel = 11 rightChannel.currentLevel = 11
println(rightChannel.currentLevel) print(rightChannel.currentLevel)
// 输出 "10" // 输出 "10"
println(AudioChannel.maxInputLevelForAllChannels) print(AudioChannel.maxInputLevelForAllChannels)
// 输出 "10" // 输出 "10"
``` ```

View File

@ -11,9 +11,9 @@
- [下标脚本用法](#subscript_usage) - [下标脚本用法](#subscript_usage)
- [下标脚本选项](#subscript_options) - [下标脚本选项](#subscript_options)
*下标脚本* 可以定义在类Class、结构体structure和枚举enumeration这些目标中可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写 `someArray[index]` ,访问字典(Dictionary)实例中的元素可以这样写 `someDictionary[key]` *下标脚本* 可以定义在类Class、结构体structure和枚举enumeration这些目标中可以认为是访问集合collection列表list或序列sequence的快捷方式使用下标脚本的索引设置和获取值,不需要再调用实例的特定的赋值和访问方法。举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写 `someArray[index]` ,访问字典(Dictionary)实例中的元素可以这样写 `someDictionary[key]`
对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个 对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,下标脚本不限于单个纬度,你可以定义多个入参的下标脚本满足自定义类型的需求
> 译者:这里附属脚本重载在本小节中原文并没有任何演示 > 译者:这里附属脚本重载在本小节中原文并没有任何演示
@ -63,7 +63,7 @@ println("3的6倍是\(threeTimesTable[6])")
你可以通过下标脚本来得到结果,比如`threeTimesTable[6]`。这条语句访问了`threeTimesTable`的第六个元素,返回`6``3`倍即`18` 你可以通过下标脚本来得到结果,比如`threeTimesTable[6]`。这条语句访问了`threeTimesTable`的第六个元素,返回`6``3`倍即`18`
>注意: >注意:
> `TimesTable`例子是基于一个固定的数学公式。它并不适合开放写权限来对`threeTimesTable[someIndex]`进行赋值操作,这也是为什么附属脚本只定义为只读的原因。 > `TimesTable`例子是基于一个固定的数学公式。它并不适合对`threeTimesTable[someIndex]`进行赋值操作,这也是为什么附属脚本只定义为只读的原因。
<a name="subscript_usage"></a> <a name="subscript_usage"></a>
## 下标脚本用法 ## 下标脚本用法
@ -77,7 +77,7 @@ var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2 numberOfLegs["bird"] = 2
``` ```
上例定义一个名为`numberOfLegs`的变量并用一个字典字面量初始化出了包含三对键值的字典实例。`numberOfLegs`的字典存放值类型推断为`Dictionary<String, Int>`。字典实例创建完成之后通过下标脚本的方式将整型值`2`赋值到字典实例的索引为`bird`的位置中。 上例定义一个名为`numberOfLegs`的变量并用一个字典字面量初始化出了包含三对键值的字典实例。`numberOfLegs`的字典存放值类型推断为`[String:Int]`。字典实例创建完成之后通过下标脚本的方式将整型值`2`赋值到字典实例的索引为`bird`的位置中。
更多关于字典Dictionary下标脚本的信息请参考[读取和修改字典](../chapter2/04_Collection_Types.html) 更多关于字典Dictionary下标脚本的信息请参考[读取和修改字典](../chapter2/04_Collection_Types.html)
@ -118,7 +118,7 @@ struct Matrix {
} }
``` ```
`Matrix`提供了一个两个入参的构造方法,入参分别是`rows``columns`,创建了一个足够容纳`rows * columns`个数的`Double`类型数组。为了存储将数组的大小和数组每个元素初始值0.0,都传入数组的构造方法中来创建一个正确大小的新数组。关于数组的构造方法和析构方法请参考[创建并且构造一个数组](../chapter2/04_Collection_Types.html)。 `Matrix`提供了一个两个入参的构造方法,入参分别是`rows``columns`,创建了一个足够容纳`rows * columns`个数的`Double`类型数组。通过传入数组长度和初始值0.0到数组的一个构造器,将`Matrix`中每个元素初始值0.0。关于数组的构造方法和析构方法请参考[创建并且构造一个数组](../chapter2/04_Collection_Types.html)。
你可以通过传入合适的`row``column`的数量来构造一个新的`Matrix`实例: 你可以通过传入合适的`row``column`的数量来构造一个新的`Matrix`实例:
@ -151,7 +151,7 @@ matrix[1, 0] = 3.2
3.2, 0.0] 3.2, 0.0]
``` ```
`Matrix`下标脚本的`getter``setter`中同时调用了下标脚本入参的`row``column`是否有效的判断。为了方便进行断言,`Matrix`包含了一个名为`indexIsValid`的成员方法,用来确认入参的`row``column`值是否会造成数组越界: `Matrix`下标脚本的`getter``setter`中同时调用了下标脚本入参的`row``column`是否有效的判断。为了方便进行断言,`Matrix`包含了一个名为`indexIsValidForRow(_:column:)`的成员方法,用来确认入参的`row``column`值是否会造成数组越界:
```swift ```swift
func indexIsValidForRow(row: Int, column: Int) -> Bool { func indexIsValidForRow(row: Int, column: Int) -> Bool {

View File

@ -11,11 +11,11 @@
- [重写Overriding](#overriding) - [重写Overriding](#overriding)
- [防止重写](#preventing_overrides) - [防止重写](#preventing_overrides)
一个类可以*继承inherit*另一个类的方法methods属性property)和其它特性。当一个类继承其它类时,继承类叫*子类subclass*,被继承类叫*超类或父类superclass*。在 Swift 中,继承是区分「类」与其它类型的一个基本特征。 一个类可以*继承inherit*另一个类的方法methods属性properties)和其它特性。当一个类继承其它类时,继承类叫*子类subclass*,被继承类叫*超类或父类superclass*。在 Swift 中,继承是区分「类」与其它类型的一个基本特征。
在 Swift 中类可以调用和访问超类的方法属性和下标脚本subscripts并且可以重写override这些方法属性和下标脚本来优化或修改它们的行为。Swift 会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。 在 Swift 中类可以调用和访问超类的方法属性和下标脚本subscripts并且可以重写override这些方法属性和下标脚本来优化或修改它们的行为。Swift 会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。
可以为类中继承来的属性添加属性观察器property observer这样一来当属性值改变时类就会被通知到。可以为任何属性添加属性观察器无论它原本被定义为存储型属性stored property还是计算型属性computed property 可以为类中继承来的属性添加属性观察器property observers这样一来当属性值改变时类就会被通知到。可以为任何属性添加属性观察器无论它原本被定义为存储型属性stored property还是计算型属性computed property
<a name="defining_a_base_class"></a> <a name="defining_a_base_class"></a>
## 定义一个基类Base class ## 定义一个基类Base class
@ -27,7 +27,7 @@ Swift 中的类并不是从一个通用的基类继承而来。如果你不为
下面的例子定义了一个叫`Vehicle`的基类。这个基类声明了一个名为`currentSpeed `默认值是0.0的存储属性(属性类型推断为`Double `)。`currentSpeed `属性的值被一个`String` 类型的只读计算型属性`description`使用,用来创建车辆的描述。 下面的例子定义了一个叫`Vehicle`的基类。这个基类声明了一个名为`currentSpeed `默认值是0.0的存储属性(属性类型推断为`Double `)。`currentSpeed `属性的值被一个`String` 类型的只读计算型属性`description`使用,用来创建车辆的描述。
`Vehicle`基类也定义了一个名为`makeNoise`的方法。这个方法实际上不为`Vehicle`实例做任何事,但之后将会被`Vehicle`的子类定制 `Vehicle`基类也定义了一个名为`makeNoise`的方法。这个方法实际上不为`Vehicle`实例做任何事,但之后将会被`Vehicle`的子类定制
```swift ```swift
class Vehicle { class Vehicle {
@ -41,7 +41,7 @@ class Vehicle {
} }
``` ```
您可以用初始化语法创建一个`Vehicle `的新实例,即 `TypeName`后面跟一个空括号: 您可以用初始化语法创建一个`Vehicle `的新实例,即类名后面跟一个空括号:
```swift ```swift
let someVehicle = Vehicle() let someVehicle = Vehicle()
``` ```
@ -51,7 +51,9 @@ let someVehicle = Vehicle()
```swift ```swift
println("Vehicle: \(someVehicle.description)") println("Vehicle: \(someVehicle.description)")
// Vehicle: traveling at 0.0 miles per hour // Vehicle: traveling at 0.0 miles per hour
``` ```
`Vehicle`类定义了一个通用特性的车辆类,实际上没什么用处。为了让它变得更加有用,需要改进它能够描述一个更加具体的车辆类。
<a name="subclassing"></a> <a name="subclassing"></a>
## 子类生成Subclassing ## 子类生成Subclassing
@ -66,17 +68,13 @@ class SomeClass: SomeSuperclass {
} }
``` ```
下一个例子,定义一个更具体的车辆类`Bicycle`。这个新类是在 `Vehicle`类的基础上创建起来。因此你需要将`Vehicle`类放在 `Bicycle`类后面,用冒号分隔。 下一个例子,定义一个叫`Bicycle`的子类,继承成父类`Vehicle`
我们可以将这读作:
“定义一个新的类叫`Bicycle `,它继承了`Vehicle`的特性”;
```swift ```swift
class Bicycle: Vehicle { class Bicycle: Vehicle {
var hasBasket = false var hasBasket = false
} }
``` ```
新的`Bicycle`类自动获得`Vehicle`类的所有特性,比如 `currentSpeed ``description`属性,还有它的`makeNoise`方法。 新的`Bicycle`类自动获得`Vehicle`类的所有特性,比如 `currentSpeed ``description`属性,还有它的`makeNoise`方法。
@ -161,7 +159,7 @@ train.makeNoise()
### 重写属性 ### 重写属性
你可以重写继承来的实例属性或类属性提供自己定制的getter和setter或添加属性观察器使重写的属性观察属性值什么时候发生改变。 你可以重写继承来的实例属性或类属性提供自己定制的getter和setter或添加属性观察器使重写的属性可以观察属性值什么时候发生改变。
#### 重写属性的Getters和Setters #### 重写属性的Getters和Setters
@ -228,7 +226,7 @@ println("AutomaticCar: \(automatic.description)")
你可以通过把方法,属性或下标脚本标记为*`final`*来防止它们被重写,只需要在声明关键字前加上`final`特性即可。(例如:`final var`, `final func`, `final class func`, 以及 `final subscript` 你可以通过把方法,属性或下标脚本标记为*`final`*来防止它们被重写,只需要在声明关键字前加上`final`特性即可。(例如:`final var`, `final func`, `final class func`, 以及 `final subscript`
如果你重写了`final`方法,属性或下标脚本,在编译时会报错。在扩展中,你添加到类里的方法,属性或下标脚本也可以在扩展的定义里标记为 final。 如果你重写了`final`方法,属性或下标脚本,在编译时会报错。在扩展中的方法,属性或下标脚本也可以在扩展的定义里标记为 final。
你可以通过在关键字`class`前添加`final`特性(`final class`)来将整个类标记为 final 的,这样的类是不可被继承的,否则会报编译错误 你可以通过在关键字`class`前添加`final`特性(`final class`)来将整个类标记为 final 的,这样的类是不可被继承的,任何子类试图继承此类时,在编译时会报错

View File

@ -41,7 +41,7 @@ struct BlackjackCard {
case .Jack, .Queen, .King: case .Jack, .Queen, .King:
return Values(first: 10, second: nil) return Values(first: 10, second: nil)
default: default:
return Values(first: self.toRaw(), second: nil) return Values(first: self.rawValue, second: nil)
} }
} }
} }
@ -49,7 +49,7 @@ struct BlackjackCard {
// BlackjackCard 的属性和方法 // BlackjackCard 的属性和方法
let rank: Rank, suit: Suit let rank: Rank, suit: Suit
var description: String { var description: String {
var output = "suit is \(suit.toRaw())," var output = "suit is \(suit.rawValue),"
output += " value is \(rank.values.first)" output += " value is \(rank.values.first)"
if let second = rank.values.second { if let second = rank.values.second {
output += " or \(second)" output += " or \(second)"
@ -63,12 +63,12 @@ struct BlackjackCard {
枚举型的`Rank`用来描述扑克牌从`Ace`~10,`J`,`Q`,`K`,13张牌并分别用一个`Int`类型的值表示牌的面值。(这个`Int`类型的值不适用于`Ace`,`J`,`Q`,`K`的牌)。 枚举型的`Rank`用来描述扑克牌从`Ace`~10,`J`,`Q`,`K`,13张牌并分别用一个`Int`类型的值表示牌的面值。(这个`Int`类型的值不适用于`Ace`,`J`,`Q`,`K`的牌)。
如上文所提到的,枚举型`Rank`在自己内部定义了一个嵌套结构体`Values`。这个结构体包含两个变量,只有`Ace`有两个数值,其余牌都只有一个数值。结构体`Values`中定义的两个属性: 如上文所提到的,枚举型`Rank`在自己内部定义了一个嵌套结构体`Values`这个结构体,只有`Ace`有两个数值,其余牌都只有一个数值。结构体`Values`中定义的两个属性:
`first`, 为` Int` `first`, 为` Int`
`second`, 为 `Int?`, 或 “optional `Int` `second`, 为 `Int?`, 或 “optional `Int`
`Rank`定义了一个计算属性`values`,这个计算属性会根据牌的面值,用适当的数值去初始化`Values`实例,并赋值给`values`。对于`J`,`Q`,`K`,`Ace`会使用特殊数值,对于数字面值的牌使用`Int`类型的值。 `Rank`定义了一个计算属性`values`它将会返回一个结构体`Values`的实例。这个计算属性会根据牌的面值,用适当的数值去初始化`Values`实例,并赋值给`values`。对于`J`,`Q`,`K`,`Ace`会使用特殊数值,对于数字面值的牌使用`Int`类型的值。
`BlackjackCard`结构体自身有两个属性—`rank``suit`,也同样定义了一个计算属性`description``description`属性用`rank``suit`的中内容来构建对这张扑克牌名字和数值的描述,并用可选类型`second`来检查是否存在第二个值,若存在,则在原有的描述中增加对第二数值的描述。 `BlackjackCard`结构体自身有两个属性—`rank``suit`,也同样定义了一个计算属性`description``description`属性用`rank``suit`的中内容来构建对这张扑克牌名字和数值的描述,并用可选类型`second`来检查是否存在第二个值,若存在,则在原有的描述中增加对第二数值的描述。
@ -76,7 +76,7 @@ struct BlackjackCard {
```swift ```swift
let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades) let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades)
println("theAceOfSpades: \(theAceOfSpades.description)") print("theAceOfSpades: \(theAceOfSpades.description)")
// 打印出 "theAceOfSpades: suit is ♠, value is 1 or 11" // 打印出 "theAceOfSpades: suit is ♠, value is 1 or 11"
``` ```
@ -88,9 +88,9 @@ println("theAceOfSpades: \(theAceOfSpades.description)")
在外部对嵌套类型的引用,以被嵌套类型的名字为前缀,加上所要引用的属性名: 在外部对嵌套类型的引用,以被嵌套类型的名字为前缀,加上所要引用的属性名:
```swift ```swift
let heartsSymbol = BlackjackCard.Suit.Hearts.toRaw() let heartsSymbol = BlackjackCard.Suit.Hearts.rawValue
// 红心的符号 为 "♡" // 红心的符号 为 "♡"
``` ```
对于上面这个例子,这样可以使`Suit`, `Rank`, 和 `Values`的名字尽可能的短,因为它们的名字会自然的由定义的上下文来限定。 对于上面这个例子,这样可以使`Suit`, `Rank`, 和 `Values`的名字尽可能的短,因为它们的名字会自然的由定义它们的上下文来限定。

View File

@ -1,5 +1,5 @@
> 翻译:[xiehurricane](https://github.com/xiehurricane) > 翻译:[xiehurricane](https://github.com/xiehurricane)
> 校对:[happyming](https://github.com/happyming) > 校对:[happyming](https://github.com/happyming) [yangsiy](https://github.com/yangsiy)
# 类型转换Type Casting # 类型转换Type Casting
----------------- -----------------
@ -14,16 +14,16 @@
_类型转换_可以判断实例的类型也可以将实例看做是其父类或者子类的实例。 _类型转换_可以判断实例的类型也可以将实例看做是其父类或者子类的实例。
类型转换在 Swift 中使用`is``as`操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。 类型转换在 Swift 中使用 `is``as` 操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。
你也可以用来检查一个类是否实现了某个协议,就像在 [Checking for Protocol Conformance](Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-XID_363)部分讲述的一样。 你也可以用来检查一个类是否实现了某个协议,就像在 [Checking for Protocol Conformance](Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-XID_363)部分讲述的一样。
<a name="defining_a_class_hierarchy_for_type_casting"></a> <a name="defining_a_class_hierarchy_for_type_casting"></a>
## 定义一个类层次作为例子 ## 定义一个类层次作为例子
你可以将用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型转换的例子。 你可以将类型转换用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型转换的例子。
第一个代码片段定义了一个新的基础类`MediaItem`。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个 `String` 类型的 `name` 属性,和一个`init name`初始化器。(假定所有的媒体项都有个名称。) 第一个代码片段定义了一个新的基础类 `MediaItem`。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个 `String` 类型的 `name` 属性,和一个 `init name` 初始化器。(假定所有的媒体项都有个名称。)
```swift ```swift
class MediaItem { class MediaItem {
@ -34,7 +34,7 @@ class MediaItem {
} }
``` ```
下一个代码段定义了 `MediaItem` 的两个子类。第一个子类`Movie`,在父类(或者说基类)的基础上增加了一个 `director`(导演) 属性,和相应的初始化器。第二个在父类的基础上增加了一个 `artist`(艺术家) 属性,和相应的初始化器: 下一个代码段定义了 `MediaItem` 的两个子类。第一个子类 `Movie` 封装了与电影相关的额外信息,在父类(或者说基类)的基础上增加了一个 `director`(导演)属性,和相应的初始化器。第二个子类 `Song`在父类的基础上增加了一个 `artist`(艺术家)属性,和相应的初始化器:
```swift ```swift
class Movie: MediaItem { class Movie: MediaItem {
@ -54,7 +54,7 @@ class Song: MediaItem {
} }
``` ```
最后一个代码段创建了一个数组常量 `library`,包含两个`Movie`实例和三个`Song`实例。`library`的类型是在它被初始化时根据它数组中所包含的内容推断来的。Swift 的类型检测器能够演绎出`Movie``Song` 有共同的父类 `MediaItem` ,所以它推断出 `[MediaItem]` 类作为 `library` 的类型。 最后一个代码段创建了一个数组常量 `library`,包含两个 `Movie` 实例和三个 `Song` 实例。`library` 的类型是在它被初始化时根据它数组中所包含的内容推断来的。Swift的类型检测器能够推理出 `Movie``Song` 有共同的父类 `MediaItem`,所以它推断出 `[MediaItem]` 类作为 `library` 的类型。
```swift ```swift
let library = [ let library = [
@ -67,14 +67,14 @@ let library = [
// the type of "library" is inferred to be [MediaItem] // the type of "library" is inferred to be [MediaItem]
``` ```
在幕后`library` 里存储的媒体项依然是 `Movie``Song` 类型的但是,若你迭代它,取出的实例会是 `MediaItem` 类型的,而不是 `Movie``Song` 类型。为了让它们作为它们本来的类型工作,你需要检查它们的类型或者向下转换它们的类型到其它类型,就像下面描述的一样。 在幕后 `library` 里存储的媒体项依然是 `Movie``Song` 类型的但是,若你迭代它,依次取出的实例会是 `MediaItem` 类型的,而不是 `Movie``Song` 类型。为了让它们作为原本的类型工作,你需要检查它们的类型或者向下转换它们到其它类型,就像下面描述的一样。
<a name="checking_type"></a> <a name="checking_type"></a>
## 检查类型Checking Type ## 检查类型Checking Type
用类型检查操作符(`is`)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 `true` ,否则返回 `false` 用类型检查操作符(`is`)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 `true`,否则返回 `false`
下面的例子定义了两个变量,`movieCount``songCount`,用来计算数组`library``Movie``Song` 类型的实例数量。 下面的例子定义了两个变量,`movieCount``songCount`,用来计算数组 `library``Movie``Song` 类型的实例数量。
```swift ```swift
var movieCount = 0 var movieCount = 0
@ -88,38 +88,38 @@ for item in library {
} }
} }
println("Media library contains \(movieCount) movies and \(songCount) songs") print("Media library contains \(movieCount) movies and \(songCount) songs")
// prints "Media library contains 2 movies and 3 songs" // prints "Media library contains 2 movies and 3 songs"
``` ```
示例迭代了数组 `library` 中的所有项。每一次, `for`-`in` 循环设置 示例迭代了数组 `library` 中的所有项。每一次,`for`-`in` 循环设置
`item` 为数组中的下一个 `MediaItem` `item` 为数组中的下一个 `MediaItem`
若当前 `MediaItem` 是一个 `Movie` 类型的实例, `item is Movie` 返回 若当前 `MediaItem` 是一个 `Movie` 类型的实例,`item is Movie` 返回
`true`,相反返回 `false`。同样的,`item is `true`,相反返回 `false`。同样的,`item is
Song`检查item是否为`Song`类型的实例。在循环结束后,`movieCount``songCount`的值就是被找到属于各自的类型的实例数量。 Song` 检查item是否为 `Song` 类型的实例。在循环结束后,`movieCount``songCount` 的值就是被找到属于各自的类型的实例数量。
<a name="downcasting"></a> <a name="downcasting"></a>
## 向下转型Downcasting ## 向下转型Downcasting
某类型的一个常量或变量可能在幕后实际上属于一个子类。你可以相信,上面就是这种情况你可以尝试向下转到它的子类型,用类型转换操作符(`as`) 某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试向下转到它的子类型,用类型转换操作符(`as?``as!`)
因为向下转型可能会失败,类型转型操作符带有两种不同形式。可选形式( optional form `as?` 返回一个你试图下转成的类型的可选值optional value。强制形式 `as` 把试图向下转型和强制解包force-unwraps结果作为一个混合动作。 因为向下转型可能会失败,类型转型操作符带有两种不同形式。条件形式(conditional form `as?` 返回一个你试图下转成的类型的可选值optional value。强制形式 `as!` 把试图向下转型和强制解包force-unwraps结果作为一个混合动作。
当你不确定向下转型可以成功时,用类型转换的可选形式(`as?`)。可选形式的类型转换总是返回一个可选值optional value并且若下转是不可能的可选值将是 `nil` 。这使你能够检查向下转型是否成功。 当你不确定向下转型可以成功时,用类型转换的条件形式(`as?`)。条件形式的类型转换总是返回一个可选值optional value并且若下转是不可能的可选值将是 `nil`。这使你能够检查向下转型是否成功。
只有你可以确定向下转型一定会成功时,才使用强制形式。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。 只有你可以确定向下转型一定会成功时,才使用强制形式(`as!`)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。
下面的例子,迭代了`library`里的每一个 `MediaItem` ,并打印出适当的描述。要这样做,`item`需要真正作为`Movie``Song`的类型来使用不仅仅是作为 `MediaItem`。为了能够使用`Movie``Song``director``artist`属性,这是必要的。 下面的例子,迭代了 `library` 里的每一个 `MediaItem`,并打印出适当的描述。要这样做,`item` 需要真正作为 `Movie``Song` 的类型来使用不仅仅是作为 `MediaItem`。为了能够在描述中使用 `Movie``Song` `director``artist` 属性,这是必要的。
在这个示例中,数组中的每一个`item`可能是 `Movie``Song` 事前你不知道每个`item`的真实类型,所以这里使用可选形式的类型转换 `as?`去检查循环里的每次下转。 在这个示例中,数组中的每一个 `item` 可能是 `Movie``Song`。事前你不知道每个 `item` 的真实类型,所以这里使用条件形式的类型转换(`as?`)去检查循环里的每次下转。
```swift ```swift
for item in library { for item in library {
if let movie = item as? Movie { if let movie = item as? Movie {
println("Movie: '\(movie.name)', dir. \(movie.director)") print("Movie: '\(movie.name)', dir. \(movie.director)")
} else if let song = item as? Song { } else if let song = item as? Song {
println("Song: '\(song.name)', by \(song.artist)") print("Song: '\(song.name)', by \(song.artist)")
} }
} }
@ -131,18 +131,18 @@ for item in library {
``` ```
示例首先试图将 `item` 下转为 `Movie`。因为 `item` 是一个 `MediaItem` 示例首先试图将 `item` 下转为 `Movie`。因为 `item` 是一个 `MediaItem`
类型的实例,它可能是一个`Movie`;同样,它可能是一个 `Song`,或者仅仅是基类 类型的实例,它可能是一个 `Movie`;同样,它可能是一个 `Song`,或者仅仅是基类
`MediaItem`。因为不确定,`as?`形式在试图下转时将返一个可选值。 `item as Movie` 的返回值是`Movie?`类型或 “optional `Movie`”。 `MediaItem`。因为不确定,`as?`形式在试图下转时将返一个可选值。`item as? Movie` 的返回值是 `Movie?` 或 “可选 `Movie`类型
当向下转型为 `Movie` 应用在两个 `Song` 当向下转型为 `Movie` 应用在两个 `Song`
实例时将会失败。为了处理这种情况上面的例子使用了可选绑定optional binding来检查可选 `Movie`真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“`if let movie = item as? Movie`”,可以这样解读: 实例时将会失败。为了处理这种情况上面的例子使用了可选绑定optional binding来检查可选 `Movie` 真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“`if let movie = item as? Movie`”,可以这样解读:
“尝试将 `item` 转为 `Movie`类型。若成功,设置一个新的临时常量 `movie` 来存储返回的可选`Movie` “尝试将 `item` 转为 `Movie` 类型。若成功,设置一个新的临时常量 `movie` 来存储返回的可选 `Movie`
若向下转型成功,然后`movie`的属性将用于打印一个`Movie`实例的描述,包括它的导演的名字`director``Song`被找到时,一个相近的原理被用来检测 `Song` 实例和打印它的描述 若向下转型成功,然后 `movie` 的属性将用于打印一个 `Movie` 实例的描述,包括它的导演的名字 `director` 。相近的原理被用来检测 `Song` 实例,当 `Song` 被找到时则打印它的描述(包含 `artist` 的名字)
> 注意: > 注意:
转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。 > 转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。
<a name="type_casting_for_any_and_anyobject"></a> <a name="type_casting_for_any_and_anyobject"></a>
## `Any`和`AnyObject`的类型转换 ## `Any`和`AnyObject`的类型转换
@ -152,12 +152,12 @@ Swift为不确定类型提供了两种特殊类型别名
* `AnyObject`可以代表任何class类型的实例。 * `AnyObject`可以代表任何class类型的实例。
* `Any`可以表示任何类型包括方法类型function types * `Any`可以表示任何类型包括方法类型function types
> 注意: > 注意:
只有当你明确的需要它的行为和功能时才使用`Any``AnyObject`。在你的代码里使用你期望的明确的类型总是更好的。 > 只有当你明确的需要它的行为和功能时才使用`Any``AnyObject`。在你的代码里使用你期望的明确的类型总是更好的。
### `AnyObject`类型 ### `AnyObject`类型
需要在工作中使用 Cocoa APIs一般接收一个`[AnyObject]`类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以确定包含在仅从你知道的 API 信息提供的这样一个数组中对象的类型。 当在工作中使用 Cocoa APIs我们一般接收一个`[AnyObject]`类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以 API 提供的信息中清晰地确定数组中对象的类型。
在这些情况下,你可以使用强制形式的类型转换(`as`)来下转在数组中的每一项到比 `AnyObject` 更明确的类型不需要可选解析optional unwrapping 在这些情况下,你可以使用强制形式的类型转换(`as`)来下转在数组中的每一项到比 `AnyObject` 更明确的类型不需要可选解析optional unwrapping
@ -171,23 +171,23 @@ let someObjects: [AnyObject] = [
] ]
``` ```
因为知道这个数组只包含 `Movie` 实例,你可以直接用(`as`)下转并解包到不可选的`Movie`类型ps其实就是我们常用的正常类型这里是为了和可选类型相对比 因为知道这个数组只包含 `Movie` 实例,你可以直接用(`as!`)下转并解包到不可选的`Movie`类型
```swift ```swift
for object in someObjects { for object in someObjects {
let movie = object as Movie let movie = object as! Movie
println("Movie: '\(movie.name)', dir. \(movie.director)") print("Movie: '\(movie.name)', dir. \(movie.director)")
} }
// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick // Movie: '2001: A Space Odyssey', dir. Stanley Kubrick
// Movie: 'Moon', dir. Duncan Jones // Movie: 'Moon', dir. Duncan Jones
// Movie: 'Alien', dir. Ridley Scott // Movie: 'Alien', dir. Ridley Scott
``` ```
为了变为一个更短的形式,下转`someObjects`数组为`[Movie]`类型来代替下转每一项方式。 为了变为一个更短的形式,下转`someObjects`数组为`[Movie]`类型来代替下转数组中每一项方式。
```swift ```swift
for movie in someObjects as! [Movie] { for movie in someObjects as! [Movie] {
println("Movie: '\(movie.name)', dir. \(movie.director)") print("Movie: '\(movie.name)', dir. \(movie.director)")
} }
// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick // Movie: '2001: A Space Odyssey', dir. Stanley Kubrick
// Movie: 'Moon', dir. Duncan Jones // Movie: 'Moon', dir. Duncan Jones
@ -196,7 +196,7 @@ for movie in someObjects as! [Movie] {
### `Any`类型 ### `Any`类型
这里有个示例,使用 `Any` 类型来和混合的不同类型一起工作,包括`class`类型。它创建了一个可以存储`Any`类型的数组 `things` 这里有个示例,使用 `Any` 类型来和混合的不同类型一起工作,包括方法类型和非 `class` 类型。它创建了一个可以存储`Any`类型的数组 `things`
```swift ```swift
var things = [Any]() var things = [Any]()
@ -211,33 +211,33 @@ things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" }) things.append({ (name: String) -> String in "Hello, \(name)" })
``` ```
`things` 数组包含两个 `Int`2个 `Double`1个 `String` 值,一个元组 `(Double, Double)` Ivan Reitman 导演的电影“Ghostbusters”。 `things` 数组包含两个 `Int`2个 `Double`1个 `String` 值,一个元组 `(Double, Double)` 电影“Ghostbusters”,和一个获取 `String` 值并返回另一个 `String` 值的闭包表达式
你可以在 `switch` `cases`里用`is``as` 操作符来发觉只知道是 `Any``AnyObject`的常量或变量的类型。 下面的示例迭代 `things`数组中的每一项的并用`switch`语句查找每一项的类型。这几种`switch`语句的情形绑定它们匹配的值到一个规定类型的常量,让它们可以打印它们的值 你可以在 `switch` 表达式的cases中使用 `is``as` 操作符来发觉只知道是 `Any``AnyObject` 的常量或变量的类型。下面的示例迭代 `things` 数组中的每一项的并用`switch`语句查找每一项的类型。这几种 `switch` 语句的情形绑定它们匹配的值到一个规定类型的常量,让它们的值可以打印:
```swift ```swift
for thing in things { for thing in things {
switch thing { switch thing {
case 0 as Int: case 0 as Int:
println("zero as an Int") print("zero as an Int")
case 0 as Double: case 0 as Double:
println("zero as a Double") print("zero as a Double")
case let someInt as Int: case let someInt as Int:
println("an integer value of \(someInt)") print("an integer value of \(someInt)")
case let someDouble as Double where someDouble > 0: case let someDouble as Double where someDouble > 0:
println("a positive double value of \(someDouble)") print("a positive double value of \(someDouble)")
case is Double: case is Double:
println("some other double value that I don't want to print") print("some other double value that I don't want to print")
case let someString as String: case let someString as String:
println("a string value of \"\(someString)\"") print("a string value of \"\(someString)\"")
case let (x, y) as (Double, Double): case let (x, y) as (Double, Double):
println("an (x, y) point at \(x), \(y)") print("an (x, y) point at \(x), \(y)")
case let movie as Movie: case let movie as Movie:
println("a movie called '\(movie.name)', dir. \(movie.director)") print("a movie called '\(movie.name)', dir. \(movie.director)")
case let stringConverter as String -> String: case let stringConverter as String -> String:
println(stringConverter("Michael")) print(stringConverter("Michael"))
default: default:
println("something else") print("something else")
} }
} }
@ -252,5 +252,5 @@ for thing in things {
``` ```
> 注意: > 注意:
在一个switch语句的case中使用强制形式的类型转换操作符as, 而不是 as?)来检查和转换到一个明确的类型。在 switch case 语句的内容中这种检查总是安全的。 > 在一个switch语句的case中使用强制形式的类型转换操作符as, 而不是 as?)来检查和转换到一个明确的类型。在 `switch` case 语句的内容中这种检查总是安全的。

View File

@ -1,5 +1,5 @@
> 翻译:[takalard](https://github.com/takalard) > 翻译:[takalard](https://github.com/takalard) [SergioChan](https://github.com/SergioChan)
> 校对:[lifedim](https://github.com/lifedim) > 校对:[lifedim](https://github.com/lifedim)
# 泛型 # 泛型
@ -13,6 +13,7 @@
- [类型参数](#type_parameters) - [类型参数](#type_parameters)
- [命名类型参数](#naming_type_parameters) - [命名类型参数](#naming_type_parameters)
- [泛型类型](#generic_types) - [泛型类型](#generic_types)
- [扩展一个泛型类型](#extending_a_generic_type)
- [类型约束](#type_constraints) - [类型约束](#type_constraints)
- [关联类型](#associated_types) - [关联类型](#associated_types)
- [`Where`语句](#where_clauses) - [`Where`语句](#where_clauses)
@ -27,7 +28,7 @@
这里是一个标准的,非泛型函数`swapTwoInts`,用来交换两个Int值 这里是一个标准的,非泛型函数`swapTwoInts`,用来交换两个Int值
```swift ```swift
func swapTwoInts(inout a: Int, inout b: Int) { func swapTwoInts(inout a: Int, inout _ b: Int) {
let temporaryA = a let temporaryA = a
a = b a = b
b = temporaryA b = temporaryA
@ -36,34 +37,34 @@ func swapTwoInts(inout a: Int, inout b: Int) {
这个函数使用写入读出in-out参数来交换`a``b`的值,请参考[写入读出参数](../chapter2/06_Functions.html)。 这个函数使用写入读出in-out参数来交换`a``b`的值,请参考[写入读出参数](../chapter2/06_Functions.html)。
`swapTwoInts`函数可以交换`b`的原始值到`a`也可以交换a的原始值到`b`,你可以调用这个函数交换两个`Int`变量值: `swapTwoInts(_:_:)`函数可以交换`b`的原始值到`a`也可以交换a的原始值到`b`,你可以调用这个函数交换两个`Int`变量值:
```swift ```swift
var someInt = 3 var someInt = 3
var anotherInt = 107 var anotherInt = 107
swapTwoInts(&someInt, &anotherInt) swapTwoInts(&someInt, &anotherInt)
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)") print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// 输出 "someInt is now 107, and anotherInt is now 3" // 输出 "someInt is now 107, and anotherInt is now 3"
``` ```
`swapTwoInts`函数是非常有用的,但是它只能交换`Int`值,如果你想要交换两个`String`或者`Double`,就不得不写更多的函数,如 `swapTwoStrings``swapTwoDoublesfunctions `,如同如下所示: `swapTwoInts(_:_:)`函数是非常有用的,但是它只能交换`Int`值,如果你想要交换两个`String`或者`Double`,就不得不写更多的函数,如 `swapTwoStrings``swapTwoDoubles(_:_:)`,如同如下所示:
```swift ```swift
func swapTwoStrings(inout a: String, inout b: String) { func swapTwoStrings(inout a: String, inout _ b: String) {
let temporaryA = a let temporaryA = a
a = b a = b
b = temporaryA b = temporaryA
} }
func swapTwoDoubles(inout a: Double, inout b: Double) { func swapTwoDoubles(inout a: Double, inout _ b: Double) {
let temporaryA = a let temporaryA = a
a = b a = b
b = temporaryA b = temporaryA
} }
``` ```
你可能注意到 `swapTwoInts``swapTwoStrings``swapTwoDoubles`函数功能都是相同的,唯一不同之处就在于传入的变量类型不同,分别是`Int``String``Double` 你可能注意到 `swapTwoInts``swapTwoStrings``swapTwoDoubles(_:_:)`函数功能都是相同的,唯一不同之处就在于传入的变量类型不同,分别是`Int``String``Double`
但实际应用中通常需要一个用处更强大并且尽可能的考虑到更多的灵活性单个函数,可以用来交换两个任何类型值,很幸运的是,泛型代码帮你解决了这种问题。(一个这种泛型函数后面已经定义好了。) 但实际应用中通常需要一个用处更强大并且尽可能的考虑到更多的灵活性单个函数,可以用来交换两个任何类型值,很幸运的是,泛型代码帮你解决了这种问题。(一个这种泛型函数后面已经定义好了。)
@ -73,29 +74,29 @@ func swapTwoDoubles(inout a: Double, inout b: Double) {
<a name="generic_functions"></a> <a name="generic_functions"></a>
## 泛型函数 ## 泛型函数
`泛型函数`可以工作于任何类型,这里是一个上面`swapTwoInts`函数的泛型版本,用于交换两个值: `泛型函数`可以工作于任何类型,这里是一个上面`swapTwoInts(_:_:)`函数的泛型版本,用于交换两个值:
```swift ```swift
func swapTwoValues<T>(inout a: T, inout b: T) { func swapTwoValues<T>(inout a: T, inout _ b: T) {
let temporaryA = a let temporaryA = a
a = b a = b
b = temporaryA b = temporaryA
} }
``` ```
`swapTwoValues`函数主体和`swapTwoInts`函数是一样的,它只在第一行稍微有那么一点点不同于`swapTwoInts`,如下所示: `swapTwoValues(_:_:)`函数主体和`swapTwoInts(_:_:)`函数是一样的,它只在第一行稍微有那么一点点不同于`swapTwoInts`,如下所示:
```swift ```swift
func swapTwoInts(inout a: Int, inout b: Int) func swapTwoInts(inout a: Int, inout _ b: Int)
func swapTwoValues<T>(inout a: T, inout b: T) func swapTwoValues<T>(inout a: T, inout _ b: T)
``` ```
这个函数的泛型版本使用了占位类型名字(通常此情况下用字母`T`来表示)来代替实际类型名(如`Int``String``Double`)。占位类型名没有提示`T`必须是什么类型,但是它提示了`a``b`必须是同一类型`T`,而不管`T`表示什么类型。只有`swapTwoValues`函数在每次调用时所传入的实际类型才能决定`T`所代表的类型。 这个函数的泛型版本使用了占位类型名字(通常此情况下用字母`T`来表示)来代替实际类型名(如`Int``String``Double`)。占位类型名没有提示`T`必须是什么类型,但是它提示了`a``b`必须是同一类型`T`,而不管`T`表示什么类型。只有`swapTwoValues(_:_:)`函数在每次调用时所传入的实际类型才能决定`T`所代表的类型。
另外一个不同之处在于这个泛型函数名后面跟着的占位类型名字T是用尖括号括起来的`<T>`)。这个尖括号告诉 Swift 那个`T``swapTwoValues`函数所定义的一个类型。因为`T`是一个占位命名类型Swift 不会去查找命名为T的实际类型。 另外一个不同之处在于这个泛型函数名后面跟着的占位类型名字T是用尖括号括起来的`<T>`)。这个尖括号告诉 Swift 那个`T``swapTwoValues(_:_:)`函数所定义的一个类型。因为`T`是一个占位命名类型Swift 不会去查找命名为T的实际类型。
`swapTwoValues`函数除了要求传入的两个任何类型值是同一类型外,也可以作为`swapTwoInts`函数被调用。每次`swapTwoValues`被调用T所代表的类型值都会传给函数。 `swapTwoValues(_:_:)`函数除了要求传入的两个任何类型值是同一类型外,也可以作为`swapTwoInts`函数被调用。每次`swapTwoValues`被调用T所代表的类型值都会传给函数。
在下面的两个例子中,`T`分别代表`Int``String` 在下面的两个例子中,`T`分别代表`Int``String`
@ -103,26 +104,26 @@ func swapTwoValues<T>(inout a: T, inout b: T)
var someInt = 3 var someInt = 3
var anotherInt = 107 var anotherInt = 107
swapTwoValues(&someInt, &anotherInt) swapTwoValues(&someInt, &anotherInt)
// someInt is now 107, and anotherInt is now 3 // someInt 现在等于 107, anotherInt 现在等于 3
``` ```
```swift ```swift
var someString = "hello" var someString = "hello"
var anotherString = "world" var anotherString = "world"
swapTwoValues(&someString, &anotherString) swapTwoValues(&someString, &anotherString)
// someString is now "world", and anotherString is now "hello" // someString 现在等于 "world", anotherString 现在等于 "hello"
``` ```
>注意 >注意
上面定义的函数`swapTwoValues`是受`swap`函数启发而实现的。`swap`函数存在于 Swift 标准库,并可以在其它类中任意使用。如果你在自己代码中需要类似`swapTwoValues`函数的功能,你可以使用已存在的交换函数`swap`函数。 上面定义的函数`swapTwoValues(_:_:)`是受`swap`函数启发而实现的。`swap`函数存在于 Swift 标准库,并可以在其它类中任意使用。如果你在自己代码中需要类似`swapTwoValues(_:_:)`函数的功能,你可以使用已存在的交换函数`swap(_:_:)`函数。
<a name="type_parameters"></a> <a name="type_parameters"></a>
## 类型参数 ## 类型参数
在上面的`swapTwoValues`例子中,占位类型`T`是一种类型参数的示例。类型参数指定并命名为一个占位类型,并且紧随在函数名后面,使用一对尖括号括起来(如`<T>`)。 在上面的`swapTwoValues`例子中,占位类型`T`是一种类型参数的示例。类型参数指定并命名为一个占位类型,并且紧随在函数名后面,使用一对尖括号括起来(如`<T>`)。
一旦一个类型参数被指定,那么其可以被使用来定义一个函数的参数类型(如`swapTwoValues`函数中的参数`a``b`),或作为一个函数返回类型,或用作函数主体中的注释类型。在这种情况下,被类型参数所代表的占位类型不管函数任何时候被调用,都会被实际类型所替换(在上面`swapTwoValues`例子中,当函数第一次被调用时,`T``Int`替换,第二次调用时,被`String`替换。)。 一旦一个类型参数被指定,那么其可以被使用来定义一个函数的参数类型(如`swapTwoValues(_:_:)`函数中的参数`a``b`),或作为一个函数返回类型,或用作函数主体中的注释类型。在这种情况下,被类型参数所代表的占位类型不管函数任何时候被调用,都会被实际类型所替换(在上面`swapTwoValues`例子中,当函数第一次被调用时,`T``Int`替换,第二次调用时,被`String`替换。)。
你可支持多个类型参数,命名在尖括号中,用逗号分开。 你可支持多个类型参数,命名在尖括号中,用逗号分开。
@ -131,10 +132,10 @@ swapTwoValues(&someString, &anotherString)
在简单的情况下,泛型函数或泛型类型需要指定一个占位类型(如上面的`swapTwoValues`泛型函数,或一个存储单一类型的泛型集,如数组),通常用一单个字母`T`来命名类型参数。不过,你可以使用任何有效的标识符来作为类型参数名。 在简单的情况下,泛型函数或泛型类型需要指定一个占位类型(如上面的`swapTwoValues`泛型函数,或一个存储单一类型的泛型集,如数组),通常用一单个字母`T`来命名类型参数。不过,你可以使用任何有效的标识符来作为类型参数名。
如果你使用多个参数定义更复杂的泛型函数或泛型类型那么使用更多的描述类型参数是非常有用的。例如Swift 字典Dictionary类型有两个类型参数一个是键另外一个是值。如果你自己写字典你或许会定义这两个类型参数为`KeyType``ValueType`,用来记住它们在你的泛型代码中的作用。 如果你使用多个参数定义更复杂的泛型函数或泛型类型那么使用更多的描述类型参数是非常有用的。例如Swift 字典Dictionary类型有两个类型参数一个是键另外一个是值。如果你自己写字典你或许会定义这两个类型参数为`Key``Value`,用来记住它们在你的泛型代码中的作用。
>注意 >注意
请始终使用大写字母开头的驼峰式命名法(例如`T``KeyType`)来给类型参数命名,以表明它们是类型的占位符,而非类型值。 请始终使用大写字母开头的驼峰式命名法(例如`T``Key`)来给类型参数命名,以表明它们是类型的占位符,而非类型值。
<a name="generic_types"></a> <a name="generic_types"></a>
## 泛型类型 ## 泛型类型
@ -145,7 +146,7 @@ swapTwoValues(&someString, &anotherString)
这部分向你展示如何写一个泛型集类型--`Stack`(栈)。一个栈是一系列值域的集合,和`Array`(数组)类似,但其是一个比 Swift 的`Array`类型更多限制的集合。一个数组可以允许其里面任何位置的插入/删除操作,而栈,只允许在集合的末端添加新的项(如同*push*一个新值进栈)。同样的一个栈也只能从末端移除项(如同*pop*一个值出栈)。 这部分向你展示如何写一个泛型集类型--`Stack`(栈)。一个栈是一系列值域的集合,和`Array`(数组)类似,但其是一个比 Swift 的`Array`类型更多限制的集合。一个数组可以允许其里面任何位置的插入/删除操作,而栈,只允许在集合的末端添加新的项(如同*push*一个新值进栈)。同样的一个栈也只能从末端移除项(如同*pop*一个值出栈)。
>注意 >注意
栈的概念已被`UINavigationController`类使用来模拟试图控制器的导航结构。你通过调用`UINavigationController``pushViewController:animated:`方法来为导航栈添加add新的试图控制器而通过`popViewControllerAnimated:`的方法来从导航栈中移除pop某个试图控制器。每当你需要一个严格的`后进先出`方式来管理集合,堆栈都是最实用的模型。 栈的概念已被`UINavigationController`类使用来模拟试图控制器的导航结构。你通过调用`UINavigationController``pushViewController(_:animated:)`方法来为导航栈添加add新的试图控制器而通过`popViewControllerAnimated(_:)`的方法来从导航栈中移除pop某个试图控制器。每当你需要一个严格的`后进先出`方式来管理集合,堆栈都是最实用的模型。
下图展示了一个栈的压栈(push)/出栈(pop)的行为: 下图展示了一个栈的压栈(push)/出栈(pop)的行为:
@ -161,7 +162,7 @@ swapTwoValues(&someString, &anotherString)
```swift ```swift
struct IntStack { struct IntStack {
var items = Int[]() var items = [Int]()
mutating func push(item: Int) { mutating func push(item: Int) {
items.append(item) items.append(item)
} }
@ -196,10 +197,12 @@ struct Stack<T> {
`T`定义了一个名为“某种类型T”的节点提供给后来用。这种将来类型可以在结构体的定义里任何地方表示为“T”。在这种情况下`T`在如下三个地方被用作节点: `T`定义了一个名为“某种类型T”的节点提供给后来用。这种将来类型可以在结构体的定义里任何地方表示为“T”。在这种情况下`T`在如下三个地方被用作节点:
- 创建一个名为`items`的属性使用空的T类型值数组对其进行初始化 - 创建一个名为`items`的属性使用空的T类型值数组对其进行初始化
- 指定一个包含一个参数名为`item``push`方法该参数必须是T类型 - 指定一个包含一个参数名为`item``push(_:)`方法该参数必须是T类型
- 指定一个`pop`方法的返回值该返回值将是一个T类型值。 - 指定一个`pop`方法的返回值该返回值将是一个T类型值。
当创建一个新单例并初始化时, 通过用一对紧随在类型名后的尖括号里写出实际指定栈用到类型,创建一个`Stack`实例,同创建`Array``Dictionary`一样: 由于`Stack`是泛型类型,所以在 Swift 中其可以用来创建任何有效类型的栈,这种方式如同`Array``Dictionary`
你可以通过在尖括号里写出栈中需要存储的数据类型来创建并初始化一个`Stack`实例。比如,要创建一个`strings`的栈,你可以写成`Stack<String>()`
```swift ```swift
var stackOfStrings = Stack<String>() var stackOfStrings = Stack<String>()
@ -218,18 +221,44 @@ stackOfStrings.push("cuatro")
```swift ```swift
let fromTheTop = stackOfStrings.pop() let fromTheTop = stackOfStrings.pop()
// fromTheTop is equal to "cuatro", and the stack now contains 3 strings // fromTheTop 等于 "cuatro", 现在栈中还有3个string
``` ```
下图展示了如何从栈中pop一个值的过程 下图展示了如何从栈中pop一个值的过程
![此处输入图片的描述](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/stackPoppedOneString_2x.png) ![此处输入图片的描述](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/stackPoppedOneString_2x.png)
由于`Stack`是泛型类型,所以在 Swift 中其可以用来创建任何有效类型的栈,这种方式如同`Array``Dictionary` <a name="extending_a_generic_type"></a>
## 扩展一个泛型类型
当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。更加方便的是,原始类型定义中声明的类型参数列表在扩展里是可以使用的,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。
下面的例子扩展了泛型`Stack`类型,为其添加了一个名为`topItem`的只读计算属性,它将会返回当前栈顶端的元素而不会将其从栈中移除。
```swift
extension Stack {
var topItem: T? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
```
`topItem`属性会返回一个`T`类型的可选值。当栈为空的时候,`topItem`将会返回`nil`;当栈不为空的时候,`topItem`会返回`items`数组中的最后一个元素。
注意这里的扩展并没有定义一个类型参数列表。相反的,`Stack`类型已有的类型参数名称,`T`,被用在扩展中当做`topItem`计算属性的可选类型。
`topItem`计算属性现在可以被用来返回任意`Stack`实例的顶端元素而无需移除它:
```swift
if let topItem = stackOfStrings.topItem {
print("The top item on the stack is \(topItem).")
}
// 输出 "The top item on the stack is tres."
```
<a name="type_constraints"></a> <a name="type_constraints"></a>
##类型约束 ##类型约束
`swapTwoValues`函数和`Stack`类型可以作用于任何类型,不过,有的时候对使用在泛型函数和泛型类型上的类型强制约束为某种特定类型是非常有用的。类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。 `swapTwoValues(_:_:)`函数和`Stack`类型可以作用于任何类型,不过,有的时候对使用在泛型函数和泛型类型上的类型强制约束为某种特定类型是非常有用的。类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。
例如Swift 的`Dictionary`类型对作用于其键的类型做了些限制。在[字典](../chapter2/04_Collection_Types.html)的描述中,字典的键类型必须是*可哈希*,也就是说,必须有一种方法可以使其被唯一的表示。`Dictionary`之所以需要其键是可哈希是为了以便于其检查其是否已经包含某个特定键的值。如无此需求,`Dictionary`既不会告诉是否插入或者替换了某个特定键的值,也不能查找到已经存储在字典里面的给定键值。 例如Swift 的`Dictionary`类型对作用于其键的类型做了些限制。在[字典](../chapter2/04_Collection_Types.html)的描述中,字典的键类型必须是*可哈希*,也就是说,必须有一种方法可以使其被唯一的表示。`Dictionary`之所以需要其键是可哈希是为了以便于其检查其是否已经包含某个特定键的值。如无此需求,`Dictionary`既不会告诉是否插入或者替换了某个特定键的值,也不能查找到已经存储在字典里面的给定键值。
@ -243,7 +272,7 @@ let fromTheTop = stackOfStrings.pop()
```swift ```swift
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here // 这里是函数主体
} }
``` ```
@ -251,11 +280,11 @@ func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
### 类型约束行为 ### 类型约束行为
这里有个名为`findStringIndex`的非泛型函数,该函数功能是去查找包含一给定`String`值的数组。若查找到匹配的字符串,`findStringIndex`函数返回该字符串在数组中的索引值(`Int`),反之则返回`nil` 这里有个名为`findStringIndex`的非泛型函数,该函数功能是去查找包含一给定`String`值的数组。若查找到匹配的字符串,`findStringIndex(_:_:)`函数返回该字符串在数组中的索引值(`Int`),反之则返回`nil`
```swift ```swift
func findStringIndex(array: [String], valueToFind: String) -> Int? { func findStringIndex(array: [String], _ valueToFind: String) -> Int? {
for (index, value) in enumerate(array) { for (index, value) in array.enumerate() {
if value == valueToFind { if value == valueToFind {
return index return index
} }
@ -265,12 +294,12 @@ func findStringIndex(array: [String], valueToFind: String) -> Int? {
``` ```
`findStringIndex`函数可以作用于查找一字符串数组中的某个字符串: `findStringIndex(_:_:)`函数可以作用于查找一字符串数组中的某个字符串:
```swift ```swift
let strings = ["cat", "dog", "llama", "parakeet", "terrapin"] let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
if let foundIndex = findStringIndex(strings, "llama") { if let foundIndex = findStringIndex(strings, "llama") {
println("The index of llama is \(foundIndex)") print("The index of llama is \(foundIndex)")
} }
// 输出 "The index of llama is 2" // 输出 "The index of llama is 2"
``` ```
@ -280,8 +309,8 @@ if let foundIndex = findStringIndex(strings, "llama") {
这里展示如何写一个你或许期望的`findStringIndex`的泛型版本`findIndex`。请注意这个函数仍然返回`Int`,是不是有点迷惑呢,而不是泛型类型?那是因为函数返回的是一个可选的索引数,而不是从数组中得到的一个可选值。需要提醒的是,这个函数不会编译,原因在例子后面会说明: 这里展示如何写一个你或许期望的`findStringIndex`的泛型版本`findIndex`。请注意这个函数仍然返回`Int`,是不是有点迷惑呢,而不是泛型类型?那是因为函数返回的是一个可选的索引数,而不是从数组中得到的一个可选值。需要提醒的是,这个函数不会编译,原因在例子后面会说明:
```swift ```swift
func findIndex<T>(array: [T], valueToFind: T) -> Int? { func findIndex<T>(array: [T], _ valueToFind: T) -> Int? {
for (index, value) in enumerate(array) { for (index, value) in array.enumerate() {
if value == valueToFind { if value == valueToFind {
return index return index
} }
@ -294,11 +323,11 @@ func findIndex<T>(array: [T], valueToFind: T) -> Int? {
不过所有的这些并不会让我们无从下手。Swift 标准库中定义了一个`Equatable`协议,该协议要求任何遵循的类型实现等式符(==)和不等符(!=)对任何两个该类型进行比较。所有的 Swift 标准类型自动支持`Equatable`协议。 不过所有的这些并不会让我们无从下手。Swift 标准库中定义了一个`Equatable`协议,该协议要求任何遵循的类型实现等式符(==)和不等符(!=)对任何两个该类型进行比较。所有的 Swift 标准类型自动支持`Equatable`协议。
任何`Equatable`类型都可以安全的使用在`findIndex`函数中,因为其保证支持等式操作。为了说明这个事实,当你定义一个函数时,你可以写一个`Equatable`类型约束作为类型参数定义的一部分: 任何`Equatable`类型都可以安全的使用在`findIndex(_:_:)`函数中,因为其保证支持等式操作。为了说明这个事实,当你定义一个函数时,你可以写一个`Equatable`类型约束作为类型参数定义的一部分:
```swift ```swift
func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? { func findIndex<T: Equatable>(array: [T], _ valueToFind: T) -> Int? {
for (index, value) in enumerate(array) { for (index, value) in array.enumerate() {
if value == valueToFind { if value == valueToFind {
return index return index
} }
@ -310,7 +339,7 @@ func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
`findIndex`中这个单个类型参数写做:`T: Equatable`也就意味着“任何T类型都遵循`Equatable`协议”。 `findIndex`中这个单个类型参数写做:`T: Equatable`也就意味着“任何T类型都遵循`Equatable`协议”。
`findIndex`函数现在则可以成功的编译过,并且作用于任何遵循`Equatable`的类型,如`Double``String`: `findIndex(_:_:)`函数现在则可以成功的编译过,并且作用于任何遵循`Equatable`的类型,如`Double``String`:
```swift ```swift
let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3) let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)
@ -326,7 +355,7 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
### 关联类型行为 ### 关联类型行为
这里是一个`Container`协议的例子定义了一个ItemType关联类型 这里是一个`Container`协议的例子,定义了一个`ItemType`关联类型:
```swift ```swift
protocol Container { protocol Container {
@ -339,19 +368,19 @@ protocol Container {
`Container`协议定义了三个任何容器必须支持的兼容要求: `Container`协议定义了三个任何容器必须支持的兼容要求:
- 必须可以通过`append`方法添加一个新item到容器里; - 必须可以通过`append(_:)`方法添加一个新元素到容器里;
- 必须可以通过使用`count`属性获取容器里items的数量,并返回一个`Int`值; - 必须可以通过使用`count`属性获取容器里元素的数量,并返回一个`Int`值;
- 必须可以通过容器的`Int`索引值下标可以检索到每一个item - 必须可以通过容器的`Int`索引值下标可以检索到每一个元素
这个协议没有指定容器里item是如何存储的或何种类型是允许的。这个协议只指定三个任何遵循`Container`类型所必须支持的功能点。一个遵循的类型在满足这三个条件的情况下也可以提供其他额外的功能。 这个协议没有指定容器里的元素是如何存储的或何种类型是允许的。这个协议只指定三个任何遵循`Container`类型所必须支持的功能点。一个遵循的类型在满足这三个条件的情况下也可以提供其他额外的功能。
任何遵循`Container`协议的类型必须指定存储在其里面的值类型,必须保证只有正确类型的items可以加进容器里,必须明确可以通过其下标返回item类型。 任何遵循`Container`协议的类型必须指定存储在其里面的值类型,必须保证只有正确类型的元素可以加进容器里,必须明确可以通过其下标返回元素类型。
为了定义这三个条件,`Container`协议需要一个方法指定容器里的元素将会保留,而不需要知道特定容器的类型。`Container`协议需要指定任何通过`append`方法添加到容器里的值和容器里元素是相同类型,并且通过容器下标返回的容器元素类型的值的类型是相同类型。 为了定义这三个条件,`Container`协议需要一个方法指定容器里的元素将会保留,而不需要知道特定容器的类型。`Container`协议需要指定任何通过`append(_:)`方法添加到容器里的值和容器里元素是相同类型,并且通过容器下标返回的容器元素类型的值的类型是相同类型。
为了达到此目的,`Container`协议声明了一个ItemType的关联类型写作`typealias ItemType`。这个协议不会定义`ItemType`是什么的别名,这个信息将由任何遵循协议的类型来提供。尽管如此,`ItemType`别名提供了一种识别Container中Items类型的方法,并且用于`append`方法和`subscript`方法的类型定义,以便保证任何`Container`期望的行为能够被执行。 为了达到此目的,`Container`协议声明了一个`ItemType`的关联类型,写作`typealias ItemType`。这个协议不会定义`ItemType`是什么的别名,这个信息将由任何遵循协议的类型来提供。尽管如此,`ItemType`别名提供了一种识别`Container`中元素类型的方法,并且用于`append(_:)`方法和`subscript`方法的类型定义,以便保证任何`Container`期望的行为能够被执行。
这里是一个早前IntStack类型的非泛型版本遵循Container协议 这里是一个早前`IntStack`类型的非泛型版本,遵循`Container`协议:
```swift ```swift
struct IntStack: Container { struct IntStack: Container {
@ -369,7 +398,7 @@ struct IntStack: Container {
self.push(item) self.push(item)
} }
var count: Int { var count: Int {
return items.count return items.count
} }
subscript(i: Int) -> Int { subscript(i: Int) -> Int {
return items[i] return items[i]
@ -380,9 +409,9 @@ struct IntStack: Container {
`IntStack`类型实现了`Container`协议的所有三个要求,在`IntStack`类型的每个包含部分的功能都满足这些要求。 `IntStack`类型实现了`Container`协议的所有三个要求,在`IntStack`类型的每个包含部分的功能都满足这些要求。
此外,`IntStack`指定了`Container`的实现适用的ItemType被用作`Int`类型。对于这个`Container`协议实现而言,定义 `typealias ItemType = Int`,将抽象的`ItemType`类型转换为具体的`Int`类型。 此外,`IntStack`指定了`Container`的实现,适用的`ItemType`被用作`Int`类型。对于这个`Container`协议实现而言,定义 `typealias ItemType = Int`,将抽象的`ItemType`类型转换为具体的`Int`类型。
感谢Swift类型参考你不用在`IntStack`定义部分声明一个具体的`Int``ItemType`。由于`IntStack`遵循`Container`协议的所有要求,只要通过简单的查找`append`方法的item参数类型和下标返回的类型Swift就可以推断出合适的`ItemType`来使用。确实,如果上面的代码中你删除了 `typealias ItemType = Int`这一行一切仍旧可以工作因为它清楚的知道ItemType使用的是何种类型。 感谢Swift类型参考你不用在`IntStack`定义部分声明一个具体的`Int``ItemType`。由于`IntStack`遵循`Container`协议的所有要求,只要通过简单的查找`append(_:)`方法的`item`参数类型和下标返回的类型Swift就可以推断出合适的`ItemType`来使用。确实,如果上面的代码中你删除了 `typealias ItemType = Int`这一行,一切仍旧可以工作,因为它清楚的知道`ItemType`使用的是何种类型。
你也可以生成遵循`Container`协议的泛型`Stack`类型: 你也可以生成遵循`Container`协议的泛型`Stack`类型:
@ -401,7 +430,7 @@ struct Stack<T>: Container {
self.push(item) self.push(item)
} }
var count: Int { var count: Int {
return items.count return items.count
} }
subscript(i: Int) -> T { subscript(i: Int) -> T {
return items[i] return items[i]
@ -409,20 +438,20 @@ struct Stack<T>: Container {
} }
``` ```
这个时候,占位类型参数`T`被用作`append`方法的item参数和下标的返回类型。Swift 因此可以推断出被用作这个特定容器的`ItemType``T`的合适类型。 这个时候,占位类型参数`T`被用作`append(_:)`方法的`item`参数和下标的返回类型。Swift 因此可以推断出被用作这个特定容器的`ItemType``T`的合适类型。
### 扩展一个存在的类型为一指定关联类型 ### 扩展一个存在的类型为一指定关联类型
在[使用扩展来添加协议兼容性](../chapter2/21_Protocols.html)中有描述扩展一个存在的类型添加遵循一个协议。这个类型包含一个关联类型的协议。 在[使用扩展来添加协议兼容性](../chapter2/21_Protocols.html)中有描述扩展一个存在的类型添加遵循一个协议。这个类型包含一个关联类型的协议。
Swift的`Array`已经提供`append`方法,一个`count`属性和通过下标来查找一个自己的元素。这三个功能都达到`Container`协议的要求。也就意味着你可以扩展`Array`去遵循`Container`协议,只要通过简单声明`Array`适用于该协议而已。如何实践这样一个空扩展,在[使用扩展来声明协议的采纳](../chapter2/21_Protocols.html)中有描述这样一个实现一个空扩展的行为: Swift的`Array`已经提供`append(_:)`方法,一个`count`属性和通过下标来查找一个自己的元素。这三个功能都达到`Container`协议的要求。也就意味着你可以扩展`Array`去遵循`Container`协议,只要通过简单声明`Array`适用于该协议而已。如何实践这样一个空扩展,在[使用扩展来声明协议的采纳](../chapter2/21_Protocols.html)中有描述这样一个实现一个空扩展的行为:
```swift ```swift
extension Array: Container {} extension Array: Container {}
``` ```
如同上面的泛型`Stack`类型一样,`Arrayappend`方法和下标保证`Swift`可以推断出`ItemType`所使用的适用的类型。定义了这个扩展后,你可以将任何`Array`当作`Container`来使用。 如同上面的泛型`Stack`类型一样,`Array``append(_:)`方法和下标保证`Swift`可以推断出`ItemType`所使用的适用的类型。定义了这个扩展后,你可以将任何`Array`当作`Container`来使用。
<a name="where_clauses"></a> <a name="where_clauses"></a>
## Where 语句 ## Where 语句
@ -480,15 +509,15 @@ func allItemsMatch<
第三个和第四个要求结合起来的意思是`anotherContainer`中的元素也可以通过 `!=` 操作来检查,因为它们在`someContainer`中元素确实是相同的类型。 第三个和第四个要求结合起来的意思是`anotherContainer`中的元素也可以通过 `!=` 操作来检查,因为它们在`someContainer`中元素确实是相同的类型。
这些要求能够使`allItemsMatch`函数比较两个容器,即便它们是不同的容器类型。 这些要求能够使`allItemsMatch(_:_:)`函数比较两个容器,即便它们是不同的容器类型。
`allItemsMatch`首先检查两个容器是否拥有同样数目的items如果它们的元素数目不同没有办法进行匹配函数就会`false` `allItemsMatch(_:_:)`首先检查两个容器是否拥有同样数目的items如果它们的元素数目不同没有办法进行匹配函数就会`false`
检查完之后,函数通过`for-in`循环和半闭区间操作(..)来迭代`someContainer`中的所有元素。对于每个元素,函数检查是否`someContainer`中的元素不等于对应的`anotherContainer`中的元素,如果这两个元素不等,则这两个容器不匹配,返回`false` 检查完之后,函数通过`for-in`循环和半闭区间操作(`..<`)来迭代`someContainer`中的所有元素。对于每个元素,函数检查是否`someContainer`中的元素不等于对应的`anotherContainer`中的元素,如果这两个元素不等,则这两个容器不匹配,返回`false`
如果循环体结束后未发现没有任何的不匹配,那表明两个容器匹配,函数返回`true` 如果循环体结束后未发现没有任何的不匹配,那表明两个容器匹配,函数返回`true`
这里演示了allItemsMatch函数运算的过程 这里演示了`allItemsMatch(_:_:)`函数运算的过程:
```swift ```swift
var stackOfStrings = Stack<String>() var stackOfStrings = Stack<String>()
@ -499,14 +528,14 @@ stackOfStrings.push("tres")
var arrayOfStrings = ["uno", "dos", "tres"] var arrayOfStrings = ["uno", "dos", "tres"]
if allItemsMatch(stackOfStrings, arrayOfStrings) { if allItemsMatch(stackOfStrings, arrayOfStrings) {
println("All items match.") print("All items match.")
} else { } else {
println("Not all items match.") print("Not all items match.")
} }
// 输出 "All items match." // 输出 "All items match."
``` ```
上面的例子创建一个`Stack`单例来存储`String`,然后压了三个字符串进栈。这个例子也创建了一个`Array`单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组是不同的类型,但它们都遵循`Container`协议,而且它们都包含同样的类型值。因此你可以调用`allItemsMatch`函数,用这两个容器作为它的参数。在上面的例子中,`allItemsMatch`函数正确的显示了所有的这两个容器的`items`匹配。 上面的例子创建一个`Stack`单例来存储`String`,然后压了三个字符串进栈。这个例子也创建了一个`Array`单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组是不同的类型,但它们都遵循`Container`协议,而且它们都包含同样的类型值。因此你可以调用`allItemsMatch(_:_:)`函数,用这两个容器作为它的参数。在上面的例子中,`allItemsMatch(_:_:)`函数正确的显示了这两个容器的所有元素都是相互匹配
[1]: ../chapter2/06_Functions.html [1]: ../chapter2/06_Functions.html
[2]: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/stackPushPop_2x.png [2]: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/stackPushPop_2x.png

View File

@ -12,6 +12,7 @@
- [默认访问级别](#default_access_levels) - [默认访问级别](#default_access_levels)
- [单目标应用程序的访问级别](#access_levels_for_single-target_apps) - [单目标应用程序的访问级别](#access_levels_for_single-target_apps)
- [Framework的访问级别](#access_levels_for_frameworks) - [Framework的访问级别](#access_levels_for_frameworks)
- [单元测试目标的访问级别](#access_levels_for_unit_test_targets)
- [访问控制语法](#access_control_syntax) - [访问控制语法](#access_control_syntax)
- [自定义类型](#custom_types) - [自定义类型](#custom_types)
- [元组类型](#tuple_types) - [元组类型](#tuple_types)
@ -33,45 +34,47 @@
- [泛型](#generics) - [泛型](#generics)
- [类型别名](#type_aliases) - [类型别名](#type_aliases)
访问控制可以限定你在源文件或模块中访问代码的级别,也就是说可以控制哪些代码你可以访问,哪些代码你不能访问。这个特性可以让我们隐藏功能实现的一些细节,并且可以明确的指定我们提供给其他人的接口中哪些部分是他们可以使用的,哪些是他们看不到的。 *访问控制*可以限定其他源文件或模块中代码对你代码的访问级别。这个特性可以让我们隐藏功能实现的一些细节,并且可以明确的申明我们提供给其他人的接口中哪些部分是他们可以访问和使用的。
你可以明确的给类、结构体、枚举设置访问级别,也可以给属性、函数、初始化方法、基本类型、下标索引等设置访问级别。协议也可以被限定在一定的范围内使用,包括协议里的全局常量、变量和函数。 你可以明确地给单个类型(类、结构体、枚举设置访问级别,也可以给这些类型的属性、函数、初始化方法、基本类型、下标索引等设置访问级别。协议也可以被限定在一定的范围内使用,包括协议里的全局常量、变量和函数。
在提供了不同访问级别的同时Swift 并没有规定我们要在任何时候都要在代码中明确指定访问级别。其实,如果我们作为独立开发者在开发我们自己的 app而不是在开发一些`Framework`的时候,我们完全可以不用明确的指定代码的访问级别。 在提供了不同访问级别的同时Swift还为某些典型场景提供了默认的访问级别,这样就不需要我们在每段代码中都申明显式访问级别。其实,如果只是开发一个单目标应用程序,我们完全可以不用明代码的显式访问级别。
> 注意:为方便起见,代码中可以设置访问级别的它们(属性、基本类型、函数等)在下面的章节中我们称之为“实体”。 > 注意:简单起见,代码中可以设置访问级别的特性(属性、基本类型、函数等)在下面的章节中我们会以“实体”代替
<a name="modules_and_source_files"></a> <a name="modules_and_source_files"></a>
## 模块和源文件 ## 模块和源文件
Swift 中的访问控制模型基于模块和源文件这两个概念。 Swift 中的访问控制模型基于模块和源文件这两个概念。
模块指的是`Framework``App bundle`。在 Swift 中可以用`import`关键字引入自己的工程。 *模块*指的是以独立单元构建和发布的`Framework``Application`。在Swift 中的一个模块可以使`import`关键字引入另外一个模块。
在 Swift 中,`Framework``App bundle`被作为模块处理。如果你是为了实现某个通用的功能,或者是为了封装一些常用方法而将代码打包成`Framework`,这个`Framework`在 Swift 中就被称为模块。不论它被引入到某个 App 工程或者其他的`Framework`,它里面的一切(属性、函数等)属于这个模块。 在 Swift 中,Xcode的每个构建目标比如`Framework``app bundle`)都被当作模块处理。如果你是为了实现某个通用的功能,或者是为了封装一些常用方法而将代码打包成独立的`Framework`,这个`Framework`在 Swift 中就被称为模块。它被引入到某个 app 工程或者另外一个`Framework`,它里面的一切(属性、函数等)仍然属于这个独立的模块。
源文件指的是 Swift 中的`Swift File`,就是编写 Swift 代码的文件,它通常属于一个模块。通常一个源文件包含`类`,在`类`中又包含`函数``属性`等类型 *源文件*指的是 Swift 中的`Swift File`,就是编写 Swift 代码的文件,它通常属于一个模块。尽管一般我们将不同的`类` 分别定义在不同的源文件中,但是同一个源文件可以包含`类``函数` 的定义
<a name="access_levels"></a> <a name="access_levels"></a>
## 访问级别 ## 访问级别
Swift 提供了三种不同的访问级别。这些访问级别相对于源文件中定义的实体,同时也相对于这些源文件所属的模块。 Swift 为代码中的实体提供了三种不同的访问级别。这些访问级别不仅与源文件中定义的实体相关,同时也源文件所属的模块相关
- `Public`:可以访问自己模块或应用中源文件里的任何实体,别人也可以访问引入该模块源文件里的所有实体。通常情况下,某个接口或`Framework`是可以被任何人使用时,你可以将其设置为`public`级别。 - `public`:可以访问自己模块中源文件里的任何实体,别人也可以通过引入该模块来访问源文件里的所有实体。通常情况下,`Framework` 中的某个接口是可以被任何人使用时,你可以将其设置为`public`级别。
- `Internal`:可以访问自己模块或应用中源文件里的任何实体,但是别人不能访问该模块中源文件里的实体。通常情况下,某个接口或`Framework`作为内部结构使用时,你可以将其设置为`internal`级别。 - `internal`:可以访问自己模块中源文件里的任何实体,但是别人不能访问该模块中源文件里的实体。通常情况下,某个接口或`Framework`作为内部结构使用时,你可以将其设置为`internal`级别。
- `Private`:只能在当前源文件中使用的实体,称为私有实体。使用`private`级别,可以用作隐藏某些功能的实现细节。 - `private`:只能在当前源文件中使用的实体,称为私有实体。使用`private`级别,可以用作隐藏某些功能的实现细节。
`Public`为最高级访问级别,`Private`为最低级访问级别。 `public`为最高级访问级别,`private`为最低级访问级别。
> 注意Swift中的`private`访问和其他语言中的`private`访问不太一样,它的范围限于整个源文件,而不是声明。这就意味着,一个`类` 可以访问定义该`类` 的源文件中定义的所有`private`实体,但是如果一个`类` 的扩展是定义在独立的源文件中,那么就不能访问这个`类` 的`private`成员。
<a name="guiding_principle_of_access_levels"></a> <a name="guiding_principle_of_access_levels"></a>
### 访问级别的使用原则 ### 访问级别的使用原则
Swift 中访问级别有如下使用原则:访问级别统一性。 Swift 中访问级别遵循一个使用原则:访问级别统一性。
比如说: 比如说:
- 一个`public`访问级别的变量,不能将它的类型定义为`internal``private`的类型。因为变量可以被任何人访问,但是定义它的类型不可以,所以这样就会出现错误。 - 一个`public`访问级别的变量,不能将它的类型定义为`internal``private`。因为变量可以被任何人访问,但是定义它的类型不可以,所以这样就会出现错误。
- 函数的访问级别不能高于它的参数、返回类型的访问级别。因为如果函数定义为`public`而参数或者返回类型定义为`internal``private`,就会出现函数可以被任何人访问,但是它的参数和返回类型不可以,同样会出现错误。 - 函数的访问级别不能高于它的参数、返回类型的访问级别。因为如果函数定义为`public`而参数或者返回类型定义为`internal``private`,就会出现函数可以被任何人访问,但是它的参数和返回类型不可以,同样会出现错误。
<a name="default_access_levels"></a> <a name="default_access_levels"></a>
### 默认访问级别 ### 默认访问级别
代码中的所有实体,如果你不明确的定义其访问级别,那么它们默认为`internal`级别。在大多数情况下,我们不需要明确的设置实体的访问级别因为我们大多数时候都是在开发一个 App bundle。 如果你不为代码中的所有实体定义显式访问级别,那么它们默认为`internal`级别。在大多数情况下,我们不需要设置实体的显式访问级别因为我们一般都是在开发一个`app bundle`
<a name="access_levels_for_single-target_apps"></a> <a name="access_levels_for_single-target_apps"></a>
### 单目标应用程序的访问级别 ### 单目标应用程序的访问级别
@ -79,9 +82,14 @@ Swift 提供了三种不同的访问级别。这些访问级别相对于源文
<a name="access_levels_for_frameworks"></a> <a name="access_levels_for_frameworks"></a>
### Framework的访问级别 ### Framework的访问级别
当你开发`Framework`时,就需要把一些实体定义为`public`级别,以便其他人导入该`Framework`后可以正常使用其功能。这些被你定义为`public`实体,就是这个`Framework`的API。 当你开发`Framework`时,就需要把一些对外的接口定义为`public`级别,以便其他人导入该`Framework`后可以正常使用其功能。这些被你定义为`public`接口,就是这个`Framework`的API。
> 注意:`Framework`的内部实现细节依然可以使用默认的`internal`级别,或者也可以定义为`private`级别。只有你想它作为 API 的实体,才将其定义为`public`级别。 > 注意:`Framework`的内部实现细节依然可以使用默认的`internal`级别,或者也可以定义为`private`级别。只有你想它作为 API 的一部分的时候,才将其定义为`public`级别。
<a name="access_levels_for_unit_test_targets"></a>
### 单元测试目标的访问级别
当你的app有单元测试目标时为了方便测试测试模块需要能访问到你app中的代码。默认情况下只有`public`级别的实体才可以被其他模块访问。然而,如果在引入一个生产模块时使用`@testable`注解,然后用带测试的方式编译这个生产模块,单元测试目标就可以访问所有`internal`级别的实体。
<a name="access_control_syntax"></a> <a name="access_control_syntax"></a>
## 访问控制语法 ## 访问控制语法
@ -97,7 +105,7 @@ internal let someInternalConstant = 0
private func somePrivateFunction() {} private func somePrivateFunction() {}
``` ```
除非有特殊的说明,否则实体都使用默认的访问级别`internal`,可以查阅**默认访问级别**这一节。这意味着`SomeInternalClass``someInternalConstant`不用明确的使用修饰符声明访问级别,但是他们任然拥有隐式的访问级别`internal` 除非有特殊的说明,否则实体都使用默认的访问级别`internal`,可以查阅**[默认访问级别](#default_access_levels)**这一节。这意味着在不使用修饰符声明显式访问级别的情况下,`SomeInternalClass``someInternalConstant`然拥有隐式的访问级别`internal`
```swift ```swift
class SomeInternalClass {} // 隐式访问级别 internal class SomeInternalClass {} // 隐式访问级别 internal
@ -105,40 +113,40 @@ var someInternalConstant = 0 // 隐式访问级别 internal
``` ```
<a name="custom_types"></a> <a name="custom_types"></a>
## 自定义类型 ## 自定义类型
如果想为一个自定义类型指定一个明确的访问级别,那么要明确一点。那就是你要确保新类型的访问级别和它实际的作用域相匹配。比如说,如果某个类里的属性、函数、返回值它们的作用域仅在当前的源文件中,那么你就可以将这个类申明为`private`类,而不需要申明为`public`或者`internal` 如果想为一个自定义类型申明显式访问级别,那么要明确一点。那就是你要确保新类型的访问级别和它实际的作用域相匹配。比如说,如果你定义了一个`private`类,那这个类就只能在定义它的源文件中当作属性类型、函数参数或者返回类型使用
类的访问级别也可以影响到类成员(属性、函数、初始化方法等)的默认访问级别。如果你将类申明为`private`类,那么该类的所有成员的默认访问级别也会成为`private`。如果你将类申明为`public`或者`internal`类(或者不明确的指定访问级别,而使用默认的`internal`访问级别),那么该类的所有成员的访问级别是`internal` 类的访问级别也可以影响到类成员(属性、函数、初始化方法等)的默认访问级别。如果你将类申明为`private`类,那么该类的所有成员的默认访问级别也会成为`private`。如果你将类申明为`public`或者`internal`类(或者不明确的申明访问级别,而使用默认的`internal`访问级别),那么该类的所有成员的访问级别是`internal`
> 注意:上面提到,一个`public`类的所有成员的访问级别默认为`internal`级别,而不是`public`级别。如果你想将某个成员申明为`public`级别,那么你必须使用修饰符明确的明该成员。这样做的好处是在你定义公共接口API的时候可以明确的选择哪些属性或方法是需要公开的哪些是内部使用的可以避免将内部使用的属性方法公开成公共API的错误。 > 注意:上面提到,一个`public`类的所有成员的访问级别默认为`internal`级别,而不是`public`级别。如果你想将某个成员申明为`public`级别,那么你必须使用修饰符明确的明该成员。这样做的好处是在你定义公共接口API的时候可以明确的选择哪些属性或方法是需要公开的哪些是内部使用的可以避免将内部使用的属性方法公开成公共API的错误。
```swift ```swift
public class SomePublicClass { // 显的 public 类 public class SomePublicClass { // 显的 public 类
public var somePublicProperty = 0 // 显的 public 类成员 public var somePublicProperty = 0 // 显的 public 类成员
var someInternalProperty = 0 // 隐式的 internal 类成员 var someInternalProperty = 0 // 隐式的 internal 类成员
private func somePrivateMethod() {} // 显的 private 类成员 private func somePrivateMethod() {} // 显的 private 类成员
} }
class SomeInternalClass { // 隐式的 internal 类 class SomeInternalClass { // 隐式的 internal 类
var someInternalProperty = 0 // 隐式的 internal 类成员 var someInternalProperty = 0 // 隐式的 internal 类成员
private func somePrivateMethod() {} // 显的 private 类成员 private func somePrivateMethod() {} // 显的 private 类成员
} }
private class SomePrivateClass { // 显的 private 类 private class SomePrivateClass { // 显的 private 类
var somePrivateProperty = 0 // 隐式的 private 类成员 var somePrivateProperty = 0 // 隐式的 private 类成员
func somePrivateMethod() {} // 隐式的 private 类成员 func somePrivateMethod() {} // 隐式的 private 类成员
} }
``` ```
<a name="tuple_types"></a> <a name="tuple_types"></a>
### 元组类型 ### 元组类型
元组的访问级别使用是所有类型的访问级别使用中最为严谨的。比如说,如果你构建一个包含两种不同类型元素的元组,其中一个元素类型的访问级别为`internal`,另一个为`private`级别,那么这个元组的访问级别为`private`。也就是说元组的访问级别遵循它里面元组中最低级的访问级别 元组的访问级别使用是所有类型的访问级别使用中最为严谨的。比如说,如果你构建一个包含两种不同类型元素的元组,其中一个元素类型的访问级别为`internal`,另一个为`private`级别,那么这个元组的访问级别为`private`。也就是说元组的访问级别与元组中访问级别最低的类型一致
> 注意:元组不同于类、结构体、枚举、函数那样有单独的定义。元组的访问级别是在它被使用时自动推导出的,而不是明确的申明。 > 注意:元组不同于类、结构体、枚举、函数那样有单独的定义。元组的访问级别是在它被使用时自动推导出的,而不是明确的申明。
<a name="function_types"></a> <a name="function_types"></a>
### 函数类型 ### 函数类型
函数的访问级别需要根据该函数的参数类型访问级别、返回类型访问级别得出。如果根据参数类型和返回类型得出的函数访问级别不符合上下文,那么就需要明确申明该函数的访问级别。 函数的访问级别需要根据该函数的参数类型返回类型访问级别得出。如果根据参数类型和返回类型得出的函数访问级别不符合默认上下文,那么就需要明确申明该函数的访问级别。
下面的例子定义了一个全局函数名为`someFunction`,并且没有明确申明其访问级别。也许会认为该函数应该拥有默认的访问级别`internal`,但事实并非如此。事实上,如果按下面这种写法,编译器是无法编译通过的: 下面的例子定义了一个名为`someFunction`全局函数,并且没有明确申明其访问级别。也许会认为该函数应该拥有默认的访问级别`internal`,但事实并非如此。事实上,如果按下面这种写法,带埋是无法编译通过的:
```swift ```swift
func someFunction() -> (SomeInternalClass, SomePrivateClass) { func someFunction() -> (SomeInternalClass, SomePrivateClass) {
@ -146,9 +154,9 @@ func someFunction() -> (SomeInternalClass, SomePrivateClass) {
} }
``` ```
我们可以看到,这个函数的返回类型是一个元组,该元组中包含两个自定义的类(可查阅**自定义类型**)。其中一个类的访问级别是`internal`,另一个的访问级别是`private`,所以根据元组访问级别的原则,该元组的访问级别是`private`(元组的访问级别遵循它里面元组中最低级的访问级别)。 我们可以看到,这个函数的返回类型是一个元组,该元组中包含两个自定义的类(可查阅**[自定义类型](#custom_types)**)。其中一个类的访问级别是`internal`,另一个的访问级别是`private`,所以根据元组访问级别的原则,该元组的访问级别是`private`(元组的访问级别与元组中访问级别最低的类型一致)。
因为该函数返回类型的访问级别是`private`,所以你必须使用`private`修饰符,明确的申请该函数: 因为该函数返回类型的访问级别是`private`,所以你必须使用`private`修饰符,明确的声明该函数:
```swift ```swift
private func someFunction() -> (SomeInternalClass, SomePrivateClass) { private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
@ -160,7 +168,7 @@ private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
<a name="enumeration_types"></a> <a name="enumeration_types"></a>
### 枚举类型 ### 枚举类型
枚举中成员的访问级别继承自该枚举,你不能为枚举中的成员指定访问级别。 枚举中成员的访问级别继承自该枚举,你不能为枚举中的成员单独申明不同的访问级别。
比如下面的例子,枚举`CompassPoint`被明确的申明为`public`级别,那么它的成员`North``South``East``West`的访问级别同样也是`public` 比如下面的例子,枚举`CompassPoint`被明确的申明为`public`级别,那么它的成员`North``South``East``West`的访问级别同样也是`public`
@ -175,11 +183,11 @@ public enum CompassPoint {
<a name="raw_values_and_associated_values"></a> <a name="raw_values_and_associated_values"></a>
### 原始值和关联值 ### 原始值和关联值
用于枚举定义中的任何原始值或关联值类型必须有一个访问级别,至少要于枚举的访问级别。比如说,你不能在一个`internal`访问级别的枚举中定义`private`级别的原始值类型。 枚举定义中的任何原始值或关联值类型必须有一个访问级别,这个级别至少要不低于枚举的访问级别。比如说,你不能在一个`internal`访问级别的枚举中定义`private`级别的原始值类型。
<a name="nested_types"></a> <a name="nested_types"></a>
### 嵌套类型 ### 嵌套类型
如果在`private`级别的类型中定义嵌套类型,那么该嵌套类型就自动拥有`private`访问级别。如果在`public`或者`internal`级别的类型中定义嵌套类型,那么该嵌套类型自动拥有`internal`访问级别。如果想让嵌套类型拥有`public`访问级别,那么需要该嵌套类型进行明确的访问级别申明 如果在`private`级别的类型中定义嵌套类型,那么该嵌套类型就自动拥有`private`访问级别。如果在`public`或者`internal`级别的类型中定义嵌套类型,那么该嵌套类型自动拥有`internal`访问级别。如果想让嵌套类型拥有`public`访问级别,那么需要明确地申明该嵌套类型的访问级别。
<a name="subclassing"></a> <a name="subclassing"></a>
## 子类 ## 子类
@ -217,7 +225,7 @@ internal class B: A {
<a name="constants_variables_properties_subscripts"></a> <a name="constants_variables_properties_subscripts"></a>
## 常量、变量、属性、下标 ## 常量、变量、属性、下标
常量、变量、属性不能拥有比它们的类型更高的访问级别。比如说,你定义一个`public`级别的属性,但是它的类型是`private`级别的,这是编译器不允许的。同样,下标也不能拥有比索引类型或返回类型更高的访问级别。 常量、变量、属性不能拥有比它们的类型更高的访问级别。比如说,你定义一个`public`级别的属性,但是它的类型是`private`级别的,这是编译器不允许的。同样,下标也不能拥有比索引类型或返回类型更高的访问级别。
如果常量、变量、属性、下标索引的定义类型是`private`级别的,那么它们必须要明确的申明访问级别为`private` 如果常量、变量、属性、下标索引的定义类型是`private`级别的,那么它们必须要明确的申明访问级别为`private`
@ -229,11 +237,11 @@ private var privateInstance = SomePrivateClass()
### Getter和Setter ### Getter和Setter
常量、变量、属性、下标索引的`Getters``Setters`的访问级别继承自它们所属成员的访问级别。 常量、变量、属性、下标索引的`Getters``Setters`的访问级别继承自它们所属成员的访问级别。
`Setter`的访问级别可以低于对应的`Getter`的访问级别,这样就可以控制变量、属性或下标索引的读写权限。在`var``subscript`定义作用域之前,你可以通过`private(set)``internal(set)`先为它的写权限申明一个较低的访问级别。 `Setter`的访问级别可以低于对应的`Getter`的访问级别,这样就可以控制变量、属性或下标索引的读写权限。在`var``subscript`定义作用域之前,你可以通过`private(set)``internal(set)`先为它的写权限申明一个较低的访问级别。
> 注意:这个规定适用于用作存储的属性或用作计算的属性。即使你不明确申明存储属性的`Getter`、`Setter`Swift也会隐式的为其创建`Getter`和`Setter`,用于对该属性进行读取操作。使用`private(set)`和`internal(set)`可以改变Swift隐式创建的`Setter`的访问级别。计算属性中也是同样的 > 注意:这个规定适用于用作存储的属性或用作计算的属性。即使你不明确申明存储属性的`Getter`、`Setter`Swift也会隐式的为其创建`Getter`和`Setter`,用于对该属性进行读取操作。使用`private(set)`和`internal(set)`可以改变Swift隐式创建的`Setter`的访问级别。这对计算属性也同样适用
下面的例子中定义了一个结构体名为`TrackedString`,它记录了`value`属性被修改的次数: 下面的例子中定义了一个名为`TrackedString`的结构体,它记录了`value`属性被修改的次数:
```swift ```swift
struct TrackedString { struct TrackedString {
@ -247,11 +255,11 @@ struct TrackedString {
``` ```
`TrackedString`结构体定义了一个用于存储的属性名为`value`类型为`String`并将初始化值设为`""`(即一个空字符串)。该结构体同时也定义了另一个用于存储的属性名`numberOfEdits`类型为`Int`它用于记录属性`value`被修改的次数。这个功能的实现通过属性`value``didSet`方法实现,每当给`value`赋新值时就会调用`didSet`方法,`numberOfEdits`加一。 `TrackedString`结构体定义了一个用于存储`String`类型的属性`value`,并将初始化值设为`""`(即一个空字符串)。该结构体同时也定义了另一个用于存储`Int`类型的属性名`numberOfEdits`,它用于记录属性`value`被修改的次数。这个功能的实现通过属性`value``didSet`方法实现,每当给`value`赋新值时就会调用`didSet`方法,然后将`numberOfEdits`的值加一。
结构体`TrackedString`和它的属性`value`均没有明确的申明访问级别,所以它们都拥有默认的访问级别`internal`。但是该结构体的`numberOfEdits`属性使用`private(set)`修饰符进行申明,这意味着`numberOfEdits`属性只能在定义该结构体的源文件中赋值。`numberOfEdits`属性的`Getter`依然是默认的访问级别`internal`,但是`Setter`的访问级别是`private`,这表示该属性只有在当前的源文件中是可读写的,在当前源文件所属的模块中它只是一个可读的属性。 结构体`TrackedString`和它的属性`value`均没有申明显式访问级别,所以它们都拥有默认的访问级别`internal`。但是该结构体的`numberOfEdits`属性使用`private(set)`修饰符进行申明,这意味着`numberOfEdits`属性只能在定义该结构体的源文件中赋值。`numberOfEdits`属性的`Getter`依然是默认的访问级别`internal`,但是`Setter`的访问级别是`private`,这表示该属性只有在当前的源文件中是可读写的,在当前源文件所属的模块中它只是一个可读的属性。
如果你实例化`TrackedString`结构体,并且多次对`value`属性的值进行修改,你就会看到`numberOfEdits`的值会随着修改次数更改 如果你实例化`TrackedString`结构体,并且多次对`value`属性的值进行修改,你就会看到`numberOfEdits`的值会随着修改次数进行变化
```swift ```swift
var stringToEdit = TrackedString() var stringToEdit = TrackedString()
@ -264,9 +272,23 @@ println("The number of edits is \(stringToEdit.numberOfEdits)")
虽然你可以在其他的源文件中实例化该结构体并且获取到`numberOfEdits`属性的值,但是你不能对其进行赋值。这样就能很好的告诉使用者,你只管使用,而不需要知道其实现细节。 虽然你可以在其他的源文件中实例化该结构体并且获取到`numberOfEdits`属性的值,但是你不能对其进行赋值。这样就能很好的告诉使用者,你只管使用,而不需要知道其实现细节。
如果有必要你可以为`Getter``Setter`方法设定显式访问级别。下面的例子就是明确申明了`public`访问级别的`TrackedString`结构体。结构体的成员(包括`numberOfEdits`属性)拥有默认的访问级别`internal`。你可以结合`public``private(set)`修饰符把结构体中的`numberOfEdits`属性`Getter`方法的访问级别设置为`public`,而`Setter`方法的访问级别设置为`private`
```swift
public struct TrackedString {
public private(set) var numberOfEdits = 0
public var value: String = "" {
didSet {
numberOfEdits++
}
}
public init() {}
}
```
<a name="initializers"></a> <a name="initializers"></a>
## 初始化 ## 初始化
我们可以给自定义的初始化方法指定访问级别,但是必须要低于或等于它所属类的访问级别。但如果该初始化方法是必须要使用的话,那它的访问级别必须和所属类的访问级别相同。 我们可以给自定义的初始化方法申明访问级别,但是要不高于它所属类的访问级别。但[必要构造器](TODO)例外,它的访问级别必须和所属类的访问级别相同。
如同函数或方法参数,初始化方法参数的访问级别也不能低于初始化方法的访问级别。 如同函数或方法参数,初始化方法参数的访问级别也不能低于初始化方法的访问级别。
@ -284,7 +306,7 @@ Swift为结构体、类都提供了一个默认的无参初始化方法用于
<a name="protocols"></a> <a name="protocols"></a>
## 协议 ## 协议
如果想为一个协议明确的申明访问级别,那么有一点需要注意,就是你要确保该协议只在你申明的访问级别作用域中使用。 如果想为一个协议明确的申明访问级别,那么需要注意一点,就是你要确保该协议只在你申明的访问级别作用域中使用。
协议中的每一个必须要实现的函数都具有和该协议相同的访问级别。这样才能确保该协议的使用者可以实现它所提供的函数。 协议中的每一个必须要实现的函数都具有和该协议相同的访问级别。这样才能确保该协议的使用者可以实现它所提供的函数。
@ -298,17 +320,17 @@ Swift为结构体、类都提供了一个默认的无参初始化方法用于
### 协议一致性 ### 协议一致性
类可以采用比自身访问级别低的协议。比如说,你可以定义一个`public`级别的类,可以让它在其他模块中使用,同时它也可以采用一个`internal`级别的协议,并且只能在定义了该协议的模块中使用。 类可以采用比自身访问级别低的协议。比如说,你可以定义一个`public`级别的类,可以让它在其他模块中使用,同时它也可以采用一个`internal`级别的协议,并且只能在定义了该协议的模块中使用。
采用了协议的类的访问级别遵循它本身和采用协议中最低的访问级别。也就是说如果一个类是`public`级别,采用的协议是`internal`级别,那采用了这个协议后,该类的访问级别也是`internal` 采用了协议的类的访问级别它本身和采用协议中最低的访问级别。也就是说如果一个类是`public`级别,采用的协议是`internal`级别,那采用了这个协议后,该类的访问级别也是`internal`
如果你采用了协议,那么实现了协议必须的方法后,该方法的访问级别遵循协议的访问级别。比如说,一个`public`级别的类,采用了`internal`级别的协议,那么该类实现协议的方法至少也得是`internal` 如果你采用了协议,那么实现了协议必须的方法后,该方法的访问级别遵循协议的访问级别。比如说,一个`public`级别的类,采用了`internal`级别的协议,那么该类实现协议的方法至少也得是`internal`
> 注意:Swift和Objective-C一样,协议的一致性保证了一个类不可能在同一个程序中用不同的方法采用同一个协议。 > 注意Swift和Objective-C一样协议的一致性保证了一个类不可能在同一个程序中用不同的方法采用同一个协议。
<a name="extensions"></a> <a name="extensions"></a>
## 扩展 ## 扩展
你可以在条件允许的情况下对类、结构体、枚举进行扩展。扩展成员应该具有和原始类成员一致的访问级别。比如你扩展了一个公共类型,那么你新加的成员应该具有和原始成员一样的默认的`internal`访问级别。 你可以在条件允许的情况下对类、结构体、枚举进行扩展。扩展成员应该具有和原始类成员一致的访问级别。比如你扩展了一个公共类型,那么你新加的成员应该具有和原始成员一样的默认的`internal`访问级别。
或者,你可以明确申明扩展的访问级别(比如使用`private extension`)给该扩展内所有成员指定一个新的默认访问级别。这个新的默认访问级别仍然可以被单独成员所指定的访问级别所覆盖。 或者,你可以明确申明扩展的访问级别(比如使用`private extension`)给该扩展内所有成员申明一个新的默认访问级别。这个新的默认访问级别仍然可以被单独成员所申明的访问级别所覆盖。
<a name="adding_protocol_conformance_with_an_extension"></a> <a name="adding_protocol_conformance_with_an_extension"></a>
### 协议的扩展 ### 协议的扩展
@ -316,12 +338,13 @@ Swift为结构体、类都提供了一个默认的无参初始化方法用于
<a name="generics"></a> <a name="generics"></a>
## 泛型 ## 泛型
泛型类型或泛型函数的访问级别遵循泛型类型、函数本身、泛型类型参数三者中访问级别最低的级别。 泛型类型或泛型函数的访问级别泛型类型、函数本身、泛型类型参数三者中的最低访问级别。
<a name="type_aliases"></a> <a name="type_aliases"></a>
## 类型别名 ## 类型别名
任何你定义的类型别名都会被视作为不同的类型,这些类型用于访问控制。一个类型别名的访问级别可以低于或等于这个类型的访问级别。比如说,一个`private`级别的类型别名可以设定给一个`public``internal``private`的类型,但是一个`public`级别的类型别名只能设定给一个`public`级别的类型,不能设定给`internal``private`类型。 任何你定义的类型别名都会被当作不同的类型,以便于进行访问控制。一个类型别名的访问级别不可高于原类型的访问级别。比如说,一个`private`级别的类型别名可以设定给一个`public``internal``private`的类型,但是一个`public`级别的类型别名只能设定给一个`public`级别的类型,不能设定给`internal``private` 级别的类型。
> 注意:这条规则也适用于为满足协议一致性而给相关类型命名别名的情况。
> 注意:这条规则也适用于为满足协议一致性而给相关类型命名别名。

View File

@ -1,5 +1,5 @@
> 翻译:[dabing1022](https://github.com/dabing1022) > 翻译:[dabing1022](https://github.com/dabing1022)
> 校对:[numbbbbb](https://github.com/numbbbbb) > 校对:[numbbbbb](https://github.com/numbbbbb), [KYawn](https://github.com/KYawn)
# 关于语言附注 # 关于语言附注
@ -11,28 +11,32 @@
本书的这一节描述了Swift编程语言的形式语法。这里描述的语法是为了帮助您更详细的了解该语言而不是让您直接实现一个解析器或编译器。 本书的这一节描述了Swift编程语言的形式语法。这里描述的语法是为了帮助您更详细的了解该语言而不是让您直接实现一个解析器或编译器。
Swift语言相对小一点这是由于在Swift代码中几乎所有常见的类型、函数以及运算符都已经在Swift标准库中定义了。虽然这些类型、函数和运算符并不是Swift语言自身的一部分但是它们被广泛应用于本书的讨论和代码范例中。
Swift语言相对小点这是由于在Swift代码中几乎无处不在的许多常见的的类型函数以及运算符都由Swift标准库来定义。虽然这些类型函数和运算符不是Swift语言本身的一部分但是它们被广泛用于这本书的讨论和代码范例。
<a name="how_to_read_the_grammar"></a> <a name="how_to_read_the_grammar"></a>
## 如何阅读语法 ## 如何阅读语法
用来描述Swift编程语言形式语法的记遵循下面几个约定: 用来描述Swift编程语言形式语法的记遵循下面几个约定:
- 箭头(→)用来标记语法产式,可以理解为“可以包含”。 - 箭头(→)用来标记语法产式,可以理解为“可以包含”。
- 句法范畴由*斜体*文字表示,并出现在一个语法产式规则两侧。 - *斜体*文字用来表示句法分类,并出现在一个语法产式规则两侧。
- 义词和标点符号由粗体固定宽度的文本显示和只出现在一个语法产式规则的右 - 义词和标点符号由粗体固定宽度的文本标记,而且只出现在一个语法产式规则的右
- 选择性的语法产式由竖线(|)分隔。当可选用的语法产式太多时,为了阅读方便,它们将被拆分为多行语法产式规则。 - 选择性的语法产式由竖线(|)分隔。当可选用的语法产式太多时,为了阅读方便,它们将被拆分为多行语法产式规则。
- 少数情况下,常规字体文字用来描述语法产式规则的右边。 - 少数情况下,常规字体文字用来描述语法产式规则的右边。
- 可选的句法范畴和文字用尾标`opt`来标记。 - 可选的句法分类和文字用尾标`opt`来标记。
举个例子getter-setter的语法块的定义如下 举个例子getter-setter的语法块的定义如下
> GRAMMAR OF A GETTER-SETTER BLOCK > GRAMMAR OF A GETTER-SETTER BLOCK
> *getter-setter-block* → {­ [*getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause) [­*setter-clause*­](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause)*opt* ­}­ | {­ [*setter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause) [­*getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause)­}­ > *getter-setter-block* → {­ [*getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause) [­*setter-clause*­](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause)*opt* ­}­ | {­ [*setter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause) [­*getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause)­}­
这个定义表明一个getter-setter方法块可以由一个getter子句后跟一个可选的setter子句构成用大括号括起来或者由一个setter子句后跟一个getter子句构成用大括号括起来。上述的文法产等价于下面的两个产生,明确阐明如何二中择一 这个定义表明一个getter-setter方法块可以由一个getter子句后跟一个可选的setter子句构成然后用大括号括起来或者由一个setter子句后跟一个getter子句构成然后用大括号括起来。下面的两个语法产等价于上述的语法产式,并明确指出了如何取舍
> GRAMMAR OF A GETTER-SETTER BLOCK > GRAMMAR OF A GETTER-SETTER BLOCK
> getter-setter-block → {­ [*getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause) [*­setter-clause*­](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause)*opt* ­}­­ > getter-setter-block → {­ [*getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause) [*­setter-clause*­](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause)*opt* ­}­­
> getter-setter-block → {­ [*setter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause) [*­getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause)­}­ > getter-setter-block → {­ [*setter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause) [*­getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause)­}­

View File

@ -1,238 +1,279 @@
> 翻译:[superkam](https://github.com/superkam) > 翻译:[superkam](https://github.com/superkam)
> 校对:[numbbbbb](https://github.com/numbbbbb) > 校对:[numbbbbb](https://github.com/numbbbbb)
# 词法结构 # 词法结构
----------------- -----------------
本页包含内容: 本页包含内容:
- [空白与注释(*Whitespace and Comments*](#whitespace_and_comments) - [空白与注释(*Whitespace and Comments*](#whitespace_and_comments)
- [标识符(*Identifiers*](#identifiers) - [标识符(*Identifiers*](#identifiers)
- [关键字(*Keywords*](#keywords) - [关键字(*Keywords*](#keywords)
- [字面量(*Literals*](#literals) - [字面量(*Literals*](#literals)
- [运算符(*Operators*](#operators) - [运算符(*Operators*](#operators)
Swift 的“词法结构(*lexical structure*)”描述了如何在该语言中字符序列构建合法标记组成语言中最底层的代码块,并在之后的章节中用于描述语言的其他部分。 Swift 的“词法结构(*lexical structure*)”描述了能构成该语言中合法标记(*tokens*)的字符序列。这些合法标记组成语言中最底层的构建基块,并在之后的章节中用于描述语言的其他部分。
通常,标记在随后介绍的语法约束下,由 Swift 源文件的输入文本中提取可能的最长子串生成。这种方法称为“最长匹配项(*longest match*)”,或者“最大适合”(*maximal munch*)。 通常情况下,标记在随后介绍的语法约束下,由 Swift 源文件的输入文本中提取可能的最长子串生成。这种方法称为“最长匹配项(*longest match*)”,或者“最大适合”(*maximal munch*)。
<a name="whitespace_and_comments"></a> <a id="whitespace_and_comments"></a>
## 空白与注释 ## 空白与注释
空白(*whitespace*)有两个用途:分隔源文件中的标记和区分运算符属于前缀还是后缀(参见 [运算符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-XID_871))在其他情况下则会被忽略。以下的字符会被当作空白:空格(*space*U+0020、换行符*line feed*U+000A、回车符*carriage return*U+000D、水平 tab*horizontal tab*U+0009、垂直 tab*vertical tab*U+000B、换页符*form feed*U+000C以及空*null*U+0000 空白(*whitespace*)有两个用途:分隔源文件中的标记和帮助区分运算符属于前缀还是后缀(参见 [运算符](#operators)在其他情况下则会被忽略。以下的字符会被当作空白:空格(*space*U+0020、换行符*line feed*U+000A、回车符*carriage return*U+000D、水平制表符*horizontal tab*U+0009、垂直制表符*vertical tab*U+000B、换页符*form feed*U+000C以及空*null*U+0000
注释(*comments*)被编译器当作空白处理。单行注释由 `//` 开始直到该行结束。多行注释由 `/*` 开始,以 `*/` 结束。可以嵌套注释,但注意注释标记必须匹配。 注释(*comments*)被编译器当作空白处理。单行注释由 `//` 开始直至遇到换行符(*line feed*U+000A或者回车符*carriage return*U+000D。多行注释由 `/*` 开始,以 `*/` 结束。注释允许嵌套,但注释标记必须匹配。
<a name="identifiers"></a> <a id="identifiers"></a>
## 标识符 ## 标识符
标识符(*identifiers*)可以由以下的字符开始:大写或小写的字母 `A``Z`、下划线 `_`、基本多语言面(*Basic Multilingual Plane*)中的 Unicode 非组合字符以及基本多语言面以外的非专用区(*Private Use Area*)字符。首字符之后,标识符允许使用数字和 Unicode 字符组合。 标识符(*identifiers*)可以由以下的字符开始:大写或小写的字母 `A``Z`、下划线 `_`、基本多文种平面(*Basic Multilingual Plane*)中的 Unicode 非组合字符以及基本多文种平面以外的非专用区(*Private Use Area*)字符。首字符之后,允许使用数字和 Unicode 字符组合。
使用保留字(*reserved word*)作为标识符,需要在其前后增加反引号 <code>\`</code>。例如,<code>class</code> 不是合法的标识符,但可以使用 <code>\`class\`</code>。反引号不属于标识符的一部分,<code>\`x\`</code>`x` 表示同一标识符。 使用保留字(*reserved word*)作为标识符,需要在其前后增加反引号 `` `。例如,`class` 不是合法的标识符,但可以使用 <code>\`class\`</code>。反引号不属于标识符的一部分,<code>\`x\`</code>`x` 表示同一标识符。
闭包(*closure*)中如果没有明确指定参数名称,参数将被隐式命名为 <code>$0</code><code>$1</code><code>$2</code>... 这些命名在闭包作用域内是合法的标识符。 闭包(*closure*)中如果没有明确指定参数名称,参数将被隐式命名为 `$0``$1``$2`等等。 这些命名在闭包作用域范围内是合法的标识符。
> 标识符语法 > 标识符语法
> *标识符* → [*标识符头(Head)*](LexicalStructure.html#identifier_head) [*标识符字符列表*](LexicalStructure.html#identifier_characters) _可选_ <a id="identifier"></a>
> *标识符* → **`** [*标识符头(Head)*](LexicalStructure.html#identifier_head) [*标识符字符列表*](LexicalStructure.html#identifier_characters) _可选_ **`** > *标识符* → [*头部标识符*](#identifier_head) [*标识符字符*](#identifier_characters)<sub>可选</sub>
> *标识符* → [*隐式参数名*](LexicalStructure.html#implicit_parameter_name) > *标识符* → \`[*头部标识符*](#identifier_head) [*标识符字符组*](#identifier_characters)<sub>可选</sub>\`
> *标识符列表* → [*标识符*](LexicalStructure.html#identifier) | [*标识符*](LexicalStructure.html#identifier) **,** [*标识符列表*](LexicalStructure.html#identifier_list) > *标识符* → [*隐式参数名*](#implicit_parameter_name)
> *标识符头(Head)* → Upper- or lowercase letter A through Z > *标识符列表* → [*标识符*](#identifier) | [*标识符*](#identifier) **,** [*标识符列表*](#identifier_list)
> *标识符头(Head)* → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2U+00B5, or U+00B7U+00BA <a id="identifier_head"></a>
> *标识符头(Head)* → U+00BCU+00BE, U+00C0U+00D6, U+00D8U+00F6, or U+00F8U+00FF > *头部标识符* → 大写或小写字母 A - Z
> *标识符头(Head)* → U+0100U+02FF, U+0370U+167F, U+1681U+180D, or U+180FU+1DBF > *头部标识符* → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2U+00B5, or U+00B7U+00BA
> *标识符头(Head)* → U+1E00U+1FFF > *头部标识符* → U+00BCU+00BE, U+00C0U+00D6, U+00D8U+00F6, or U+00F8U+00FF
> *标识符头(Head)* → U+200BU+200D, U+202AU+202E, U+203FU+2040, U+2054, or U+2060U+206F > *头部标识符* → U+0100U+02FF, U+0370U+167F, U+1681U+180D, or U+180FU+1DBF
> *标识符头(Head)* → U+2070U+20CF, U+2100U+218F, U+2460U+24FF, or U+2776U+2793 > *头部标识符* → U+1E00U+1FFF
> *标识符头(Head)* → U+2C00U+2DFF or U+2E80U+2FFF > *头部标识符* → U+200BU+200D, U+202AU+202E, U+203FU+2040, U+2054, or U+2060U+206F
> *标识符头(Head)* → U+3004U+3007, U+3021U+302F, U+3031U+303F, or U+3040U+D7FF > *头部标识符* → U+2070U+20CF, U+2100U+218F, U+2460U+24FF, or U+2776U+2793
> *标识符头(Head)* → U+F900U+FD3D, U+FD40U+FDCF, U+FDF0U+FE1F, or U+FE30U+FE44 > *头部标识符* → U+2C00U+2DFF or U+2E80U+2FFF
> *标识符头(Head)* → U+FE47U+FFFD > *头部标识符* → U+3004U+3007, U+3021U+302F, U+3031U+303F, or U+3040U+D7FF
> *标识符头(Head)* → U+10000U+1FFFD, U+20000U+2FFFD, U+30000U+3FFFD, or U+40000U+4FFFD > *头部标识符* → U+F900U+FD3D, U+FD40U+FDCF, U+FDF0U+FE1F, or U+FE30U+FE44
> *标识符头(Head)* → U+50000U+5FFFD, U+60000U+6FFFD, U+70000U+7FFFD, or U+80000U+8FFFD > *头部标识符* → U+FE47U+FFFD
> *标识符头(Head)* → U+90000U+9FFFD, U+A0000U+AFFFD, U+B0000U+BFFFD, or U+C0000U+CFFFD > *头部标识符* → U+10000U+1FFFD, U+20000U+2FFFD, U+30000U+3FFFD, or U+40000U+4FFFD
> *标识符头(Head)* → U+D0000U+DFFFD or U+E0000U+EFFFD > *头部标识符* → U+50000U+5FFFD, U+60000U+6FFFD, U+70000U+7FFFD, or U+80000U+8FFFD
> *标识符字符* → 数值 0 到 9 > *头部标识符* → U+90000U+9FFFD, U+A0000U+AFFFD, U+B0000U+BFFFD, or U+C0000U+CFFFD
> *标识符字符* → U+0300U+036F, U+1DC0U+1DFF, U+20D0U+20FF, or U+FE20U+FE2F > *头部标识符* → U+D0000U+DFFFD or U+E0000U+EFFFD
> *标识符字符* → [*标识符头(Head)*](LexicalStructure.html#identifier_head) > *标识符字符* → 数值 0 - 9
> *标识符字符列表* → [*标识符字符*](LexicalStructure.html#identifier_character) [*标识符字符列表*](LexicalStructure.html#identifier_characters) _可选_ > *标识符字符* → U+0300U+036F, U+1DC0U+1DFF, U+20D0U+20FF, or U+FE20U+FE2F
> *隐式参数名* → **$** [*十进制数字列表*](LexicalStructure.html#decimal_digits) > *标识符字符* → [*头部标识符*](#identifier_head)
<a id="identifier_characters"></a>
<a name="keywords"></a> > *标识符字符组* → [*标识符字符*](#identifier_character) [*标识符字符列表*](#identifier_characters)<sub>可选</sub>
## 关键字 <a id="implicit_parameter_name"></a>
> *隐式参数名* → **$** [*十进制数字列表*](#decimal_digits)
被保留的关键字(*keywords*)不允许用作标识符,除非被反引号转义,参见 [标识符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-XID_796)。
<a id="keywords"></a>
* **用作声明的关键字:** *class*、*deinit*、*enum*、*extension*、*func*、*import*、*init*、*let*、*protocol*、*static*、*struct*、*subscript*、*typealias*、*var* ## 关键字和符号
* **用作语句的关键字:** *break*、*case*、*continue*、*default*、*do*、*else*、*fallthrough*、*if*、*in*、*for*、*return*、*switch*、*where*、*while*
* **用作表达和类型的关键字:** *as*、*dynamicType*、*is*、*new*、*super*、*self*、*Self*、*Type*、*\_\_COLUMN\_\_*、*\_\_FILE\_\_*、*\_\_FUNCTION\_\_*、*\_\_LINE\_\_* 下面这些被保留的关键字(*keywords*)不允许用作标识符,除非被反引号转义,具体描述请参考 [标识符](#identifiers)。
* **特定上下文中被保留的关键字:** *associativity*、*didSet*、*get*、*infix*、*inout*、*left*、*mutating*、*none*、*nonmutating*、*operator*、*override*、*postfix*、
*precedence*、*prefix*、*right*、*set*、*unowned*、*unowned(safe)*、*unowned(unsafe)*、*weak*、*willSet*,这些关键字在特定上下文之外可以被用于标识符。 * **用在声明中的关键字:** *class*、*deinit*、*enum*、*extension*、*func*、*import*、*init*、*let*、*protocol*、*static*、*struct*、*subscript*、*typealias*、*var*
* **用在语句中的关键字:** *break*、*case*、*continue*、*default*、*do*、*else*、*fallthrough*、*if*、*in*、*for*、*return*、*switch*、*where*、*while*
<a name="literals"></a> * **用在表达式和类型中的关键字:** *as*、*dynamicType*、*is*、*new*、*super*、*self*、*Self*、*Type*、*\_\_COLUMN\_\_*、*\_\_FILE\_\_*、*\_\_FUNCTION\_\_*、*\_\_LINE\_\_*
## 字面量 * **用在模式中的关键字:** *\_*
* **特定上下文中被保留的关键字:** *associativity*、*didSet*、*get*、*infix*、*inout*、*left*、*mutating*、*none*、*nonmutating*、*operator*、*override*、*postfix*、*precedence*、*prefix*、*right*、*set*、*unowned*、*unowned(safe)*、*unowned(unsafe)*、*weak*、*willSet*,这些关键字在特定上下文之外可以被用于标识符。
字面值表示整型、浮点型数字或文本类型的值,举例如下:
以下标记被当作保留符号,不能用于自定义操作符:`(``)``{``}``[``]``.``,``:``;``=``@``#``&(作为前缀操作符)``->`、`` ``?``!(作为后缀操作符)`
```swift
42 // 整型字面量 <a id="literals"></a>
3.14159 // 浮点型字面量 ## 字面量
"Hello, world!" // 文本型字面量
``` 字面量是用来表示源码中某种特定类型的值,比如一个数字或字符串。
> 字面量语法 下面是字面量的一些示例:
> *字面量* → [*整型字面量*](LexicalStructure.html#integer_literal) | [*浮点数字面量*](LexicalStructure.html#floating_point_literal) | [*字符串字面量*](LexicalStructure.html#string_literal)
```swift
### 整型字面量 42 // 整型字面量
3.14159 // 浮点型字面量
整型字面量(*integer literals*)表示未指定精度整型数的值。整型字面量默认用十进制表示,可以加前缀来指定其他的进制,二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x` "Hello, world!" // 字符串型字面量
true // 布尔型字面量
十进制字面量包含数字 `0``9`。二进制字面量只包含 `0``1`,八进制字面量包含数字 `0``7`,十六进制字面量包含数字 `0``9` 以及字母 `A``F` (大小写均可)。 ```
负整数的字面量在数字前加减号 `-`,比如 `-42` 字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中Swift 使用了显式类型标注(`: Int8`)来推导出 `42` 这个整型字面量的类型是 `Int8`。如果没有可用的类型信息, Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整型字面量的默认类型是 `Int`,浮点型字面量的默认类型是 `Double`,字符串型字面量的默认类型是 `String`,布尔型字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"`的默认推导类型就是 `String`
允许使用下划线 `_` 来增加数字的可读性,下划线不会影响字面量的值。整型字面量也可以在数字前加 `0`,同样不会影响字面量的值 当为一个字面量值指定了类型标注的时候,这个注解的类型必须能通过这个字面量值实例化后得到。也就是说,这个类型必须遵守这些 Swift 标准库协议中的一个:整型字面量的`IntegerLiteralConvertible`协议、符点型字面量的`FloatingPointLiteralConvertible`协议、字符串字面量的`StringLiteralConvertible`协议以及布尔型字面量的`BooleanLiteralConvertible`协议。比如,`Int8` 遵守了 `IntegerLiteralConvertible`协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整型字面量 `42` 的类型标注
```swift > 字面量语法
1000_000 // 等于 1000000 > *字面量* → [*数字型字面量*](#numeric_literal) | [*字符串型字面量*](#string_literal) | [*布尔型字面量*](#boolean_literal) | [*nil型字面量*](#nil_literal)
005 // 等于 5 <a id="numeric_literal"></a>
``` > *数字型字面量* → -<sub>可选</sub>[*整型字面量*](#integer_literal) | -<sub>可选</sub>[*符点型字面量*](#floating_point_literal)
> *布尔型字面量* → **true** | **false**
除非特殊指定,整型字面量的默认类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数类型](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_411)。 > *nil型字面量* → **nil**
> 整型字面量语法 ### 整型字面量
> *整型字面量* → [*二进制字面量*](LexicalStructure.html#binary_literal)
> *整型字面量* → [*八进制字面量*](LexicalStructure.html#octal_literal) 整型字面量(*integer literals*)表示未指定精度整型数的值。整型字面量默认用十进制表示,可以加前缀来指定其他的进制,二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`
> *整型字面量* → [*十进制字面量*](LexicalStructure.html#decimal_literal)
> *整型字面量* → [*十六进制字面量*](LexicalStructure.html#hexadecimal_literal) 十进制字面量包含数字 `0``9`。二进制字面量只包含 `0``1`,八进制字面量包含数字 `0``7`,十六进制字面量包含数字 `0``9` 以及字母 `A``F` (大小写均可)。
> *二进制字面量* → **0b** [*二进制数字*](LexicalStructure.html#binary_digit) [*二进制字面量字符列表*](LexicalStructure.html#binary_literal_characters) _可选_
> *二进制数字* → 数值 0 到 1 负整数的字面量在整型字面量前加减号 `-`,比如 `-42`
> *二进制字面量字符* → [*二进制数字*](LexicalStructure.html#binary_digit) | **_**
> *二进制字面量字符列表* → [*二进制字面量字符*](LexicalStructure.html#binary_literal_character) [*二进制字面量字符列表*](LexicalStructure.html#binary_literal_characters) _可选_ 整型字面面可以使用下划线 `_` 来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。
> *八进制字面量* → **0o** [*八进字数字*](LexicalStructure.html#octal_digit) [*八进制字符列表*](LexicalStructure.html#octal_literal_characters) _可选_
> *八进字数字* → 数值 0 到 7 除非特别指定,整型字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数类型](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-ID323)。
> *八进制字符* → [*八进字数字*](LexicalStructure.html#octal_digit) | **_**
> *八进制字符列表* → [*八进制字符*](LexicalStructure.html#octal_literal_character) [*八进制字符列表*](LexicalStructure.html#octal_literal_characters) _可选_ > 整型字面量语法
> *十进制字面量* → [*进制数字*](LexicalStructure.html#decimal_digit) [*十进制字符列表*](LexicalStructure.html#decimal_literal_characters) _可选_ > *整型字面量* → [*进制字面量*](#binary_literal)
> *十进制数字* → 数值 0 到 9 > *整型字面量* → [*八进制字面量*](#octal_literal)
> *十进制数字列表* → [*十进制数字*](LexicalStructure.html#decimal_digit) [*十进制数字列表*](LexicalStructure.html#decimal_digits) _可选_ > *整型字面量* → [*十进制字面量*](#decimal_literal)
> *十进制字符* → [*十进制数字*](LexicalStructure.html#decimal_digit) | **_** > *整型字面量* → [*十进制字面量*](#hexadecimal_literal)
> *十进制字符列表* → [*十进制字符*](LexicalStructure.html#decimal_literal_character) [*十进制字符列表*](LexicalStructure.html#decimal_literal_characters) _可选_ <a id="binary_literal"></a>
> *十六进制字面量* → **0x** [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制字面量字符列表*](LexicalStructure.html#hexadecimal_literal_characters) _可选_ > *进制字面量* → **0b** [*进制数字*](#binary_digit) [*进制字面量字符*](#binary_literal_characters)<sub>可选</sub>
> *十六进制数字* → 数值 0 到 9, a through f, or A through F > *进制数字* → 数值 0 到 1
> *十六进制字符* → [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) | **_** > *二进制字面量字符* → [*进制数字*](#binary_digit) | _
> *十六进制字面量字符列表* → [*十六进制字符*](LexicalStructure.html#hexadecimal_literal_character) [*十六进制字面量字符列表*](LexicalStructure.html#hexadecimal_literal_characters) _可选_ > *进制字面量字符* → [*二进制字面量字符*](#binary_literal_character) [*进制字面量字符*](#binary_literal_characters)<sub>可选</sub>
<a id="octal_literal"></a>
### 浮点型字面量 > *八进制字面量* → **0o** [*八进字数字*](#octal_digit) [*八进制字符列表*](#octal_literal_characters)<sub>可选</sub>
> *八进字数字* → 数值 0 到 7
浮点型字面量(*floating-point literals*)表示未指定精度浮点数的值。 > *八进制字符* → [*八进字数字*](#octal_digit) | _
> *八进制字符组* → [*八进制字符*](#octal_literal_character) [*八进制字符列表*](#octal_literal_characters)<sub>可选</sub>
浮点型字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。 <a id="decimal_literal"></a>
> *十进制字面量* → [*十进制数字*](#decimal_digit) [*十进制字符组*](#decimal_literal_characters)<sub>可选</sub>
十进制浮点型字面量(*decimal floating-point literals*)由十进制数字串后跟小数部分或指数部分(或两者皆有)组成。十进制小数部分由小数点 `.` 后跟十进制数字串组成。指数部分由大写或小写字母 `e` 后跟十进制数字串组成,这串数字表示 `e` 之前的数量乘以 10 的几次方。例如:`1.25e2` 表示 `1.25 ⨉ 10^2`,也就是 `125.0`;同样,`1.25e2` 表示 `1.25 ⨉ 10^2`,也就是 `0.0125` > *十进制数字* → 数值 0 到 9
> *十进制数字列表* → [*十进制数字*](#decimal_digit) [*十进制数字列表*](#decimal_digits)<sub>可选</sub>
十六进制浮点型字面量(*hexadecimal floating-point literals*)由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 `15 ⨉ 2^2`,也就是 `60`;同样,`0xFp-2` 表示 `15 ⨉ 2^-2`,也就是 `3.75` > *十进制字符* → [*十进制数字*](#decimal_digit) | _
> *十进制字符列表* → [*十进制字符*](#decimal_literal_character) [*十进制字符列表*](#decimal_literal_characters)<sub>可选</sub>
与整型字面量不同,负的浮点型字面量由一元运算符减号 `-` 和浮点型字面量组成,例如 `-42.0`。这代表一个表达式,而不是一个浮点整型字面量。 <a id="hexadecimal_literal"></a>
> *十六进制字面量* → **0x** [*十六进制数字*](#hexadecimal_digit) [*十六进制字面量字符列表*](#hexadecimal_literal_characters)<sub>可选</sub>
允许使用下划线 `_` 来增强可读性,下划线不会影响字面量的值。浮点型字面量也可以在数字前加 `0`,同样不会影响字面量的值。 > *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F
> *十六进制字符* → [*十六进制数字*](#hexadecimal_digit) | _
```swift > *十六进制字面量字符列表* → [*十六进制字符*](#hexadecimal_literal_character) [*十六进制字面量字符列表*](#hexadecimal_literal_characters)<sub>可选</sub>
10_000.56 // 等于 10000.56
005000.76 // 等于 5000.76 ### 浮点型字面量
```
浮点型字面量(*floating-point literals*)表示未指定精度浮点数的值。
除非特殊指定,浮点型字面量的默认类型为 Swift 标准库类型中的 `Double`表示64位浮点数。Swift 标准库也定义 `Float` 类型表示32位浮点数。
浮点型字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。
> 浮点型字面量语法
> *浮点数字面量* → [*十进制字面量*](LexicalStructure.html#decimal_literal) [*十进制分数*](LexicalStructure.html#decimal_fraction) _可选_ [*十进制指数*](LexicalStructure.html#decimal_exponent) _可选_ 十进制浮点型字面量(*decimal floating-point literals*)由十进制数字串后跟小数部分或指数部分(或两者皆有)组成。十进制小数部分由小数点 `.` 后跟十进制数字串组成。指数部分由大写或小写字母 `e` 为前缀后跟十进制数字串组成,这串数字表示 `e` 之前的数量乘以 10 的几次方。例如:`1.25e2` 表示 `1.25 ⨉ 10^2`,也就是 `125.0`;同样,`1.25e2` 表示 `1.25 ⨉ 10^2`,也就是 `0.0125`
> *浮点数字面量* → [*十六进制字面量*](LexicalStructure.html#hexadecimal_literal) [*十六进制分数*](LexicalStructure.html#hexadecimal_fraction) _可选_ [*十六进制指数*](LexicalStructure.html#hexadecimal_exponent)
> *十进制分数* → **.** [*十进制字面量*](LexicalStructure.html#decimal_literal) 十六进制浮点型字面量(*hexadecimal floating-point literals*)由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 为前缀后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 `15 ⨉ 2^2`,也就是 `60`;同样,`0xFp-2` 表示 `15 ⨉ 2^-2`,也就是 `3.75`
> *十进制指数* → [*浮点数e*](LexicalStructure.html#floating_point_e) [*正负号*](LexicalStructure.html#sign) _可选_ [*十进制字面量*](LexicalStructure.html#decimal_literal)
> *十六进制分数* → **.** [*十六进制字面量*](LexicalStructure.html#hexadecimal_literal) _可选_ 负的浮点型字面量由一元运算符减号 `-` 和浮点型字面量组成,例如 `-42.5`
> *十六进制指数* → [*浮点数p*](LexicalStructure.html#floating_point_p) [*正负号*](LexicalStructure.html#sign) _可选_ [*十六进制字面量*](LexicalStructure.html#hexadecimal_literal)
> *浮点数e* → **e** | **E** 浮点型字面量允许使用下划线 `_` 来增强数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。
> *浮点数p* → **p** | **P**
> *正负号* → **+** | **-** 除非特别指定,浮点型字面量的默认推导类型为 Swift 标准库类型中的 `Double`表示64位浮点数。Swift 标准库也定义了 `Float` 类型表示32位浮点数。
### 文本型字面量 > 浮点型字面量语法
> *浮点数字面量* → [*十进制字面量*](#decimal_literal) [*十进制分数*](#decimal_fraction)<sub>可选</sub> [*十进制指数*](#decimal_exponent)<sub>可选</sub>
文本型字面量(*string literal*)由双引号中的字符串组成,形式如下: > *浮点数字面量* → [*十六进制字面量*](#hexadecimal_literal) [*十六进制分数*](#hexadecimal_fraction)<sub>可选</sub> [*十六进制指数*](#hexadecimal_exponent)
<a id="decimal_fraction"></a>
```swift > *十进制分数* → **.** [*十进制字面量*](#decimal_literal)
"characters" > *十进制指数* → [*浮点数e*](#floating_point_e) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal_literal)
``` <a id="hexadecimal_literal"></a>
> *十六进制分数* → **.** [*十六进制数字*](#hexadecimal_digit) [*十六进制字面量字符列表*](#hexadecimal_literal_characters)<sub>可选</sub>
文本型字面量中不能包含未转义的双引号 `"`、未转义的反斜线`\`、回车符(*carriage return*)或换行符(*line feed*)。 > *十六进制指数* → [*浮点数p*](#floating_point_p) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal_literal)
<a id="floating_point_e"></a>
可以在文本型字面量中使用的转义特殊符号如下: > *浮点数e* → **e** | **E**
> *浮点数p* → **p** | **P**
* 空字符Null Character`\0` > *正负号* → **+** | **-**
* 反斜线Backslash`\\`
* 水平 Tab Horizontal Tab`\t`
* 换行符Line Feed`\n` ### 字符串型字面量
* 回车符Carriage Return`\r`
* 双引号Double Quote`\"` 字符串型字面量(*string literal*)由被包在双引号中的一串字符组成,形式如下:
* 单引号Single Quote`\'`
```
字符也可以用以下方式表示: "characters"
```
* `\x` 后跟两位十六进制数字
* `\u` 后跟四位十六进制数字 字符串型字面量中不能包含未转义的双引号 `"`)、未转义的反斜线(`\`)、回车符(*carriage return*)或换行符(*line feed*)。
* `\U` 后跟八位十六进制数字
可以在字符串字面量中使用的转义特殊符号如下:
后跟的数字表示一个 Unicode 码点。
* 空字符Null Character`\0`
文本型字面量允许在反斜线小括号 `\()` 中插入表达式的值。插入表达式(*interpolated expression*)不能包含未转义的双引号 `"`、反斜线 `\`、回车符或者换行符。表达式值的类型必须在 *String* 类中有对应的初始化方法。 * 反斜线Backslash`\\`
* 水平制表符Horizontal Tab`\t`
例如,以下所有文本型字面量的值相同: * 换行符Line Feed`\n`
* 回车符Carriage Return`\r`
```swift * 双引号Double Quote`\"`
"1 2 3" * 单引号Single Quote`\'`
"1 2 \(3)" * Unicode标量 `\u{n}`n为一到八位的十六进制数字
"1 2 \(1 + 2)"
var x = 3; "1 2 \(x)" 字符串字面量允许在反斜杠小括号 `\()` 中插入表达式的值。插入表达式(*interpolated expression*)不能包含未转义的双引号 `"`、未转义的反斜线 `\`、回车符或者换行符。表达式结果的类型必须在 *String* 类中有对应的初始化方法。
```
例如,以下所有字符串字面量的值都是相同的:
文本型字面量的默认类型为 `String`。组成字符串的字符类型为 `Character`。更多有关 `String``Character` 的信息请参照 [字符串和字符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_368)。
```
> 字符型字面量语法 "1 2 3"
> *字符串字面量* → **"** [*引用文本*](LexicalStructure.html#quoted_text) **"** "1 2 \(3)"
> *引用文本* → [*引用文本条目*](LexicalStructure.html#quoted_text_item) [*引用文本*](LexicalStructure.html#quoted_text) _可选_ "1 2 \(1 + 2)"
> *引用文本条目* → [*转义字符*](LexicalStructure.html#escaped_character) let x = 3; "1 2 \(x)"
> *引用文本条目* → **\(** [*表达式*](..\chapter3\04_Expressions.html#expression) **)** ```
> *引用文本条目* → 除了"­, \­, U+000A, or U+000D的所有Unicode的字符
> *转义字符* → **\0** | **\\** | **\t** | **\n** | **\r** | **\"** | **\'** 字符串字面量的默认推导类型为 `String`。组成字符串的字符默认推导类型为 `Character`。更多有关 `String``Character` 的信息请参照 [字符串和字符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_368)。
> *转义字符* → **\x** [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit)
> *转义字符* → **\u** [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) > 字符型字面量语法
> *转义字符* → **\U** [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) > *字符串字面量* → **"** [*引用文本*](#quoted_text)<sub>可选</sub> **"**
<a id="quoted_text"></a>
<a name="operators"></a> > *引用文本* → [*引用文本条目*](#quoted_text_item) [*引用文本*](#quoted_text) <sub>可选</sub>
## 运算符 > *引用文本条目* → [*转义字符*](#escaped_character)
> *引用文本条目* → **\(** [*表达式*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID383) **)**
Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_70) 和 [高级运算符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_28) 中进行了阐述。这里将描述哪些字符能用作运算符。 > *引用文本条目* → **除了"­, \­, U+000A, 或者 U+000D的所有Unicode的字符**
> *转义字符* → **\0** | **\\** | **\t** | **\n** | **\r** | **\"** | **\'**
运算符由一个或多个以下字符组成: > *转义字符* → **\u {** [*unicode标量数字*](#unicode_scalar_digits) **}**
`/``=``-``+``!``*``%``<``>``&``|``^``~``.`。也就是说,标记 `=`, `->``//``/*``*/``.` 以及一元前缀运算符 `&` 属于保留字,这些标记不能被重写或用于自定义运算符。 > *unicode标量数字* → 一到八位的十六进制数字
运算符两侧的空白被用来区分该运算符是否为前缀运算符(*prefix operator*)、后缀运算符(*postfix operator*)或二元运算符(*binary operator*)。规则总结如下: <a id="operators"></a>
## 运算符
* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+b``a + b` 中的运算符 `+` 被看作二元运算符。
* 如果运算符只有左侧空白,将被看作前缀一元运算符。例如 `a ++b` 中的 `++` 被看作前缀一元运算符。 Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_70) 和 [高级运算符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_28) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。
* 如果运算符只有右侧空白,将被看作后缀一元运算符。例如 `a++ b` 中的 `++` 被看作后缀一元运算符。
* 如果运算符左侧没有空白并紧跟 `.`,将被看作后缀一元运算符。例如 `a++.b` 中的 `++` 被看作后缀一元运算符(同理, `a++ . b` 中的 `++` 是后缀一元运算符而 `a ++ .b` 中的 `++` 不是). 自定义运算符可以由以下其中之一的 ASCII 字符 `/``=``-``+``!``*``%``<``>``&``|``^``?` 以及 `~`, 或者后面语法中规定的任一个 Unicode 字符开始。在第一个字符之后,允许使用组合型 Unicode 字符。也可以使用两个或者多个的点号来自定义运算符(比如, `....`)。虽然可以自定义包含问号`?`的运算符,但是这个运算符不能只包含单独的一个问号。
鉴于这些规则,运算符前的字符 `(``[``{` ;运算符后的字符 `)``]``}` 以及字符 `,``;``:` 都将用于空白检测。 注意:
以下这些标记 =, ->, //, /*, */, ., <(前缀运算符), &, and ?, ?(中缀运算符), >(后缀运算符), ! 以及 ? 是被系统保留的。这些标记不能被重载,也不能用于自定义操作符。
以上规则需注意一点,如果运算符 `!``?` 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将 `?` 用作可选类型(*optional type*)修饰,左侧必须无空白。如果用于条件运算符 `? :`,必须两侧都有空白。
运算符两侧的空白被用来区分该运算符是否为前缀运算符(*prefix operator*)、后缀运算符(*postfix operator*)或二元运算符(*binary operator*)。规则总结如下:
在特定构成中 ,以 `<``>` 开头的运算符会被分离成两个或多个标记,剩余部分以同样的方式会被再次分离。因此,在 `Dictionary<String, Array<Int>>` 中没有必要添加空白来消除闭合字符 `>` 的歧义。在这个例子中, 闭合字符 `>` 被看作单字符标记,而不会被误解为移位运算符 `>>`
* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+b``a + b` 中的运算符 `+` 被看作二元运算符。
要学习如何自定义新的运算符,请参考 [自定义操作符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_48) 和 [运算符声明](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-XID_644)。学习如何重写现有运算符,请参考 [运算符方法](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_43) * 如果运算符只有左侧空白,将被看作前缀一元运算符。例如 `a ++b` 中的 `++` 被看作前缀一元运算符
* 如果运算符只有右侧空白,将被看作后缀一元运算符。例如 `a++ b` 中的 `++` 被看作后缀一元运算符。
> 运算符语法语法 * 如果运算符左侧没有空白并紧跟 `.`,将被看作后缀一元运算符。例如 `a++.b` 中的 `++` 被看作后缀一元运算符(即上式被视为 `a++ .b` 而非 `a ++ .b`)。
> *运算符* → [*运算符字符*](LexicalStructure.html#operator_character) [*运算符*](LexicalStructure.html#operator) _可选_
> *运算符字符* → **/** | **=** | **-** | **+** | **!** | **&#42;** | **%** | **<** | **>** | **&** | **|** | **^** | **~** | **.** 鉴于这些规则,运算符前的字符 `(``[``{` ;运算符后的字符 `)``]``}` 以及字符 `,``;``:` 都被视为空白。
> *二元运算符* → [*运算符*](LexicalStructure.html#operator)
> *前置运算符* → [*运算符*](LexicalStructure.html#operator) 以上规则需注意一点,如果预定义运算符 `!``?` 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将 `?` 用作可选链(*optional-chaining*)操作符,左侧必须无空白。如果用于条件运算符 `? :`,必须两侧都有空白。
> *后置运算符* → [*运算符*](LexicalStructure.html#operator)
在某些特定的构造中 ,以 `<``>` 开头的运算符会被分离成两个或多个标记,剩余部分以同样的方式会被再次分离。因此,在 `Dictionary<String, Array<Int>>` 中没有必要添加空白来消除闭合字符 `>` 的歧义。在这个例子中, 闭合字符 `>` 不会被视为单独的标记,因而不会被误解析为 `>>` 运算符的一部分。
要学习如何自定义运算符,请参考 [自定义操作符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_48) 和 [运算符声明](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-XID_644)。要学习如何重载运算符,请参考 [运算符方法](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_43)。
> 运算符语法语法
> *运算符* → [*头部运算符*](#operator_head) [*运算符字符组*](#operator_characters)<sub>可选</sub>
> *运算符* → [*头部点运算符*](#dot_operator_head) [*点运算符字符组*](#dot_operator_characters)<sub>可选</sub>
<a id="operator_head"></a>
> *头部运算符* → **/** | **=** | **+** | **!** |**\*** | **%** |**<** | **>** |**&** | **|** |**/** | **~** | **?** |
> *头部运算符* → U+00A1U+00A7
> *头部运算符* → U+00A9 or U+00AB
> *头部运算符* → U+00AC or U+00AE
> *头部运算符* → U+00B0U+00B1, U+00B6, U+00BB, U+00BF, U+00D7, or U+00F7
> *头部运算符* → U+2016U+2017 or U+2020U+2027
> *头部运算符* → U+2030U+203E
> *头部运算符* → U+2041U+2053
> *头部运算符* → U+2055U+205E
> *头部运算符* → U+2190U+23FF
> *头部运算符* → U+2500U+2775
> *头部运算符* → U+2794U+2BFF
> *头部运算符* → U+2E00U+2E7F
> *头部运算符* → U+3001U+3003
> *头部运算符* → U+3008U+3030
<a id="operator_character"></a>
> *运算符字符* → [*头部运算符*](#operator_head)
> *运算符字符* → U+0300U+036F
> *运算符字符* → U+1DC0U+1DFF
> *运算符字符* → U+20D0U+20FF
> *运算符字符* → U+FE00U+FE0F
> *运算符字符* → U+FE20U+FE2F
> *运算符字符* → U+E0100U+E01EF
<a id="operator_characters"></a>
> *运算符字符组* → [*运算符字符*](#operator_character) [*运算符字符组*] (#operator_characters)<sub>可选</sub>
<a id="dot_operator_head"></a>
> *头部点运算符* → **..**
> *头部点运算符字符* → . | [*运算符字符*](#operator_character)
> *头部点运算符字符组* → [*点运算符字符*](#dot_operator_character) [*点运算符字符组*](#dot_operator_characters)<sub>可选</sub>
> *二元运算符* → [*运算符*](#operator)
> *前置运算符* → [*运算符*](#operator)
> *后置运算符* → [*运算符*](#operator)

View File

@ -1,5 +1,5 @@
> 翻译:[Hawstein](https://github.com/Hawstein) > 翻译:[Hawstein](https://github.com/Hawstein)
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai) > 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai), [KYawn](https://github.com/KYawn)
# 特性 # 特性
----------------- -----------------
@ -9,7 +9,7 @@
- [声明特性](#declaration_attributes) - [声明特性](#declaration_attributes)
- [类型特性](#type_attributes) - [类型特性](#type_attributes)
特性提供了关于声明和类型的更多信息。在Swift中有两类特性用于修饰声明的以及用于修饰类型的。例如,`required`特性,当应用于一个类的指定或便利初始化器声明时,表明它的每个子类都必须实现那个初始化器。再比如`noreturn`特性,当应用于函数或方法类型时,表明该函数或方法不会返回到它的调用者。 特性提供了关于声明和类型的更多信息。在Swift中有两类特性用于修饰声明的以及用于修饰类型的。
通过以下方式指定一个特性:符号`@`后面跟特性名,如果包含参数,则把参数带上: 通过以下方式指定一个特性:符号`@`后面跟特性名,如果包含参数,则把参数带上:
@ -23,30 +23,44 @@
声明特性只能应用于声明。然而,你也可以将`noreturn`特性应用于函数或方法类型。 声明特性只能应用于声明。然而,你也可以将`noreturn`特性应用于函数或方法类型。
`availability` `autoclosure`
这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以声明返回表达式自身类型的没有参数的方法类型,也可以用于函数参数的声明。含有`autoclosure`特性的声明同时也具有`noescape`的特性,除非传递可选参数`escaping`.关于怎样使用`autoclosure`特性的例子,参见[函数类型](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Types.html#//apple_ref/doc/uid/TP40014097-CH31-ID449).
`availability`特性用于声明时,将表示该声明的生命周期会依赖于特定的平台和操作系统版本。 `available`
`availability`特性总会与参数列表一同出现该参数列表至少有两个参数参数之间由逗号分隔。第一个参数由以下这些平台名字中的一个起头iOS, iOSApplicationExtension, OSX, or OSXApplicationExtension。当然你也可以用一个星号(*)来表示,该声明在上面提到的所有平台上都是有效的。剩下的参数,可以以任何顺序出现,并且可以附加关于声明生命周期的附加信息,包括重要的里程碑。
- `unavailable`参数表示该声明在特定的平台上是无效的 `available`特性用于声明时,意味着该声明的生命周期会依赖于特定的平台和操作系统版本。
`available`特性经常与参数列表一同出现,该参数列表至少有两个参数,参数之间由逗号分隔。这些参数由以下这些平台名字中的一个起头:
- `introduced`参数表示:特定的平台上,该声明被使用的第一个版本。格式如下:<p>`introduced=version number`<p>这里的`version number`由一个正的十进制整数或浮点数构成。 - `iOS`
- `iOSApplicationExtension`
- `OSX`
- `OSXApplicationExtension`
- `watchOS`
- `deprecated`参数表示:特定的平台上,该声明被建议弃用的第一个版本。格式如下: 当然,你也可以用一个星号(*)来表示,该声明在上面提到的所有平台上都是有效的。
剩下的参数,可以以任何顺序出现,并且可以添加关于声明生命周期的附加信息,包括重要的里程碑。
- `unavailable`参数表示:该声明在特定的平台上是无效的
- `introduced`参数表示:该声明第一次被引入时所在平台的版本。格式如下:
<p>`introduced=version number`<p>这里的`version number`由一个正的十进制整数或浮点数构成。
- `deprecated`参数表示:该声明第一次被建议弃用时所在平台的版本。格式如下:
<p>`deprecated=version number`<p>这里的`version number`由一个正的十进制整数或浮点数构成。 <p>`deprecated=version number`<p>这里的`version number`由一个正的十进制整数或浮点数构成。
- `obsoleted`参数表示:特定的平台上,该声明被弃用的第一个版本。格式如下: - `obsoleted`参数表示:该声明第一次被弃用时所在平台的版本。当一个声明被弃用时,它就从此平台中被移除,不能再被使用。格式如下:
<p>`deprecated=version number`<p>这里的`version number`由一个正的十进制整数或浮点数构成。 <p>`obsoleted=version number`<p>这里的`version number`由一个正的十进制整数或浮点数构成。
- `message`参数用来提供文本信息,并在因使用建议弃用或者被弃用的声明而遇到警告或错误时,编译器抛出。格式如下: - `message`参数用来提供文本信息。当使用建议弃用或者被弃用的声明时,编译器抛出错误或警告信息。格式如下:
<p>`message=message`<p>这里的`message`由一个字符串文字构成。 <p>`message=message`<p>这里的`message`由一个字符串文字构成。
- `renamed`参数用来提供文本信息,用以表示被重命名的声明的新名字。当使用这个重命名的声明遇到错误时,该新名字会被编译器显示出。格式如下: - `renamed`参数用来提供文本信息,用以表示被重命名的声明的新名字。当使用这个重命名的声明遇到错误时,编译器显示出该新名字。格式如下:
<p>`renamed=new name`<p>这里的`new name`由一个字符串文字构成。 <p>`renamed=new name`<p>这里的`new name`由一个字符串文字构成。
你可以将`renamed`参数和`unavailable`参数以及类型别名声明组合使用,以向用户表示:在你的代码中,一个声明已经被重命名。当一个声明的名字在一个框架或者库的不同发布版本间发生变化时,这会相当用。 你可以将`renamed`参数和`unavailable`参数以及类型别名声明组合使用,以向用户表示:在你的代码中,一个声明已经被重命名。当一个声明的名字在一个框架或者库的不同发布版本间发生变化时,这会相当用。
```swift ```swift
// First release // First release
@ -58,50 +72,34 @@ protocol MyRenamedProtocol {
// protocol definition // protocol definition
} }
@availability(*, unavailable, renamed="MyRenamedProtocol") @available(*, unavailable, renamed="MyRenamedProtocol")
typealias MyProtocol = MyRenamedProtocol typealias MyProtocol = MyRenamedProtocol
``` ```
你可以在一个单独的声明上使用多个`availability`特性,以详细说明该声明在不同平台上的有效性。编译器只有在当前的目标平台和`availability`特性中指定的平台匹配时,才会使用`availability`特性 你可以在一个单独的声明上使用多个`available`特性,以详细说明该声明在不同平台上的有效性。编译器只有在当前的目标平台和`available`特性中指定的平台匹配时,才会使用`available`特性
`autoclosure` 如果`available`特性除了平台名称参数外,只指定了一个`introduced `参数,那么可以使用以下简写语法代替:
这个属性通过把表达式自动封装成不带参数的闭包来延迟表达式的计算。这个属性使用在函数参数声明或者不带参数但是返回表达式类型的方法类型上。含有```autoclosure```属性的声明同时也具有```noescape```的特性,除非传递一个可选的参数属性```escaping```,请看[函数类型](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Types.html#//apple_ref/doc/uid/TP40014097-CH31-ID449)。 @available(`platform name` `version number`, *)
`noescape` `available`特性的简写语法可以简明地表达出多个平台的可用性。尽管这两种形式在功能上是相同的,但请尽可能地使用简明语法形式。
```swift
在函数或者方法声明上使用该属性表示参数将不会被存储用作后续的计算,其用来确保不会超出函数调用的声明周期。使用```noescape```声明属性的函数类型不需要显式的使用```self```,对于其属性或者方法来说。 @available(iOS 8.0, OSX 10.10, *)
class MyClass {
`noreturn` // class definition
}
该特性用于修饰函数或方法声明,表明该函数或方法的对应类型,`T`,是`@noreturn T`。你可以用这个特性修饰函数或方法的类型,这样一来,函数或方法就不会返回到它的调用者中去。 ```
对于一个没有用`noreturn`特性标记的函数或方法,你可以将它重写(override)为用该特性标记的。相反,对于一个已经用`noreturn`特性标记的函数或方法你则不可以将它重写为没使用该特性标记的。相同的规则试用于当你在一个comforming类型中实现一个协议方法时。
`NSApplicationMain`
在类上使用该属性表示该类是应用程序委托类,使用该属性与调用```NSApplicationMain```函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个属性,可以提供一个```main.swift```文件,并且提供一个```main```函数去调用```NSApplicationMain```函数。比如,如果你的应用程序使用一个派生于```NSApplication```的自定义子类作为主要类,你可以调用```NSApplicationMain```函数而不是使用该属性。
`NSCopying`
该特性用于修饰一个类的存储型变量属性。该特性将使属性的setter与属性值的一个副本合成由`copyWithZone`方法返回,而不是属性本身的值。该属性的类型必需遵循`NSCopying`协议。
`NSCopying`特性的行为与Objective-C中的`copy`特性相似。
`NSManaged`
该特性用于修饰`NSManagedObject`子类中的存储型变量属性表明属性的存储和实现由Core Data在运行时基于相关实体描述动态提供。
`objc` `objc`
该特性用于修饰任可以在Objective-C中表示的声明比如,非嵌套类协议类和协议的属性和方法(包gettersetter),初始化器,析构器以及下标。`objc`特性告诉编译器声明可以在Objective-C代码中使用。 该特性用于修饰任可以在Objective-C中表示的声明比如,非嵌套类协议、非泛型枚举(仅限整型值类型)、类和协议的属性和方法(包`getter``setter`)、构造器、析构器以及下标。`objc`特性告诉编译器这个声明可以在Objective-C代码中使用。
如果你将`objc`特性应用于一个类或协议,它也会隐式地应用于那个类或协议的成员。对于标记了`objc`特性的类,编译器会隐式地为它的子类添加`objc`特性。标记了`objc`特性的协议不能继承没有标记`objc`的协议。 如果你将`objc`特性应用于一个类或协议,它也会隐式地应用于那个类的成员或协议。对于标记了`objc`特性的类,编译器会隐式地为它的子类添加`objc`特性。标记了`objc`特性的协议不能继承没有标记`objc`的协议。
`objc`特性有一个可选的参数,由标记符组成。当你想把`objc`所修饰的实体以一个不同的名字暴露Objective-C你就可以使用这个特性参数。你可以使用这个参数来命名类协议方法getterssetters以及初始化器。下面的例子把`ExampleClass`中`enabled`属性的getter暴露Objective-C,名字是`isEnabled`,而不是它原来的属性名 如果你将`objc`特性应用于枚举,每一个枚举的`case`都会以枚举名称和`case`名称组合的方式暴露Objective-C代码中。例如:一个名为`Venus``case``Planet`枚举中,这个`case`暴露Objective-C代码中时叫做`PlanetVenus`
`objc`特性有一个可选的参数,由标记符组成。当你想把`objc`所修饰的实体以一个不同的名字暴露给Objective-C时你就可以使用这个特性参数。你可以使用这个参数来命名类协议方法getterssetters以及构造器。下面的例子把`ExampleClass``enabled`属性的getter暴露给Objective-C名字是`isEnabled`,而不是它原来的属性名。
```swift ```swift
@objc @objc
@ -114,17 +112,64 @@ class ExampleClass {
} }
``` ```
`optional` `noescape`
用该特性修饰协议的属性方法或下标成员表示实现这些成员并不需要一致性类型conforming type 在函数或者方法声明上使用该特性,它表示参数将不会被存储用作后续的计算,其用来确保不会超出函数调用的生命周期。对于其属性方法来说,使用`noescape`声明属性的函数类型不需要显式的使用`self.`
你只能用`optional`特性修饰那些标记了`objc`特性的协议。因此只有类类型可以adopt和comform to那些包含可选成员需求的协议。更多关于如何使用`optional`特性以及如何访问可选协议成员的指导例如当你不确定一个conforming类型是否实现了它们请见[可选协议需求]()。 `nonobjc`
`required` 该特性用于方法、属性、下标、或构造器的声明这些声明本是可以在Objective-C代码中表示的。使用`nonobjc`特性告诉编译器这个声明不能在Objective-C代码中使用。
用该特性修饰一个类的指定或便利初始化器,表示该类的所有子类都必需实现该初始化器 可以使用`nonobjc`特性解决标有`objc`的类中桥接方法的循环问题,该特性还允许标有`objc`的类的构造器和方法进行重载(overload)
加了该特性的指定初始化器必需显式地实现,而便利初始化器既可显式地实现,也可以在子类实现了超类所有指定初始化器后继承而来(或者当子类使用便利初始化器重写了指定初始化器) 标有`nonobjc`特性的方法不能重写(override)一个标有`objc`特性的方法。然而,标有`objc`特性的方法可以重写标有`nonobjc`特性的方法。同样,标有`nonobjc`特性的方法不能满足一个需要标有`@objc`特性的方法的协议
`noreturn`
该特性用于修饰函数或方法声明,表明该函数或方法的对应类型,`T`,是`@noreturn T`。你可以用这个特性修饰函数或方法的类型,这样一来,函数或方法就不会返回到它的调用者中去。
对于一个没有用`noreturn`特性标记的函数或方法,你可以将它重写为用该特性标记的。相反,对于一个已经用`noreturn`特性标记的函数或方法你则不可以将它重写为没使用该特性标记的。当你在一个comforming类型中实现一个协议方法时该规则同样适用。
`NSApplicationMain`
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用`NSApplicationMain(_:_:)`函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个特性,可以提供一个`main.swift`文件,并且提供一个`main`函数去调用`NSApplicationMain(_:_:)`函数。比如,如果你的应用程序使用一个派生于`NSApplication`的自定义子类作为主要类,你可以调用`NSApplicationMain`函数而不是使用该特性。
`NSCopying`
该特性用于修饰一个类的存储型变量属性。该特性将使属性的setter与属性值的一个副本合成这个值由`copyWithZone(_:)`方法返回,而不是属性本身的值。该属性的类型必需遵循`NSCopying`协议。
`NSCopying`特性的原理与Objective-C中的`copy`特性相似。
`NSManaged`
该特性用于修饰`NSManagedObject`子类中的存储型变量属性表明属性的存储和实现由Core Data在运行时基于相关实体描述动态提供。
`testable`
该特性用于`import`声明可以测试的编译模块,它能访问任何标有`internal`权限标识符的实体,这和将它声明为`public`权限标识符有同样的效果。
`UIApplicationMain`
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用`UIApplicationMain(_:_:)`函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个特性,可以提供一个`main.swift`文件,并且提供一个`main`函数去调用`UIApplicationMain(_:_:)`函数。比如,如果你的应用程序使用一个派生于`UIApplication`的自定义子类作为主要类,你可以调用`UIApplicationMain`函数而不是使用该特性。
`warn_unused_result`
该特性应用于方法或函数声明,当方法或函数被调用,但其结果未被使用时,该特性会让编译器会产生警告。
你可以使用这个特性提供一个警告信息,这个警告信息是关于不正确地使用未变异的方法的,这个方法也有一个对应的变异方法。
`warn_unused_result`有下面两个可选的参数。
- `message`参数用来提供警告信息,并在因当方法或函数被调用,但其结果未被使用时,显示警告信息。格式如下:
<p>`message=message`<p>这里的`message`由一个字符串文字构成。
- `mutable_variant`参数用于提供变异方法的名称,如果未变异方法以一个可变的值被调用而且其结果并未被使用时,应该使用此变异方法。格式如下(方法名有字符串构成):<p>`mutable_variant=method name`<p>
比如Swift标准库提供了变异方法`sortInPlace()`和未变异方法`sort()`集合,它们的元素生成器符合`Comparable`协议。如果你调用了`sort()`方法,而没有使用它的结果,很有可能,你打算使用变异方法`sortInPlace()`替代。
### Interface Builder使用的声明特性 ### Interface Builder使用的声明特性
@ -137,22 +182,36 @@ Interface Builder特性是Interface Builder用来与Xcode同步的声明特性
类型特性只能用于修饰类型。然而,你也可以用`noreturn`特性去修饰函数或方法声明。 类型特性只能用于修饰类型。然而,你也可以用`noreturn`特性去修饰函数或方法声明。
`auto_closure` `convention`
这个特性通过自动地将表达式封闭到一个无参数闭包中来延迟表达式的求值。使用该特性修饰无参的函数或方法类型,返回表达式的类型。一个如何使用`auto_closure`特性的例子,见[函数类型]() 该特性用于函数的类型,它指出函数调用的约定。
`convention`特性有下面几个可选的参数。
- `swift`参数用于表明一个Swift函数引用。这是Swift中标准的函数值调用约定。
- `block`参数用于表明一个Objective-C兼容的块引用。函数值表示为一个块对象的引用这是一个`id-`兼容的Objective-C对象对象中嵌入了调用函数。调用函数使用C的调用约定。
- `c`参数用于表明一个C函数引用。函数值没有上下文这个函数也使用C的调用约定。
使用C函数调用约定的函数也可用作使用Objective-C块调用约定的函数同时使用Objective-C块调用约定的函数也可用作使用Swift函数调用约定的函数。然而只有非泛型的全局函数和本地函数或者不使用任何本地变量的闭包可以被用作使用C函数调用约定的函数。
`noreturn` `noreturn`
该特性用于修饰函数或方法的类型,表明该函数或方法不会返回到它的调用者中去。你也可以用它标记函数或方法的声明,表示函数或方法的相应类型,`T`,是`@noreturn T` 该特性用于修饰函数或方法的类型,表明该函数或方法不会返回到它的调用者中去。你也可以用它标记函数或方法的声明,表示函数或方法的相应类型,`T`,是`@noreturn T`
> 特性语法 > 特性语法
> *特性* → **@** [*特性名*](..\chapter3\06_Attributes.html#attribute_name) [*特性参数子句*](..\chapter3\06_Attributes.html#attribute_argument_clause) _可选_ > *特性* → **@** [*特性名*](#attribute_name) [*特性参数子句*](#attribute_argument_clause) <sub>_可选_</sub>
> *特性名* → [*标识符*](LexicalStructure.html#identifier) > *特性名* → [*标识符*](02_Lexical_Structure.html#identifiers)
> *特性参数子句* → **(** [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_ **)** > *特性参数子句* → **(** [*平衡令牌列表*](#balanced_tokens) <sub>_可选_</sub> **)**
> *特性(Attributes)列表* → [*特色*](..\chapter3\06_Attributes.html#attribute) [*特性(Attributes)列表*](..\chapter3\06_Attributes.html#attributes) _可选_ > *特性(Attributes)列表* → [*特色*](#attribute) [*特性(Attributes)列表*](#attributes) <sub>_可选_</sub>
> *平衡令牌列表* → [*平衡令牌*](..\chapter3\06_Attributes.html#balanced_token) [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_ > *平衡令牌列表* → [*平衡令牌*](#balanced_token) [*平衡令牌列表*](#balanced_tokens) <sub>_可选_</sub>
> *平衡令牌* → **(** [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_ **)** > *平衡令牌* → **(** [*平衡令牌列表*](#balanced_tokens) <sub>_可选_</sub> **)**
> *平衡令牌* → **[** [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_ **]** > *平衡令牌* → **[** [*平衡令牌列表*](#balanced_tokens) <sub>_可选_</sub> **]**
> *平衡令牌* → **{** [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_ **}** > *平衡令牌* → **{** [*平衡令牌列表*](#balanced_tokens) <sub>_可选_</sub> **}**
> *平衡令牌* → **任意标识符, 关键字, 字面量或运算符** > *平衡令牌* → **任意标识符, 关键字, 字面量或运算符**
> *平衡令牌* → **任意标点除了(, ), [, ], {, 或 }** > *平衡令牌* → **任意标点除了(, ), [, ], {, 或 }**

View File

@ -1,4 +1,4 @@
> 翻译:[honghaoz](https://github.com/honghaoz) > 翻译:[honghaoz](https://github.com/honghaoz), [ray16897188](https://github.com/ray16897188)
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai) > 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
# 模式Patterns # 模式Patterns
@ -11,28 +11,32 @@
- [值绑定模式Value-Binding Pattern](#value-binding_pattern) - [值绑定模式Value-Binding Pattern](#value-binding_pattern)
- [元组模式Tuple Pattern](#tuple_pattern) - [元组模式Tuple Pattern](#tuple_pattern)
- [枚举用例模式Enumeration Case Pattern](#enumeration_case_pattern) - [枚举用例模式Enumeration Case Pattern](#enumeration_case_pattern)
- [类型转换模式Type-Casting Patterns](#type-casting_patterns) - [可选模式Optional Pattern](#optional_pattern)
- [类型转换模式Type-Casting Pattern](#type-casting_pattern)
- [表达式模式Expression Pattern](#expression_pattern) - [表达式模式Expression Pattern](#expression_pattern)
模式pattern代表了单个值或者复合值的结构。例如元组`(1, 2)`的结构是逗号分隔的,包含两个元素的列表。因为模式代表一种值的结构,而不是特定的某个值,你可以把模式和各种同类型的值匹配起来。比如,`(x, y)`可以匹配元组`(1, 2)`,以及任何含两个元素的元组。除了将模式与一个值匹配外,你可以从合值中提取出部分或全部,然后分别把各个部分和一个常量或变量绑定起来。 模式pattern代表了单个值或者复合值的结构。例如元组`(1, 2)`的结构是逗号分隔的,包含两个元素的列表。因为模式代表一种值的结构,而不是特定的某个值,你可以把模式和各种同类型的值匹配起来。比如,`(x, y)`可以匹配元组`(1, 2)`,以及任何含两个元素的元组。除了将模式与一个值匹配外,你可以从合值中提取出部分或全部,然后分别把各个部分和一个常量或变量绑定起来。
在Swift中,模式出现在变量和常量的声明(在它们的左侧),`for-in`语句和`switch`语句在它们的case标签中。尽管任何模式都可以出现在`switch`语句的case标签中但在其他情况下只有通配符模式wildcard pattern标识符模式identifier pattern和包含这两种模式的模式才能出现 swift语言中模式有2个基本的分类一类能成功和任何值的类型相匹配另一类在运行时runtime和某特定值匹配时可能会失败
你可以为通配符模式wildcard pattern标识符模式identifier pattern和元组模式tuple pattern指定类型注释用来限制这种模式只匹配某种类型的值。 第一类模式用于解构简单变量,常量和可选绑定中的值。此类模式包括通配符模式wildcard patterns标识符模式identifier patterns以及任何包含了它们的值绑定模式value binding patterns或者元祖模式tuple patterns。你可以为这类模式指定一个类型标注type annotation从而限制它们只能匹配某种特定类型的值。
第二类模式用于全模式匹配这种情况下你用来相比较的值在运行时可能还不存在。此类模式包括枚举用例模式enumeration case patterns可选模式optional patterns表达式模式expression patterns和类型转换模式type-casting patterns。你在`switch`语句的case标签中`do`语句的`catch`从句中,或者在`if, while, guard``for-in`语句的case条件句中使用这类模式。
> 模式(Patterns) 语法 > 模式(Patterns) 语法
> *模式* → [*通配符模式*](..\chapter3\07_Patterns.html#wildcard_pattern) [*类型注*](..\chapter3\03_Types.html#type_annotation) _可选_ > *模式* → [*通配符模式*](..\chapter3\07_Patterns.html#wildcard_pattern) [*类型注*](..\chapter3\03_Types.html#type_annotation) _可选_
> *模式* → [*标识符模式*](..\chapter3\07_Patterns.html#identifier_pattern) [*类型注*](..\chapter3\03_Types.html#type_annotati(Value Binding)on) _可选_ > *模式* → [*标识符模式*](..\chapter3\07_Patterns.html#identifier_pattern) [*类型注*](..\chapter3\03_Types.html#type_annotati(Value Binding)on) _可选_
> *模式* → [*值绑定模式*](..\chapter3\07_Patterns.html#value_binding_pattern) > *模式* → [*值绑定模式*](..\chapter3\07_Patterns.html#value_binding_pattern)
> *模式* → [*元组模式*](..\chapter3\07_Patterns.html#tuple_pattern) [*类型注*](..\chapter3\03_Types.html#type_annotation) _可选_ > *模式* → [*元组模式*](..\chapter3\07_Patterns.html#tuple_pattern) [*类型注*](..\chapter3\03_Types.html#type_annotation) _可选_
> *模式* → [*enum-case-pattern*](..\chapter3\07_Patterns.html#enum_case_pattern) > *模式* → [*枚举用例模式*](..\chapter3\07_Patterns.html#enum_case_pattern)
> *模式* → [*type-casting-pattern*](..\chapter3\07_Patterns.html#type_casting_pattern) > *模式* → [*可选模式*](..\chapter3\07_Patterns.html#optional_pattern)
> *模式* → [*类型转换模式*](..\chapter3\07_Patterns.html#type_casting_pattern)
> *模式* → [*表达式模式*](..\chapter3\07_Patterns.html#expression_pattern) > *模式* → [*表达式模式*](..\chapter3\07_Patterns.html#expression_pattern)
<a name="wildcard_pattern"></a> <a name="wildcard_pattern"></a>
## 通配符模式Wildcard Pattern ## 通配符模式Wildcard Pattern
通配符模式匹配并忽略任何值包含一个下划线_。当你不关心被匹配的值时可以使用模式。例如,下面这段代码进行了`1...3`循环,并忽略了每次循环的值: 通配符模式由一个下划线_构成匹配并忽略任何值。当你不在乎被匹配的值时可以使用模式。例如,下面这段代码在闭区间`1...3`循环,每次循环时忽略该区间内的当前值:
```swift ```swift
for _ in 1...3 { for _ in 1...3 {
@ -46,7 +50,7 @@ for _ in 1...3 {
<a name="identifier_pattern"></a> <a name="identifier_pattern"></a>
## 标识符模式Identifier Pattern ## 标识符模式Identifier Pattern
标识符模式匹配任何值,并将匹配的值和一个变量或常量绑定起来。例如,在下面的常量明中,`someValue`是一个标识符模式,匹配了类型是`Int``42` 标识符模式匹配任何值,并将匹配的值和一个变量或常量绑定起来。例如,在下面的常量明中,`someValue`是一个标识符模式,匹配了类型是`Int``42`
```swift ```swift
let someValue = 42 let someValue = 42
@ -54,7 +58,7 @@ let someValue = 42
当匹配成功时,`42`被绑定(赋值)给常量`someValue` 当匹配成功时,`42`被绑定(赋值)给常量`someValue`
一个变量或常量明的左边是标识符模式时,此时,标识符模式是隐式的值绑定模式value-binding pattern 如果一个变量或常量明的左边的模式是一个标识符模式,那么这个标识符模式是一个隐式的值绑定模式value-binding pattern
> 标识符模式语法 > 标识符模式语法
> *标识符模式* → [*标识符*](LexicalStructure.html#identifier) > *标识符模式* → [*标识符*](LexicalStructure.html#identifier)
@ -62,21 +66,21 @@ let someValue = 42
<a name="value-binding_pattern"></a> <a name="value-binding_pattern"></a>
## 值绑定模式Value-Binding Pattern ## 值绑定模式Value-Binding Pattern
值绑定模式绑定匹配的值一个变量或常量。当绑定匹配给常量时,用关键字`let`,绑定给变量时,用关键字`var` 值绑定模式匹配的值绑定给一个变量或常量名。把绑定匹配到的值绑定给常量时,用关键字`let`,绑定给变量时,用关键字`var`
标识符模式包含在值绑定模式中,绑定新的变量或常量匹配值。例如,你可以分解一个元组的元素,把每个元素绑定到相应的标识符模式中。 在值绑定模式中的标识符模式会把新命名的变量或常量匹配值做绑定。例如,你可以拆开一个元组的元素,然后把每个元素绑定到相应一个的标识符模式中。
```swift ```swift
let point = (3, 2) let point = (3, 2)
switch point { switch point {
// Bind x and y to the elements of point. // Bind x and y to the elements of point.
case let (x, y): case let (x, y):
println("The point is at (\(x), \(y)).") print("The point is at (\(x), \(y)).")
} }
// prints "The point is at (3, 2).” // prints "The point is at (3, 2).”
``` ```
在上面这个例子中,`let`将元组模式`(x, y)`分配到各个标识符模式。因为这种行为`switch`语句中`case let (x, y):``case (let x, let y):`匹配的值是一样的。 在上面这个例子中,`let`将元组模式`(x, y)`分配到各个标识符模式。正是由于这么做`switch`语句中`case let (x, y):``case (let x, let y):`匹配的值是一样的。
> 值绑定(Value Binding)模式语法 > 值绑定(Value Binding)模式语法
> *值绑定模式* → **var** [*模式*](..\chapter3\07_Patterns.html#pattern) | **let** [*模式*](..\chapter3\07_Patterns.html#pattern) > *值绑定模式* → **var** [*模式*](..\chapter3\07_Patterns.html#pattern) | **let** [*模式*](..\chapter3\07_Patterns.html#pattern)
@ -84,11 +88,11 @@ case let (x, y):
<a name="tuple_pattern"></a> <a name="tuple_pattern"></a>
## 元组模式Tuple Pattern ## 元组模式Tuple Pattern
元组模式是逗号分隔的列表,包含一个或多个模式,并包含在一对圆括号。元组模式匹配相应元组类型的值。 元组模式是逗号分隔的,有零个或多个模式的列表,并被一对圆括号括起来。元组模式匹配相应元组类型的值。
你可以使用类型注释来限制一个元组模式匹配种元组类型。例如,在常量`let (x, y): (Int, Int) = (1, 2)`中的元组模式`(x, y): (Int, Int)`只匹配两个元素都是`Int`这种类型的元组。如果仅需要限制一个元组模式中的某几个元素,只需要直接对这几个元素提供类型注即可。例如,在`let (x: String, y)`中的元组模式,只要某个元组类型是包含两个元素,且第一个元素类型是`String`,则被匹配。 你可以使用类型标注去限制一个元组模式匹配哪些种元组类型。例如,在常量`let (x, y): (Int, Int) = (1, 2)`中的元组模式`(x, y): (Int, Int)`只匹配两个元素都是`Int`这种类型的元组。如果仅需要限制一个元组模式中的某几个元素,只需要直接对这几个元素提供类型注即可。例如,在`let (x: String, y)`中的元组模式可以和任何有两个元素,且第一个元素类型是`String`的元组类型匹配。
当元组模式被用在`for-in`语句或者变量或常量明时,它可以包含通配符模式,标识符模式或者其他包含这两种模式的模式。例如,下面这段代码不正确,因为`(x, 0)`中的元素`0`是一个表达式模式: 当元组模式被用在`for-in`语句或者变量或常量明时,它可以包含通配符模式,标识符模式,可选模式或者其他包含这模式的元祖模式。比如下面这段代码不正确,因为`(x, 0)`中的元素`0`是一个表达式模式:
```swift ```swift
let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)] let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]
@ -98,7 +102,7 @@ for (x, 0) in points {
} }
``` ```
对于只包含一个元素的元组,括号是不起作用的。模式匹配个单个元素的类型。例如,下面是等效的: 对于只包含一个元素的元组,括号是不起作用的。模式匹配个单个元素的类型。举例来说下面3条语句是等效的:
```swift ```swift
let a = 2 // a: Int = 2 let a = 2 // a: Int = 2
@ -114,24 +118,59 @@ let (a): Int = 2 // a: Int = 2
<a name="enumeration_case_pattern"></a> <a name="enumeration_case_pattern"></a>
## 枚举用例模式Enumeration Case Pattern ## 枚举用例模式Enumeration Case Pattern
枚举用例模式匹配现有的枚举类型的某用例。枚举用例模式`switch`语句中的`case`标签中出现 一个枚举用例模式匹配现有的某个枚举类型的某用例case。枚举用例模式出现`switch`语句中的case标签中以及`if``while``guard``for-in`语句的case条件中
如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。关于使用`switch`语句来匹配包含关联值枚举用例的例子,请参阅`Associated Values`. 如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。关于使用`switch`语句来匹配包含关联值枚举用例的例子,请参阅`Associated Values`.
> 枚举用例模式语法 > 枚举用例模式语法
> *enum-case-pattern* → [*类型标识*](..\chapter3\03_Types.html#type_identifier) _可选_ **.** [*枚举的case名*](..\chapter3\05_Declarations.html#enum_case_name) [*元组模式*](..\chapter3\07_Patterns.html#tuple_pattern) _可选_ > *enum-case-pattern* → [*类型标识*](..\chapter3\03_Types.html#type_identifier) _可选_ **.** [*枚举的case名*](..\chapter3\05_Declarations.html#enum_case_name) [*元组模式*](..\chapter3\07_Patterns.html#tuple_pattern) _可选_
<a name="optional_pattern"></a>
## 可选模式Optional Pattern
可选模式与封装在一个`Optional(T)`或者一个`ExplicitlyUnwrappedOptional(T)`枚举中的`Some(T)`用例相匹配。可选模式由一个标识符模式和紧随其后的一个问号组成,在某些情况下表现为枚举用例模式。
由于可选模式是`optional``ImplicitlyUnwrappedOptional`枚举用例模式的语法糖syntactic sugar下面的2种写法是一样的
```swift
let someOptional: Int? = 42
// Match using an enumeration case pattern
if case .Some(let x) = someOptional {
print(x)
}
// Match using an optional pattern
if case let x? = someOptional {
print(x)
}
```
如果一个数组的元素是可选类型,可选模式为`for-in`语句提供了一种在该数组中迭代的简便方式,只为数组中的非空`non-nil`元素执行循环体。
```swift
let arrayOfOptionalInts: [Int?] = [nil, 2, 3, nil, 5]
// Match only non-nil values
for case let number? in arrayOfOptinalInts {
print("Found a \(number)")
}
//Found a 2
//Found a 3
//Found a 5
```
> 可选模式语法
> *可选模式* → [*标识符模式*](..\chapter3\03_Types.html#type_identifier) ?
<a name="type-casting_patterns"></a> <a name="type-casting_patterns"></a>
## 类型转换模式Type-Casting Patterns ## 类型转换模式Type-Casting Patterns
有两种类型转换模式,`is`模式和`as`模式。这两种模式只出现在`switch`语句中的`case`标签中。`is`模式和`as`模式有以下形式: 有两种类型转换模式,`is`模式和`as`模式。这两种模式只出现在`switch`语句中的case标签中。`is`模式和`as`模式有以下形式:
> is `type` > is `type`
> `pattern` as `type` > `pattern` as `type`
`is`模式匹配一个值,如果这个值的类型在运行时runtime`is`模式右边的指定类型(或者那个类型的子类)是一致的`is`模式和`is`操作符一样,它们都进行类型转换,但是抛弃了返回的类型。 `is`模式仅当一个值的类型在运行时runtime`is`模式右边的指定类型一致 - 或者是该类型的子类 - 的情况下,才会匹配这个值`is`模式和`is`操作符有相似表现,它们都进行类型转换,却舍弃返回的类型。
`as`模式匹配一个值,如果这个值的类型在运行时runtime`as`模式右边的指定类型(或者那个类型的子类)是一致的。一旦匹配成功,匹配的值的类型被转换成`as`模式左边指定的模式。 `as`模式仅当一个值的类型在运行时runtime`as`模式右边的指定类型一致 - 或者是该类型的子类 - 的情况下,才会匹配这个值。如果匹配成功,匹配的值的类型被转换成`as`模式左边指定的模式。
关于使用`switch`语句来匹配`is`模式和`as`模式值的例子,请参阅`Type Casting for Any and AnyObject` 关于使用`switch`语句来匹配`is`模式和`as`模式值的例子,请参阅`Type Casting for Any and AnyObject`
@ -143,24 +182,24 @@ let (a): Int = 2 // a: Int = 2
<a name="expression_pattern"></a> <a name="expression_pattern"></a>
## 表达式模式Expression Pattern ## 表达式模式Expression Pattern
表达式模式代表了一个表达式的值。这个模式只出现在`switch`语句中的`case`标签中。 一个表达式模式代表了一个表达式的值。表达式模式只出现在`switch`语句中的`case`标签中。
由表达式模式所代表的表达式Swift标准库中`~=`操作符输入表达式的值进行比较。如果`~=`操作符返回`true`,则匹配成功。默认情况下,`~=`操作符使用`==`操作符来比较两个相同类型的值。它也可以匹配一个整数值与一个`Range`对象中的整数范围,正如下面这个例子所示: 由表达式模式所代表的表达式与使用了Swift标准库中`~=`操作符输入表达式的值进行比较。如果`~=`操作符返回`true`,则匹配成功。默认情况下,`~=`操作符使用`==`操作符来比较两个相同类型的值。它也可以一个整数值与一个`Range`对象中的一段整数区间做匹配,正如下面这个例子所示:
```swift ```swift
let point = (1, 2) let point = (1, 2)
switch point { switch point {
case (0, 0): case (0, 0):
println("(0, 0) is at the origin.") print("(0, 0) is at the origin.")
case (-2...2, -2...2): case (-2...2, -2...2):
println("(\(point.0), \(point.1)) is near the origin.") print("(\(point.0), \(point.1)) is near the origin.")
default: default:
println("The point is at (\(point.0), \(point.1)).") print("The point is at (\(point.0), \(point.1)).")
} }
// prints "(1, 2) is near the origin.” // prints "(1, 2) is near the origin.”
``` ```
你可以重载`~=`操作符来提供自定义的表达式行为。例如,你可以重写上面的例子,以实现用字符串表达的点来比较`point`表达式。 你可以重载`~=`操作符来提供自定义的表达式匹配行为。比如你可以重写上面的例子,`point`表达式去比较字符串形式的点
```swift ```swift
// Overload the ~= operator to match a string with an integer // Overload the ~= operator to match a string with an integer
@ -169,14 +208,12 @@ func ~=(pattern: String, value: Int) -> Bool {
} }
switch point { switch point {
case ("0", "0"): case ("0", "0"):
println("(0, 0) is at the origin.") print("(0, 0) is at the origin.")
case ("-2...2", "-2...2"):
println("(\(point.0), \(point.1)) is near the origin.")
default: default:
println("The point is at (\(point.0), \(point.1)).") print("The point is at (\(point.0), \(point.1)).")
} }
// prints "(1, 2) is near the origin.” // prints "(1, 2) is near the origin.”
``` ```
> 表达式模式语法 > 表达式模式语法
> *表达式模式* → [*表达式*](..\chapter3\04_Expressions.html#expression) > *表达式模式* → [*表达式*](..\chapter3\04_Expressions.html#expression)

View File

@ -1,5 +1,5 @@
> 翻译:[fd5788](https://github.com/fd5788) > 翻译:[fd5788](https://github.com/fd5788)
> 校对:[yankuangshi](https://github.com/yankuangshi), [stanzhai](https://github.com/stanzhai) > 校对:[yankuangshi](https://github.com/yankuangshi), [stanzhai](https://github.com/stanzhai), [wardenNScaiyi](https:github.com/wardenNScaiyi)
# 泛型参数 # 泛型参数
--------- ---------
@ -9,28 +9,29 @@
- [泛型形参子句](#generic_parameter) - [泛型形参子句](#generic_parameter)
- [泛型实参子句](#generic_argument) - [泛型实参子句](#generic_argument)
本节涉及泛型类型、泛型函数以及泛型构造器的参数,包括形参和实参。声明泛型类型、函数或构造器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型构造器时,就用具体的类型实参替代之。 本节涉及泛型类型、泛型函数以及泛型初始化器(**initializer**的参数,包括形参和实参。声明泛型类型、函数或初始化器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型初始化器时,就用具体的类型实参替代之。
关于 Swift 语言的泛型概述,见[泛型](../charpter2/22_Generics.md)(第二部分第22章)。 关于 Swift 语言的泛型概述,见[泛型](../charpter2/22_Generics.md)(第二部分第22章)。
<a name="generic_parameter"></a> <a name="generic_parameter"></a>
## 泛型形参子句 ## 泛型形参子句
泛型形参子句指定泛型类型或函数的类型形参,以及这些参数的关联约束和要求。泛型形参子句用尖括号(<>)包住,并且有以下两种形式: 泛型形参子句指定泛型类型或函数的类型形参,以及这些参数的关联约束和关联类型要求(**requirement**。泛型形参子句用尖括号(<>)包住,并且有以下两种形式:
> <`generic parameter list`> > <`泛型形参列表`>
> <`generic parameter list` where `requirements`> > <`泛型形参列表` where `关联类型要求`>
泛型形参列表中泛型形参用逗号分开,每一个采用以下形式: 泛型形参列表中泛型形参用逗号分开,其中每一个采用以下形式:
> `type parameter` : `constrain` > `类型形参` : `约束`
泛型形参由两部分组成类型形参及其后的可选约束。类型形参只是占位符类型如TUVKeyTypeValueType等)的名字而已。你可以在泛型类型、函数的其余部分或者构造器声明,以及函数或构造器的签名中使用它。 泛型形参由两部分组成:类型形参及其后的可选约束。类型形参只是占位符类型(如 TUVKeyValue 等)的名字而已。你可以在泛型类型、函数的其余部分或者初始化器声明,包括函数或初始化器的签名中使用它(与其任何相关类型)
约束用于指明该类型形参继承自某个类或者遵守某个协议或协议的一部分。例如,在下面的泛型函数中,泛型形参`T: Comparable`表示任何用于替代类型形参`T`的类型实参必须满足`Comparable`协议。
约束用于指明该类型形参继承自某个类或者遵守某个协议或协议的一部分。例如,在下面的泛型中,泛型形参`T: Comparable`表示任何用于替代类型形参`T`的类型实参必须满足`Comparable`协议。
```swift ```swift
func simpleMin<T: Comparable>(x: T, y: T) -> T { func simpleMax<T: Comparable>(x: T, _ y: T) -> T {
if x < y { if x < y {
return y return y
} }
@ -38,28 +39,30 @@ func simpleMin<T: Comparable>(x: T, y: T) -> T {
} }
``` ```
如,`Int``Double`均满足`Comparable`协议,该函数接受任何一种类型。与泛型类型相反,调用泛型函数或构造器时不需要指定泛型实参子句。类型实参由传递给函数或构造器的实参推断而出。
```swift
simpleMin(17, 42) // T is inferred to be Int 如,`Int``Double`均满足`Comparable`协议,该函数接受任何一种类型。与泛型类型相反,调用泛型函数或初始化器时不需要指定泛型实参子句。类型实参由传递给函数或初始化器的实参推断而出。
simpleMin(3.14159, 2.71828) // T is inferred to be Double
```
simpleMax(17, 42) // T被推断出为Int类型
simpleMax(3.14159, 2.71828) // T被推断出为Double类型
``` ```
## Where 子句 ## Where 子句
要想对类型形参及其关联类型指定额外要求,可以在泛型形参列表之后添加`where`子句。`where`子句由关键字`where`及其后的用逗号分割的多个要求组成。 要想对类型形参及其关联类型指定额外关联类型要求,可以在泛型形参列表之后添加`where`子句。`where`子句由关键字`where`及其后的用逗号分割的多个关联类型要求组成。
`where`子句中的要求用于指明该类型形参继承自某个类或遵守某个协议或协议的一部分。尽管`where`子句有助于表达类型形参上的简单约束(如`T: Comparable`等同于`T where T: Comparable`,等等),但是依然可以用来对类型形参及其关联约束提供更复杂的约束。如,`<T where T: C, T: P>`表示泛型类型`T`继承自类`C`且遵守协议`P` `where`子句中的关联关系用于指明该类型形参继承自某个类或遵守某个协议或协议的一部分。尽管`where`子句提供了语法糖使其有助于表达类型形参上的简单约束(如`T: Comparable`等同于`T where T: Comparable`,等等),但是依然可以用来对类型形参及其关联类型提供更复杂的约束。如,`<T where T: C, T: P>`表示泛型类型`T`继承自类`C`且遵守协议`P`
如上所述,可以强制约束类型形参的关联类型遵守某个协议。`<T: Generator where T.Element: Equatable>`表示`T`遵守`Generator`协议,而且`T`的关联类型`T.Element`遵守`Eauatable`协议(`T`有关联类型是因为`Generator`声明了`Element`,而`T`遵守`Generator`协议)。 如上所述,可以强制约束类型形参的关联类型遵守某个协议。例如`<T: Generator where T.Element: Equatable>`表示`T`遵守`Generator`协议,而且`T`的关联类型`T.Element`遵守`Eauatable`协议(`T`有关联类型`Element`是因为`Generator`声明了`Element`,而`T`遵守`Generator`协议)。
也可以用操作符`==`来指定两个类型等效的要求。例如,有这样一个约束:`T``U`遵守`Generator`协议,同时要求它们的关联类型等同,可以这样来表达:`<T: Generator, U: Generator where T.Element == U.Element>` 也可以用操作符`==`来指定两个类型等效的关联关系。例如,有这样一个约束:`T``U`遵守`Generator`协议,同时要求它们的关联类型等同,可以这样来表达:`<T: Generator, U: Generator where T.Element == U.Element>`
当然,替代类型形参的类型实参必须满足所有类型形参所要求的约束和要求。 当然,替代类型形参的类型实参必须满足所有类型形参的约束和关联类型要求。
泛型函数或构造器可以重载,但在泛型形参子句中的类型形参必须有不同的约束或要求,抑或二者皆不同。当调用重载的泛型函数或构造器时,编译器会用这些约束来决定调用哪个重载函数或构造器。 泛型函数或初始化器可以重载,但在泛型形参子句中的类型形参必须有不同的约束或关联类型要求,抑或二者皆不同。当调用重载的泛型函数或始化器时,编译器会用这些约束来决定调用哪个重载函数或始化器。
泛型类可以生成一个子类,但是这个子类也必须是泛型类。
> 泛型形参子句语法 > 泛型形参子句语法
> *泛型参数子句* → **<** [*泛型参数列表*](GenericParametersAndArguments.html#generic_parameter_list) [*约束子句*](GenericParametersAndArguments.html#requirement_clause) _可选_ **>** > *泛型参数子句* → **<** [*泛型参数列表*](GenericParametersAndArguments.html#generic_parameter_list) [*约束子句*](GenericParametersAndArguments.html#requirement_clause) _可选_ **>**
@ -80,27 +83,30 @@ simpleMin(3.14159, 2.71828) // T is inferred to be Double
泛型实参子句指定_泛型类型_的类型实参。泛型实参子句用尖括号<>)包住,形式如下: 泛型实参子句指定_泛型类型_的类型实参。泛型实参子句用尖括号<>)包住,形式如下:
> <`generic argument list`> > <`泛型实参列表`>
泛型实参列表中类型实参有逗号分开。类型实参是实际具体类型的名字用来替代泛型类型的泛型形参子句中的相应的类型形参。从而得到泛型类型的一个特化版本。如Swift标准库的泛型字典类型定义如下 泛型实参列表中类型实参有逗号分开。类型实参是实际具体类型的名字用来替代泛型类型的泛型形参子句中的相应的类型形参。从而得到泛型类型的一个特化版本。如Swift标准库的泛型字典类型定义如下
```swift ```swift
struct Dictionary<KeyTypel: Hashable, ValueType>: Collection, DictionaryLiteralConvertible { struct Dictionary<KeyTypel: Hashable, ValueType>: Collection, DictionaryLiteralConvertible {
/* .. */ /* .. */
} }
``` ```
泛型`Dictionary`类型的特化版本,`Dictionary<String, Int>`就是用具体的`String``Int`类型替代泛型类型`KeyType: Hashable``ValueType`产生的。每一个类型实参必须满足它所替代的泛型形参的所有约束,包括任何`where`子句所指定的额外的要求。上面的例子中,类型形参`KeyType`要求满足`Hashable`协议,因此`String`也必须满足`Hashable`协议。 泛型`Dictionary`类型的特化版本,`Dictionary<String, Int>`就是用具体的`String``Int`类型替代泛型类型`KeyType: Hashable``ValueType`产生的。每一个类型实参必须满足它所替代的泛型形参的所有约束,包括任何`where`子句所指定的额外的关联类型要求。上面的例子中,类型形参`Key`类型要求满足`Hashable`协议,因此`String`也必须满足`Hashable`协议。
可以用本身就是泛型类型的特化版本的类型实参替代类型形参(假设已满足合适的约束和要求)。例如,为了生成一个元素类型是整型数组的数组,可以用数组的特化版本`Array<Int>`替代泛型类型`Array<T>`的类型形参`T`来实现。 可以用本身就是泛型类型的特化版本的类型实参替代类型形参(假设已满足合适的约束和关联类型要求)。例如,为了生成一个元素类型是整型数组的数组,可以用数组的特化版本`Array<Int>`替代泛型类型`Array<T>`的类型形参 `T` 来实现。
```swift ```
let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
``` ```
如[泛型形参子句](#generic_parameter)所述,不能用泛型实参子句来指定泛型函数或构造器的类型实参。 如[泛型形参子句](#generic_parameter)所述,不能用泛型实参子句来指定泛型函数或初始化器的类型实参。
> 泛型实参子句语法 > 泛型实参子句语法
> *(泛型参数子句Generic Argument Clause)* → **<** [*泛型参数列表*](GenericParametersAndArguments.html#generic_argument_list) **>** > *(泛型参数子句Generic Argument Clause)* → **<** [*泛型参数列表*](GenericParametersAndArguments.html#generic_argument_list) **>**
> *泛型参数列表* → [*泛型参数*](GenericParametersAndArguments.html#generic_argument) | [*泛型参数*](GenericParametersAndArguments.html#generic_argument) **,** [*泛型参数列表*](GenericParametersAndArguments.html#generic_argument_list) > *泛型参数列表* → [*泛型参数*](GenericParametersAndArguments.html#generic_argument) | [*泛型参数*](GenericParametersAndArguments.html#generic_argument) **,** [*泛型参数列表*](GenericParametersAndArguments.html#generic_argument_list)
> *泛型参数* → [*类型*](..\chapter3\03_Types.html#type) > *泛型参数* → [*类型*](..\chapter3\03_Types.html#type)