Translations for Swift 4.1 (#777)
* Translations for Chapter 3 part 01 for Swift 4.0/4.1 * update chapter 3 part 02 for swift 4.1 * update chapter 3 part 03 for Swift 4.1 * update chapter 1 part 01 for Swift 4.1 * update chapter 1 part 02 for Swift 4.1 * update chapter 1 part 03 for Swift 4.1 * update chapter 2 part 01 for Swift 4.1 * update chapter 2 part 02 for Swift 4.1 * update chapter 2 part 3 for Swift 4.1 * update "summary" and corrected file names * update chapter 2 part 4 for Swift 4.1 * update chapter 2 part 5 for Swift 4.1 * update chapter 2 part 6 for Swift 4.1 * update chapter 2 parts 06-11 and other parts' errors * update chapter 2 parts 12-14 * update chapter 2 parts 14-19 * update all chapter 2 * update whole chapter 1 * update some parts of chapter 3
This commit is contained in:
@ -1,9 +1,10 @@
|
||||
# Summary
|
||||
|
||||
* [欢迎使用 Swift](chapter1/chapter1.md)
|
||||
* [关于 Swift](chapter1/01_swift.md)
|
||||
* [Swift 初见](chapter1/02_a_swift_tour.md)
|
||||
* [Swift 版本历史记录](chapter1/03_revision_history.md)
|
||||
* [关于 Swift](chapter1/01_about_swift.md)
|
||||
* [版本兼容性](chapter1/02_version_compatibility.md)
|
||||
* [Swift 初见](chapter1/03_a_swift_tour.md)
|
||||
* [Swift 版本历史记录](chapter1/04_revision_history.md)
|
||||
* [Swift 1.0 发布内容](v1.0.md)
|
||||
* [Swift 教程](chapter2/chapter2.md)
|
||||
* [基础部分](chapter2/01_The_Basics.md)
|
||||
@ -21,27 +22,29 @@
|
||||
* [继承](chapter2/13_Inheritance.md)
|
||||
* [构造过程](chapter2/14_Initialization.md)
|
||||
* [析构过程](chapter2/15_Deinitialization.md)
|
||||
* [自动引用计数](chapter2/16_Automatic_Reference_Counting.md)
|
||||
* [可选链](chapter2/17_Optional_Chaining.md)
|
||||
* [错误处理](chapter2/18_Error_Handling.md)
|
||||
* [类型转换](chapter2/19_Type_Casting.md)
|
||||
* [嵌套类型](chapter2/20_Nested_Types.md)
|
||||
* [扩展](chapter2/21_Extensions.md)
|
||||
* [协议](chapter2/22_Protocols.md)
|
||||
* [泛型](chapter2/23_Generics.md)
|
||||
* [访问控制](chapter2/24_Access_Control.md)
|
||||
* [高级运算符](chapter2/25_Advanced_Operators.md)
|
||||
* [可选链](chapter2/16_Optional_Chaining.md)
|
||||
* [错误处理](chapter2/17_Error_Handling.md)
|
||||
* [类型转换](chapter2/18_Type_Casting.md)
|
||||
* [嵌套类型](chapter2/19_Nested_Types.md)
|
||||
* [扩展](chapter2/20_Extensions.md)
|
||||
* [协议](chapter2/21_Protocols.md)
|
||||
* [泛型](chapter2/22_Generics.md)
|
||||
* [自动引用计数](chapter2/23_Automatic_Reference_Counting.md)
|
||||
* [内存安全](chapter2/24_Memory_Safety.md)
|
||||
* [访问控制](chapter2/25_Access_Control.md)
|
||||
* [高级运算符](chapter2/26_Advanced_Operators.md)
|
||||
* 语言参考
|
||||
* [关于语言参考](chapter3/01_About_the_Language_Reference.md)
|
||||
* [词法结构](chapter3/02_Lexical_Structure.md)
|
||||
* [类型](chapter3/03_Types.md)
|
||||
* [表达式](chapter3/04_Expressions.md)
|
||||
* [语句](chapter3/10_Statements.md)
|
||||
* [声明](chapter3/05_Declarations.md)
|
||||
* [特性](chapter3/06_Attributes.md)
|
||||
* [模式](chapter3/07_Patterns.md)
|
||||
* [泛型参数](chapter3/08_Generic_Parameters_and_Arguments.md)
|
||||
* [语法总结](chapter3/09_Summary_of_the_Grammar.md)
|
||||
* [语句](chapter3/05_Statements.md)
|
||||
* [声明](chapter3/06_Declarations.md)
|
||||
* [特性](chapter3/07_Attributes.md)
|
||||
* [模式](chapter3/08_Patterns.md)
|
||||
* [泛型参数](chapter3/09_Generic_Parameters_and_Arguments.md)
|
||||
* [语法总结](chapter3/10_Summary_of_the_Grammar.md)
|
||||
|
||||
* 苹果官方Blog官方翻译
|
||||
* [Access Control 权限控制的黑与白](chapter4/01_Access_Control.md)
|
||||
* [造个类型不是梦-白话Swift类型创建](chapter4/02_Type_Custom.md)
|
||||
|
||||
@ -7,15 +7,17 @@
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[xtymichael](https://github.com/xtymichael)
|
||||
>
|
||||
|
||||
> 3.0 翻译+校对:[shanks](http://codebuild.me),2016-10-06
|
||||
> 3.0.1 review : 2016-11-09
|
||||
>
|
||||
|
||||
> 3.1 校对: [SketchK](https://github.com/SketchK) 2017-04-08
|
||||
>
|
||||
|
||||
> 4.0
|
||||
> 翻译:[rain2540](https://github.com/rain2540) 2017-09-21
|
||||
>
|
||||
|
||||
> 4.1
|
||||
> 翻译:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
Swift 是一种非常好的编写软件的方式,无论是手机,台式机,服务器,还是其他运行代码的设备。它是一种安全,快速和互动的编程语言,将现代编程语言的精华和苹果工程师文化的智慧,以及来自开源社区的多样化贡献结合了起来。编译器对性能进行了优化,编程语言对开发进行了优化,两者互不干扰,鱼与熊掌兼得。
|
||||
|
||||
@ -26,7 +28,7 @@ Swift通过采用现代编程模式来避免大量常见编程错误:
|
||||
* 变量始终在使用前初始化。
|
||||
* 检查数组索引超出范围的错误。
|
||||
* 检查整数是否溢出。
|
||||
* 可选值确保明确处理 nil 值。
|
||||
* 可选值确保明确处理 `nil` 值。
|
||||
* 内存被自动管理。
|
||||
* 错误处理允许从意外故障控制恢复。
|
||||
|
||||
@ -34,4 +36,4 @@ Swift 代码被编译和优化,以充分利用现代硬件。语法和标准
|
||||
|
||||
Swift 将强大的类型推理和模式匹配与现代轻巧的语法相结合,使复杂的想法能够以清晰简洁的方式表达。因此,代码不仅更容易编写,而且易于阅读和维护。
|
||||
|
||||
Swift 已经进行了多年,并且随着新特性和功能的不断发展。我们对 Swift 的目标是雄心勃勃的。我们迫不及待想看看你创建的内容。
|
||||
Swift 已经进行了多年,并且随着新特性和功能的不断发展。我们对 Swift 的目标是雄心勃勃的。我们迫不及待想看到你用它创建出的东西。
|
||||
@ -2,18 +2,21 @@
|
||||
-----------------
|
||||
|
||||
> 4.0
|
||||
>
|
||||
> 翻译:[muhlenXi](https://github.com/muhlenxi) 2017-09-25
|
||||
|
||||
本书描述的是 Swift 4.0,是 Xcode 9 中包含的默认版本。你可以用 Xcode 9 来构建用 Swift 4 或 Swift 3 写的项目。
|
||||
> 4.1
|
||||
> 翻译:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本书描述的是 Swift 4.1,是 Xcode 9.2 中包含的默认版本。你可以用 Xcode 9.2 来构建用 Swift 4 或 Swift 3 写的项目。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 当 Swift 4 编译器编译 Swift 3 版本的代码时,它识别的语言版本为 3.2 版本。因此,你可以使用像 `#if swift(>=3.2)` 条件编译块来编写多版本编译器可以并存的代码。
|
||||
|
||||
当你用 Xcode 编译 Swift 3 的代码,Swift 4 中大部分功能是可以使用的。也就是说,下面的功能仅仅是 Swift 4 的代码中可以使用:
|
||||
当你用 Xcode 9.2 编译 Swift 3 的代码,Swift 4 中大部分功能是可以使用的。也就是说,下面的功能仅仅是 Swift 4 的代码中可以使用:
|
||||
|
||||
* 字符串的子串操作返回的实例是 `Substring` 类型,不再是 `String` 类型。
|
||||
* 在一些地方显式的添加 `@objc` 属性。
|
||||
* 同一文件中一个类型的 extension 可以访问这个类型的 private 属性。
|
||||
* 在更少的地方显式的添加 `@objc` 属性。
|
||||
* 同一文件中类型的扩展可以访问这个类型的私有成员。
|
||||
|
||||
用 Swift 4 写的项目可以依赖用 Swift 3 写的项目,反之亦然。这意味着,如果你将一个大的项目分解成多个框架(framework),你可以依次迁移每个框架到 Swift 4。
|
||||
用 Swift 4 写的项目可以依赖用 Swift 3 写的项目,反之亦然。这意味着,如果你将一个大的项目分解成多个框架(framework),你可以每次一个框架地迁移 Swift 3 代码到 Swift 4。
|
||||
|
||||
@ -22,6 +22,9 @@
|
||||
> 4.0
|
||||
> 翻译+校对:[muhlenxi](https://github.com/muhlenxi) 2017-09-26
|
||||
|
||||
> 4.1
|
||||
> 翻译:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页内容包括:
|
||||
|
||||
- [简单值(Simple Values)](#simple_values)
|
||||
@ -43,8 +46,11 @@ print("Hello, world!")
|
||||
|
||||
这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解到。
|
||||
|
||||
> 注意:
|
||||
> 在 Mac 中下载 Playground 文件并用双击的方式在 Xcode 中打开:[https://developer.apple.com/go/?id=swift-tour](https://developer.apple.com/go/?id=swift-tour)
|
||||
> 注意
|
||||
>
|
||||
> 最好的体验是把这一章作为Playground文件在Xcode中打开。 Playgrounds允许你可以编辑代码并立刻看到输出结果。
|
||||
>
|
||||
> [Download Playground](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.playground.zip)
|
||||
|
||||
<a name="simple_values"></a>
|
||||
## 简单值
|
||||
@ -57,7 +63,7 @@ myVariable = 50
|
||||
let myConstant = 42
|
||||
```
|
||||
|
||||
常量或者变量的类型必须和你赋给它们的值一样。然而,你不用明确地声明类型。当你通过一个值来声明变量和常量时,编译器会自动推断其类型。在上面的例子中,编译器推断出 `myVariable` 是一个整数类型(integer)因为它的初始值是整数。
|
||||
常量或者变量的类型必须和你赋给它们的值一样。然而,你不用明确地声明类型。当你通过一个值来声明变量和常量时,编译器会自动推断其类型。在上面的例子中,编译器推断出 `myVariable` 是一个整数类型,因为它的初始值是整数。
|
||||
|
||||
如果初始值没有提供足够的信息(或者没有初始值),那你需要在变量后面声明类型,用冒号分割。
|
||||
|
||||
@ -67,7 +73,8 @@ let implicitDouble = 70.0
|
||||
let explicitDouble: Double = 70
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 创建一个常量,显式指定类型为 `Float` 并指定初始值为 4。
|
||||
|
||||
值永远不会被隐式转换为其他类型。如果你需要把一个值转换成其他类型,请显式转换。
|
||||
@ -77,7 +84,8 @@ let label = "The width is"
|
||||
let width = 94
|
||||
let widthLabel = label + String(width)
|
||||
```
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 删除最后一行中的 `String`,错误提示是什么?
|
||||
|
||||
有一种更简单的把值转换成字符串的方法:把值写到括号中,并且在括号之前写一个反斜杠(\)。例如:
|
||||
@ -89,19 +97,16 @@ let appleSummary = "I have \(apples) apples."
|
||||
let fruitSummary = "I have \(apples + oranges) pieces of fruit."
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 使用 `\()` 来把一个浮点计算转换成字符串,并加上某人的名字,和他打个招呼。
|
||||
|
||||
使用一对三个单引号(`"""`)来包含多行字符串内容,字符串中的内容(包括引号、空格、换行符等)都会保留下来。举个例子:
|
||||
|
||||
```swift
|
||||
let quotation = """
|
||||
Even though there's whitespace to the left,
|
||||
the actual lines aren't indented.
|
||||
Except for this line.
|
||||
Double quotes (") can appear without being escaped.
|
||||
|
||||
I still have \(apples + oranges) pieces of fruit.
|
||||
I said "I have \(apples) apples."
|
||||
And then I said "I have \(apples + oranges) pieces of fruit."
|
||||
"""
|
||||
```
|
||||
|
||||
@ -165,7 +170,8 @@ if let name = optionalName {
|
||||
}
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 把 `optionalName` 改成 `nil`,greeting 会是什么?添加一个 `else` 语句,当 `optionalName` 是 `nil` 时给 greeting 赋一个不同的值。
|
||||
|
||||
如果变量的可选值是 `nil`,条件会判断为 `false`,大括号中的代码会被跳过。如果不是 `nil`,会将值解包并赋给 `let` 后面的常量,这样代码块中就可以使用这个值了。
|
||||
@ -193,7 +199,8 @@ default:
|
||||
}
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 删除 `default` 语句,看看会有什么错误?
|
||||
|
||||
注意 `let` 在上述例子的等式中是如何使用的,它将匹配等式的值赋给常量 `x`。
|
||||
@ -219,7 +226,8 @@ for (kind, numbers) in interestingNumbers {
|
||||
print(largest)
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 添加另一个变量来记录最大数字的种类(kind),同时仍然记录这个最大数字的值。
|
||||
|
||||
使用 `while` 来重复运行一段代码直到条件改变。循环条件也可以在结尾,保证能至少循环一次。
|
||||
@ -262,7 +270,8 @@ func greet(person: String, day: String) -> String {
|
||||
greet(person:"Bob", day: "Tuesday")
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 删除 `day` 参数,添加一个参数来表示今天吃了什么午饭。
|
||||
|
||||
默认情况下,函数使用它们的参数名称作为它们参数的标签,在参数名称前可以自定义参数标签,或者使用 `_` 表示不使用参数标签。
|
||||
@ -353,7 +362,8 @@ numbers.map({
|
||||
})
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 重写闭包,对所有奇数返回 0。
|
||||
|
||||
有很多种创建更简洁的闭包的方法。如果一个闭包的类型已知,比如作为一个代理的回调,你可以忽略参数,返回值,甚至两个都忽略。单个语句闭包会把它语句的值当做结果返回。
|
||||
@ -384,7 +394,8 @@ class Shape {
|
||||
}
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 使用 `let` 添加一个常量属性,再添加一个接收一个参数的方法。
|
||||
|
||||
要创建一个类的实例,在类名后面加上括号。使用点语法来访问实例的属性和方法。
|
||||
@ -443,7 +454,8 @@ test.area()
|
||||
test.simpleDescription()
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 创建 `NamedShape` 的另一个子类 `Circle`,构造器接收两个参数,一个是半径一个是名称,在子类 `Circle` 中实现 `area()` 和 `simpleDescription()` 方法。
|
||||
|
||||
除了储存简单的属性之外,属性可以有 getter 和 setter 。
|
||||
@ -547,7 +559,8 @@ let ace = Rank.ace
|
||||
let aceRawValue = ace.rawValue
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 写一个函数,通过比较它们的原始值来比较两个 `Rank` 值。
|
||||
|
||||
默认情况下,Swift 按照从 0 开始每次加 1 的方式为原始值进行赋值,不过你可以通过显式赋值进行改变。在上面的例子中,`Ace` 被显式赋值为 1,并且剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用 `rawValue` 属性来访问一个枚举成员的原始值。
|
||||
@ -582,7 +595,8 @@ let hearts = Suit.hearts
|
||||
let heartsDescription = hearts.simpleDescription()
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 给 `Suit` 添加一个 `color()` 方法,对 `spades` 和 `clubs` 返回 “black” ,对 `hearts` 和 `diamonds` 返回 “red” 。
|
||||
|
||||
注意在上面的例子中用了两种方式引用 `hearts` 枚举成员:给 `hearts` 常量赋值时,枚举成员 `Suit.hearts` 需要用全名来引用,因为常量没有显式指定类型。在 `switch` 里,枚举成员使用缩写 `.hearts` 来引用,因为 `self` 的值已经是一个 `suit` 类型,在已知变量类型的情况下可以使用缩写。
|
||||
@ -606,7 +620,8 @@ case let .failure(message):
|
||||
}
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 给 `ServerResponse` 和 switch 添加第三种情况。
|
||||
|
||||
注意日升和日落时间是如何从 `ServerResponse` 中提取到并与 `switch` 的 `case` 相匹配的。
|
||||
@ -625,7 +640,8 @@ let threeOfSpades = Card(rank: .three, suit: .spades)
|
||||
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 给 `Card` 添加一个方法,创建一副完整的扑克牌并把每张牌的 rank 和 suit 对应起来。
|
||||
|
||||
<a name="protocols_and_extensions"></a>
|
||||
@ -665,7 +681,8 @@ b.adjust()
|
||||
let bDescription = b.simpleDescription
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 写一个实现这个协议的枚举。
|
||||
|
||||
注意声明 `SimpleStructure` 时候 `mutating` 关键字用来标记一个会修改结构体的方法。`SimpleClass` 的声明不需要标记任何方法,因为类中的方法通常可以修改类属性(类的性质)。
|
||||
@ -684,7 +701,8 @@ extension Int: ExampleProtocol {
|
||||
print(7.simpleDescription)
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 给 `Double` 类型写一个扩展,添加 `absoluteValue` 属性。
|
||||
|
||||
你可以像使用其他命名类型一样使用协议名——例如,创建一个有不同类型但是都实现一个协议的对象集合。当你处理类型是协议的值时,协议外定义的方法不可用。
|
||||
@ -732,7 +750,8 @@ do {
|
||||
}
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 将 printer name 改为 `"Never Has Toner"` 使 `send(job:toPrinter:)` 函数抛出错误。
|
||||
|
||||
可以使用多个 `catch` 块来处理特定的错误。参照 switch 中的 `case` 风格来写 `catch`。
|
||||
@ -750,7 +769,8 @@ do {
|
||||
}
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 在 `do` 代码块中添加抛出错误的代码。你需要抛出哪种错误来使第一个 `catch` 块进行接收?怎么使第二个和第三个 `catch` 进行接收呢?
|
||||
|
||||
另一种处理错误的方式使用 `try?` 将结果转换为可选的。如果函数抛出错误,该错误会被抛弃并且结果为 `nil`。否则,结果会是一个包含函数返回值的可选值。
|
||||
@ -792,7 +812,7 @@ func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
|
||||
}
|
||||
return result
|
||||
}
|
||||
repeatItem(repeating: "knock", numberOfTimes:4)
|
||||
makeArray(repeating: "knock", numberOfTimes: 4)
|
||||
```
|
||||
|
||||
你也可以创建泛型函数、方法、类、枚举和结构体。
|
||||
@ -824,7 +844,8 @@ func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
|
||||
anyCommonElements([1, 2, 3], [3])
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 练习
|
||||
>
|
||||
> 修改 `anyCommonElements(_:_:)` 函数来创建一个函数,返回一个数组,内容是两个序列的共有元素。
|
||||
|
||||
`<T: Equatable>` 和 `<T> ... where T: Equatable>` 的写法是等价的。
|
||||
|
||||
@ -45,7 +45,8 @@
|
||||
|
||||
本页内容包括:
|
||||
|
||||
- [Swift 4.0 更新](#swift_4_1)
|
||||
- [Swift 4.1 更新](#swift_4_1)
|
||||
- [Swift 4.0.3 更新](#swift_4_0_3)
|
||||
- [Swift 4.0 更新](#swift_4_0)
|
||||
- [Swift 3.1 更新](#swift_3_1)
|
||||
- [Swift 3.0 更新](#swift_3_0)
|
||||
@ -68,7 +69,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td scope="row">2018-02-06</td>
|
||||
<td scope="row">2018-03-29</td>
|
||||
<td>
|
||||
<ul class="list-bullet">
|
||||
<li>更新至 Swift 4.1。</li>
|
||||
@ -81,6 +82,29 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<a name="swift_4_0_3"></a>
|
||||
### Swift 4.0.3 更新
|
||||
|
||||
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" width="100">发布日期</th>
|
||||
<th scope="col">语法变更记录</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td scope="row">2017-12-04</td>
|
||||
<td>
|
||||
<ul class="list-bullet">
|
||||
<li>更新至 Swift 4.0.3。</li>
|
||||
<li>更新 <a href='https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID563'>Key-Path 表达式</a>,现在 key path 支持下标子路径。</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<a name="swift_4_0"></a>
|
||||
### Swift 4.0 更新
|
||||
|
||||
@ -93,11 +117,18 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td scope="row">2017-12-04</td>
|
||||
<td scope="row">2017-09-19</td>
|
||||
<td>
|
||||
<ul class="list-bullet">
|
||||
<li>更新至 Swift 4.0.3。</li>
|
||||
<li>更新 <a href='https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID563'>Key-Path 表达式</a>,现在 key path 支持下标子路径。</li>
|
||||
<li>更新至 Swift 4.0。</li>
|
||||
<li>Added information about exclusive access to memory to the Memory Safety chapter.</li>
|
||||
<li>Added the Associated Types with a Generic Where Clause section, now that you can use generic where clauses to constrain associated types.</li>
|
||||
<li>Added information about multiline string literals to the String Literals section of the Strings and Characters chapter, and to the String Literals section of the Lexical Structure chapter.</li>
|
||||
<li>Updated the discussion of the objc attribute in Declaration Attributes, now that this attribute is inferred in fewer places.</li>
|
||||
<li>Added the Generic Subscripts section, now that subscripts can be generic.</li>
|
||||
<li>Updated the discussion in the Protocol Composition section of the Protocols chapter, and in the Protocol Composition Type section of the Types chapter, now that protocol composition types can contain a superclass requirement.</li>
|
||||
<li>Updated the discussion of protocol extensions in Extension Declaration now that final isn’t allowed in them.</li>
|
||||
<li>Added information about preconditions and fatal errors to the Assertions and Preconditions section.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -23,6 +23,9 @@
|
||||
> 4.0
|
||||
> 校对:[kemchenj](https://kemchenj.github.io)
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [常量和变量](#constants_and_variables)
|
||||
@ -55,7 +58,7 @@
|
||||
|
||||
Swift 是一门开发 iOS, macOS, watchOS 和 tvOS 应用的新语言。然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 Swift 的很多内容都是你熟悉的。
|
||||
|
||||
Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int`表示整型值; `Double` 和 `Float` 表示浮点型值; `Bool` 是布尔型值;`String` 是文本型数据。 Swift 还提供了三个基本的集合类型,`Array` ,`Set` 和 `Dictionary` ,详见[集合类型](04_Collection_Types.html)。
|
||||
Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int`表示整型值; `Double` 和 `Float` 表示浮点型值; `Bool` 是布尔型值;`String` 是文本型数据。 Swift 还提供了三个基本的集合类型,`Array` ,`Set` 和 `Dictionary` ,详见[集合类型](./04_Collection_Types.html)。
|
||||
|
||||
就像 C 语言一样,Swift 使用变量来进行存储并通过变量名来关联值。在 Swift 中,广泛的使用着值不可变的变量,它们就是常量,而且比 C 语言的常量更强大。在 Swift 中,如果你要处理的值不需要改变,那使用常量可以让你的代码更加安全并且更清晰地表达你的意图。
|
||||
|
||||
@ -124,13 +127,14 @@ welcomeMessage = "Hello"
|
||||
var red, green, blue: Double
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值,Swift可以推断出这个常量或者变量的类型,请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给 `welcomeMessage` 赋初始值,所以变量 `welcomeMessage` 的类型是通过一个类型标注指定的,而不是通过初始值推断的。
|
||||
|
||||
<a name="naming"></a>
|
||||
### 常量和变量的命名
|
||||
|
||||
你可以用任何你喜欢的字符作为常量和变量名,包括 Unicode 字符:
|
||||
常量和变量名可以包含任何字符,包括 Unicode 字符:
|
||||
|
||||
```swift
|
||||
let π = 3.14159
|
||||
@ -142,7 +146,8 @@ let 🐶🐮 = "dogcow"
|
||||
|
||||
一旦你将常量或者变量声明为确定的类型,你就不能使用相同的名字再次进行声明,或者改变其存储的值的类型。同时,你也不能将常量与变量进行互转。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名,你可以使用反引号(`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。
|
||||
|
||||
你可以更改现有的变量值为其他同类型的值,在下面的例子中,`friendlyWelcome`的值从`"Hello!"`改为了`"Bonjour!"`:
|
||||
@ -180,8 +185,9 @@ print("The current value of friendlyWelcome is \(friendlyWelcome)")
|
||||
// 输出 "The current value of friendlyWelcome is Bonjour!
|
||||
```
|
||||
|
||||
> 注意:
|
||||
字符串插值所有可用的选项,请参考[字符串插值](./03_Strings_and_Characters.html#string_interpolation)。
|
||||
> 注意
|
||||
>
|
||||
> 字符串插值所有可用的选项,请参考[字符串插值](./03_Strings_and_Characters.html#string_interpolation)。
|
||||
|
||||
<a name="comments"></a>
|
||||
## 注释
|
||||
@ -197,8 +203,8 @@ Swift 中的注释与 C 语言的注释非常相似。单行注释以双正斜
|
||||
你也可以进行多行注释,其起始标记为单个正斜杠后跟随一个星号(`/*`),终止标记为一个星号后跟随单个正斜杠(`*/`):
|
||||
|
||||
```swift
|
||||
/* 这是一个,
|
||||
多行注释 */
|
||||
/* 这也是一个注释,
|
||||
但是是多行的 */
|
||||
```
|
||||
|
||||
与 C 语言多行注释不同,Swift 的多行注释可以嵌套在其它的多行注释之中。你可以先生成一个多行注释块,然后在这个注释块之中再嵌套成第二个多行注释。终止注释时先插入第二个注释块的终止标记,然后再插入第一个注释块的终止标记:
|
||||
@ -258,8 +264,9 @@ Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台
|
||||
* 在32位平台上,`UInt` 和 `UInt32` 长度相同。
|
||||
* 在64位平台上,`UInt` 和 `UInt64` 长度相同。
|
||||
|
||||
> 注意:
|
||||
尽量不要使用`UInt`,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用`Int`,即使你要存储的值已知是非负的。统一使用`Int`可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,请参考[类型安全和类型推断](#type_safety_and_type_inference)。
|
||||
> 注意
|
||||
>
|
||||
> 尽量不要使用`UInt`,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用`Int`,即使你要存储的值已知是非负的。统一使用`Int`可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,请参考[类型安全和类型推断](#type_safety_and_type_inference)。
|
||||
|
||||
<a name="floating-point_numbers"></a>
|
||||
## 浮点数
|
||||
@ -271,8 +278,9 @@ Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台
|
||||
* `Double`表示64位浮点数。当你需要存储很大或者很高精度的浮点数时请使用此类型。
|
||||
* `Float`表示32位浮点数。精度要求不高的话可以使用此类型。
|
||||
|
||||
> 注意:
|
||||
`Double`精确度很高,至少有15位数字,而`Float`只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围,在两种类型都匹配的情况下,将优先选择 `Double`。
|
||||
> 注意
|
||||
>
|
||||
> `Double`精确度很高,至少有15位数字,而`Float`只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围,在两种类型都匹配的情况下,将优先选择 `Double`。
|
||||
|
||||
<a name="type_safety_and_type_inference"></a>
|
||||
## 类型安全和类型推断
|
||||
@ -415,8 +423,9 @@ let integerPi = Int(pi)
|
||||
|
||||
当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说 `4.75` 会变成 `4`,`-3.9` 会变成 `-3`。
|
||||
|
||||
> 注意:
|
||||
结合数字类常量和变量不同于结合数字类字面量。字面量`3`可以直接和字面量`0.14159`相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。
|
||||
> 注意
|
||||
>
|
||||
> 结合数字类常量和变量不同于结合数字类字面量。字面量`3`可以直接和字面量`0.14159`相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。
|
||||
|
||||
<a name="type_aliases"></a>
|
||||
## 类型别名
|
||||
@ -545,22 +554,19 @@ print("The status message is \(http200Status.description)")
|
||||
|
||||
作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个 `(Int, String)` 元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考[函数参数与返回值](./06_Functions.html#Function_Parameters_and_Return_Values)。
|
||||
|
||||
> 注意:
|
||||
元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考[类和结构体](./09_Classes_and_Structures.html)。
|
||||
> 注意
|
||||
>
|
||||
> 元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考[类和结构体](./09_Classes_and_Structures.html)。
|
||||
|
||||
<a name="optionals"></a>
|
||||
## 可选类型
|
||||
|
||||
使用*可选类型(optionals)*来处理值可能缺失的情况。可选类型表示:
|
||||
使用*可选类型(optionals)*来处理值可能缺失的情况。可选类型表示两种可能:
|
||||
或者有值, 你可以解析可选类型访问这个值, 或者根本没有值。
|
||||
|
||||
* 有值,等于 x
|
||||
|
||||
或者
|
||||
|
||||
* 没有值
|
||||
|
||||
> 注意:
|
||||
C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回`nil`,`nil`表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法一般会返回一个特殊值(比如`NSNotFound`)来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而,Swift 的可选类型可以让你暗示*任意类型*的值缺失,并不需要一个特殊值。
|
||||
> 注意
|
||||
>
|
||||
> C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回`nil`,`nil`表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法一般会返回一个特殊值(比如`NSNotFound`)来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而,Swift 的可选类型可以让你暗示*任意类型*的值缺失,并不需要一个特殊值。
|
||||
|
||||
来看一个例子。Swift 的 `Int` 类型有一种构造器,作用是将一个 `String` 值转换成一个 `Int` 值。然而,并不是所有的字符串都可以转换成一个整数。字符串 `"123"` 可以被转换成数字 `123` ,但是字符串 `"hello, world"` 不行。
|
||||
|
||||
@ -586,8 +592,9 @@ serverResponseCode = nil
|
||||
// serverResponseCode 现在不包含值
|
||||
```
|
||||
|
||||
> 注意:
|
||||
`nil`不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
|
||||
> 注意
|
||||
>
|
||||
> `nil`不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
|
||||
|
||||
如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 `nil`:
|
||||
|
||||
@ -596,8 +603,9 @@ var surveyAnswer: String?
|
||||
// surveyAnswer 被自动设置为 nil
|
||||
```
|
||||
|
||||
> 注意:
|
||||
Swift 的 `nil` 和 Objective-C 中的 `nil` 并不一样。在 Objective-C 中,`nil` 是一个指向不存在对象的指针。在 Swift 中,`nil` 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 `nil`,不只是对象类型。
|
||||
> 注意
|
||||
>
|
||||
> Swift 的 `nil` 和 Objective-C 中的 `nil` 并不一样。在 Objective-C 中,`nil` 是一个指向不存在对象的指针。在 Swift 中,`nil` 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 `nil`,不只是对象类型。
|
||||
|
||||
<a name="if"></a>
|
||||
### if 语句以及强制解析
|
||||
@ -622,10 +630,11 @@ if convertedNumber != nil {
|
||||
// 输出 "convertedNumber has an integer value of 123."
|
||||
```
|
||||
|
||||
更多关于 `if` 语句的内容,请参考[控制流](05_Control_Flow.html)。
|
||||
更多关于 `if` 语句的内容,请参考[控制流](./05_Control_Flow.html)。
|
||||
|
||||
> 注意:
|
||||
使用 `!` 来获取一个不存在的可选值会导致运行时错误。使用 `!` 来强制解析值之前,一定要确定可选包含一个非 `nil` 的值。
|
||||
> 注意
|
||||
>
|
||||
> 使用 `!` 来获取一个不存在的可选值会导致运行时错误。使用 `!` 来强制解析值之前,一定要确定可选包含一个非 `nil` 的值。
|
||||
|
||||
<a name="optional_binding"></a>
|
||||
### 可选绑定
|
||||
@ -640,7 +649,7 @@ if let constantName = someOptional {
|
||||
}
|
||||
```
|
||||
|
||||
你可以像上面这样使用可选绑定来重写 `possibleNumber` 这个[例子](./01_The_Basics.html#optionals):
|
||||
你可以像上面这样使用可选绑定来重写 在[可选类型](./01_The_Basics.html#optionals)举出的`possibleNumber`例子:
|
||||
|
||||
```swift
|
||||
if let actualNumber = Int(possibleNumber) {
|
||||
@ -677,8 +686,9 @@ if let firstNumber = Int("4") {
|
||||
// 输出 "4 < 42 < 100"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 在 `if` 条件语句中使用常量和变量来创建一个可选绑定,仅在 `if` 语句的句中(`body`)中才能获取到值。相反,在 `guard` 语句中使用常量和变量来创建一个可选绑定,仅在 `guard` 语句外且在语句后才能获取到值,请参考[提前退出](./05_Control_Flow#early_exit.html)。
|
||||
> 注意
|
||||
>
|
||||
> 在 `if` 条件语句中使用常量和变量来创建一个可选绑定,仅在 `if` 语句的句中(`body`)中才能获取到值。相反,在 `guard` 语句中使用常量和变量来创建一个可选绑定,仅在 `guard` 语句外且在语句后才能获取到值,请参考[提前退出](./05_Control_Flow.html#early_exit)。
|
||||
|
||||
<a name="implicityly_unwrapped_optionals"></a>
|
||||
### 隐式解析可选类型
|
||||
@ -689,7 +699,7 @@ if let firstNumber = Int("4") {
|
||||
|
||||
这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(`String?`)改成感叹号(`String!`)来声明一个隐式解析可选类型。
|
||||
|
||||
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考[无主引用以及隐式解析可选属性](./16_Automatic_Reference_Counting.html#unowned_references_and_implicitly_unwrapped_optional_properties)。
|
||||
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考[无主引用以及隐式解析可选属性](./23_Automatic_Reference_Counting.html#unowned_references_and_implicitly_unwrapped_optional_properties)。
|
||||
|
||||
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型 `String` 和隐式解析可选类型 `String` 之间的区别:
|
||||
|
||||
@ -703,7 +713,8 @@ let implicitString: String = assumedString // 不需要感叹号
|
||||
|
||||
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。
|
||||
|
||||
你仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:
|
||||
@ -724,7 +735,8 @@ if let definiteString = assumedString {
|
||||
// 输出 "An implicitly unwrapped optional string."
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 如果一个变量之后可能变成`nil`的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是`nil`的话,请使用普通可选类型。
|
||||
|
||||
<a name="error_handling"></a>
|
||||
@ -776,7 +788,7 @@ do {
|
||||
|
||||
如果没有错误被抛出,`eatASandwich()` 函数会被调用。如果一个匹配 `SandwichError.outOfCleanDishes` 的错误被抛出,`washDishes()` 函数会被调用。如果一个匹配 `SandwichError.missingIngredients` 的错误被抛出,`buyGroceries(_:)` 函数会被调用,并且使用 `catch` 所捕捉到的关联值 `[String]` 作为参数。
|
||||
|
||||
抛出,捕捉,以及传播错误会在[错误处理](./18_Error_Handling.html)章节详细说明。
|
||||
抛出,捕捉,以及传播错误会在[错误处理](./17_Error_Handling.html)章节详细说明。
|
||||
|
||||
<a name="assertions_and_Preconditions"></a>
|
||||
## 断言和先决条件
|
||||
@ -785,7 +797,7 @@ do {
|
||||
|
||||
你使用断言和先决条件来表达你所做的假设和你在编码时候的期望。你可以将这些包含在你的代码中。断言帮助你在开发阶段找到错误和不正确的假设,先决条件帮助你在生产环境中探测到存在的问题。
|
||||
|
||||
除了在运行时验证你的期望值,断言和先决条件也变成了一个在你的代码中的有用的文档形式。和在上面讨论过的[错误处理](./18_Error_Handling.html)不同,断言和先决条件并不是用来处理可以恢复的或者可预期的错误。因为一个断言失败表明了程序正处于一个无效的状态,没有办法去捕获一个失败的断言。
|
||||
除了在运行时验证你的期望值,断言和先决条件也变成了一个在你的代码中的有用的文档形式。和在上面讨论过的[错误处理](./17_Error_Handling.html)不同,断言和先决条件并不是用来处理可以恢复的或者可预期的错误。因为一个断言失败表明了程序正处于一个无效的状态,没有办法去捕获一个失败的断言。
|
||||
|
||||
使用断言和先决条件不是一个能够避免出现程序出现无效状态的编码方法。然而,如果一个无效状态程序产生了,断言和先决条件可以强制检查你的数据和程序状态,使得你的程序可预测的中止(译者:不是系统强制的,被动的中止),并帮助使这个问题更容易调试。一旦探测到无效的状态,执行则被中止,防止无效的状态导致的进一步对于系统的伤害。
|
||||
|
||||
@ -823,7 +835,7 @@ if age > 10 {
|
||||
|
||||
### 强制执行先决条件
|
||||
|
||||
当一个条件可能为false(假),但是继续执行代码要求条件必须为true(真)的时候,需要使用先决条件。例如使用先决条件来检查是否下标越界,或者来检查是否将一个正确的参数传给函数。
|
||||
当一个条件可能为假,但是继续执行代码要求条件必须为真的时候,需要使用先决条件。例如使用先决条件来检查是否下标越界,或者来检查是否将一个正确的参数传给函数。
|
||||
|
||||
你可以使用全局 `precondition(_:_:file:line:)` 函数来写一个先决条件。向这个函数传入一个结果为 `true` 或者 `false` 的表达式以及一条信息,当表达式的结果为 `false` 的时候这条信息会被显示:
|
||||
|
||||
@ -834,6 +846,8 @@ precondition(index > 0, "Index must be greater than zero.")
|
||||
|
||||
你可以调用 `precondition(_:_:file:line:)`方法来表明出现了一个错误,例如,switch 进入了 default 分支,但是所有的有效值应该被任意一个其他分支(非 default 分支)处理。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 如果你使用unchecked模式(-Ounchecked)编译代码,先决条件将不会进行检查。编译器假设所有的先决条件总是为true(真),他将优化你的代码。然而,`fatalError(_:file:line:)`函数总是中断执行,无论你怎么进行优化设定。
|
||||
>
|
||||
>你能使用 `fatalError(_:file:line:)`函数在设计原型和早期开发阶段,这个阶段只有方法的声明,但是没有具体实现,你可以在方法体中写上fatalError("Unimplemented")作为具体实现。因为fatalError不会像断言和先决条件那样被优化掉,所以你可以确保当代码执行到一个没有被实现的方法时,程序会被中断。
|
||||
|
||||
@ -18,6 +18,9 @@
|
||||
> 4.0
|
||||
> 翻译+校对:[kemchenj](https://kemchenj.github.io)
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [术语](#terminology)
|
||||
@ -32,11 +35,11 @@
|
||||
|
||||
*运算符*是检查、改变、合并值的特殊符号或短语。例如,加号(`+`)将两个数相加(如 `let i = 1 + 2`)。更复杂的运算例子包括逻辑与运算符 `&&`(如 `if enteredDoorCode && passedRetinaScan`)。
|
||||
|
||||
Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。算术运算符(`+`,`-`,`*`,`/`,`%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](../chapter2/25_Advanced_Operators.html#overflow_operators)。
|
||||
Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。算术运算符(`+`,`-`,`*`,`/`,`%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](./26_Advanced_Operators.html#overflow_operators)。
|
||||
|
||||
Swift 还提供了 C 语言没有的区间运算符,例如 `a..<b` 或 `a...b`,这方便我们表达一个区间内的数值。
|
||||
|
||||
本章节只描述了 Swift 中的基本运算符,[高级运算符](../chapter2/25_Advanced_Operators.html)这章会包含 Swift 中的高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
|
||||
本章节只描述了 Swift 中的基本运算符,[高级运算符](./26_Advanced_Operators.html)这章会包含 Swift 中的高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
|
||||
|
||||
<a name="terminology"></a>
|
||||
## 术语
|
||||
@ -68,7 +71,7 @@ let (x, y) = (1, 2)
|
||||
// 现在 x 等于 1,y 等于 2
|
||||
```
|
||||
|
||||
与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。所以以下代码是错误的:
|
||||
与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。所以以下陈述时无效的:
|
||||
|
||||
```swift
|
||||
if x = y {
|
||||
@ -76,7 +79,7 @@ if x = y {
|
||||
}
|
||||
```
|
||||
|
||||
这个特性使你无法把(`==`)错写成(`=`),由于 `if x = y` 是错误代码,Swift 能帮你避免此类错误发生。
|
||||
这个特性使你无法把(`==`)错写成(`=`),由于 `if x = y` 是无效的,Swift 能帮你避免此类错误发生。
|
||||
|
||||
<a name="arithmetic_operators"></a>
|
||||
## 算术运算符
|
||||
@ -95,7 +98,7 @@ Swift 中所有数值类型都支持了基本的四则*算术运算符*:
|
||||
10.0 / 2.5 // 等于 4.0
|
||||
```
|
||||
|
||||
与 C 语言和 Objective-C 不同的是,Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如 `a &+ b`)。详情参见[溢出运算符](../chapter2/25_Advanced_Operators.html#overflow_operators)。
|
||||
与 C 语言和 Objective-C 不同的是,Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如 `a &+ b`)。详情参见[溢出运算符](./26_Advanced_Operators.html#overflow_operators)。
|
||||
|
||||
加法运算符也可用于 `String` 的拼接:
|
||||
|
||||
@ -107,8 +110,9 @@ Swift 中所有数值类型都支持了基本的四则*算术运算符*:
|
||||
|
||||
*求余运算符*(`a % b`)是计算 `b` 的多少倍刚刚好可以容入`a`,返回多出来的那部分(余数)。
|
||||
|
||||
> 注意:
|
||||
求余运算符(`%`)在其他语言也叫*取模运算符*。但是严格说来,我们看该运算符对负数的操作结果,「求余」比「取模」更合适些。
|
||||
> 注意
|
||||
>
|
||||
> 求余运算符(`%`)在其他语言也叫*取模运算符*。但是严格说来,我们看该运算符对负数的操作结果,「求余」比「取模」更合适些。
|
||||
|
||||
我们来谈谈取余是怎么回事,计算 `9 % 4`,你先计算出 `4` 的多少倍会刚好可以容入 `9` 中:
|
||||
|
||||
@ -182,8 +186,9 @@ a += 2
|
||||
|
||||
表达式 `a += 2` 是 `a = a + 2` 的简写,一个组合加运算就是把加法运算和赋值运算组合成进一个运算符里,同时完成两个运算任务。
|
||||
|
||||
> 注意:
|
||||
复合赋值运算没有返回值,`let b = a += 2`这类代码是错误。这不同于上面提到的自增和自减运算符。
|
||||
> 注意
|
||||
>
|
||||
> 复合赋值运算没有返回值,`let b = a += 2`这类代码是错误。这不同于上面提到的自增和自减运算符。
|
||||
|
||||
更多 Swift 标准库运算符的信息,请看[运算符声明](https://developer.apple.com/documentation/swift/operator_declarations)。
|
||||
|
||||
@ -199,8 +204,9 @@ a += 2
|
||||
- 大于等于(`a >= b`)
|
||||
- 小于等于(`a <= b`)
|
||||
|
||||
> 注意:
|
||||
Swift 也提供恒等(`===`)和不恒等(`!==`)这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](../chapter2/09_Classes_and_Structures.html)。
|
||||
> 注意
|
||||
>
|
||||
> Swift 也提供恒等(`===`)和不恒等(`!==`)这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](./09_Classes_and_Structures.html)。
|
||||
|
||||
每个比较运算都返回了一个标识表达式是否成立的布尔值:
|
||||
|
||||
@ -225,7 +231,7 @@ if name == "world" {
|
||||
// 输出 "hello, world", 因为 `name` 就是等于 "world"
|
||||
```
|
||||
|
||||
关于 `if` 语句,请看[控制流](../chapter2/05_Control_Flow.html)。
|
||||
关于 `if` 语句,请看[控制流](./05_Control_Flow.html)。
|
||||
|
||||
如果两个元组的元素相同,且长度相同的话,元组就可以被比较。比较元组大小会按照从左到右、逐值比较的方式,直到发现有两个值不等时停止。如果所有的值都相等,那么这一对元组我们就称它们是相等的。例如:
|
||||
|
||||
@ -244,15 +250,16 @@ if name == "world" {
|
||||
("blue", false) < ("purple", true) // 错误,因为 < 不能比较布尔类型
|
||||
```
|
||||
|
||||
>注意:
|
||||
Swift 标准库只能比较七个以内元素的元组比较函数。如果你的元组元素超过七个时,你需要自己实现比较运算符。
|
||||
>注意
|
||||
>
|
||||
>Swift 标准库只能比较七个以内元素的元组比较函数。如果你的元组元素超过七个时,你需要自己实现比较运算符。
|
||||
|
||||
<a name="ternary_conditional_operator"></a>
|
||||
## 三目运算符(Ternary Conditional Operator)
|
||||
## 三元运算符(Ternary Conditional Operator)
|
||||
|
||||
*三目运算符*的特殊在于它是有三个操作数的运算符,它的形式是 `问题 ? 答案 1 : 答案 2`。它简洁地表达根据 `问题`成立与否作出二选一的操作。如果 `问题` 成立,返回 `答案 1` 的结果;反之返回 `答案 2` 的结果。
|
||||
*三元运算符*的特殊在于它是有三个操作数的运算符,它的形式是 `问题 ? 答案 1 : 答案 2`。它简洁地表达根据 `问题`成立与否作出二选一的操作。如果 `问题` 成立,返回 `答案 1` 的结果;反之返回 `答案 2` 的结果。
|
||||
|
||||
三目运算符是以下代码的缩写形式:
|
||||
三元运算符是以下代码的缩写形式:
|
||||
|
||||
```swift
|
||||
if question {
|
||||
@ -285,9 +292,9 @@ if hasHeader {
|
||||
// rowHeight 现在是 90
|
||||
```
|
||||
|
||||
第一段代码例子使用了三目运算,所以一行代码就能让我们得到正确答案。这比第二段代码简洁得多,无需将 `rowHeight` 定义成变量,因为它的值无需在 `if` 语句中改变。
|
||||
第一段代码例子使用了三元运算,所以一行代码就能让我们得到正确答案。这比第二段代码简洁得多,无需将 `rowHeight` 定义成变量,因为它的值无需在 `if` 语句中改变。
|
||||
|
||||
三目运算提供有效率且便捷的方式来表达二选一的选择。需要注意的事,过度使用三目运算符会使简洁的代码变的难懂。我们应避免在一个组合语句中使用多个三目运算符。
|
||||
三元运算提供有效率且便捷的方式来表达二选一的选择。需要注意的事,过度使用三元运算符会使简洁的代码变的难懂。我们应避免在一个组合语句中使用多个三元运算符。
|
||||
|
||||
<a name="nil_coalescing_operator"></a>
|
||||
## 空合运算符(Nil Coalescing Operator)
|
||||
@ -300,10 +307,11 @@ if hasHeader {
|
||||
a != nil ? a! : b
|
||||
```
|
||||
|
||||
上述代码使用了三目运算符。当可选类型 `a` 的值不为空时,进行强制解封(`a!`),访问 `a` 中的值;反之返回默认值 `b`。无疑空合运算符(`??`)提供了一种更为优雅的方式去封装条件判断和解封两种行为,显得简洁以及更具可读性。
|
||||
上述代码使用了三元运算符。当可选类型 `a` 的值不为空时,进行强制解封(`a!`),访问 `a` 中的值;反之返回默认值 `b`。无疑空合运算符(`??`)提供了一种更为优雅的方式去封装条件判断和解封两种行为,显得简洁以及更具可读性。
|
||||
|
||||
> 注意:
|
||||
如果 `a` 为非空值(`non-nil`),那么值 `b` 将不会被计算。这也就是所谓的*短路求值*。
|
||||
> 注意
|
||||
>
|
||||
> 如果 `a` 为非空值(`non-nil`),那么值 `b` 将不会被计算。这也就是所谓的*短路求值*。
|
||||
|
||||
下文例子采用空合运算符,实现了在默认颜色名和可选自定义颜色名之间抉择:
|
||||
|
||||
@ -318,7 +326,7 @@ var colorNameToUse = userDefinedColorName ?? defaultColorName
|
||||
`userDefinedColorName` 变量被定义为一个可选的 `String` 类型,默认值为 `nil`。由于 `userDefinedColorName` 是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为 `colorNameToUse` 的变量赋予一个字符串类型初始值。
|
||||
由于 `userDefinedColorName` 值为空,因此表达式 `userDefinedColorName ?? defaultColorName` 返回 `defaultColorName` 的值,即 `red`。
|
||||
|
||||
另一种情况,分配一个非空值(`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在 `userDefaultColorName` 中的值,而非默认值。
|
||||
如果你分配一个非空值(`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在 `userDefaultColorName` 中的值,而非默认值。
|
||||
|
||||
```swift
|
||||
userDefinedColorName = "green"
|
||||
@ -334,7 +342,7 @@ Swift 提供了几种方便表达一个区间的值的*区间运算符*。
|
||||
### 闭区间运算符
|
||||
|
||||
*闭区间运算符*(`a...b`)定义一个包含从 `a` 到 `b`(包括 `a` 和 `b`)的所有值的区间。`a` 的值不能超过 `b`。
|
||||
|
||||
|
||||
闭区间运算符在迭代一个区间的所有值时是非常有用的,如在 `for-in` 循环中:
|
||||
|
||||
```swift
|
||||
@ -348,7 +356,8 @@ for index in 1...5 {
|
||||
// 5 * 5 = 25
|
||||
```
|
||||
|
||||
关于 `for-in`,请看[控制流](../chapter2/05_Control_Flow.html)。
|
||||
关于 `for-in`循环,请看[控制流](./05_Control_Flow.html)。
|
||||
|
||||
|
||||
### 半开区间运算符
|
||||
|
||||
@ -369,7 +378,7 @@ for i in 0..<count {
|
||||
// 第 4 个人叫 Jack
|
||||
```
|
||||
|
||||
数组有 4 个元素,但 `0..<count` 只数到3(最后一个元素的下标),因为它是半开区间。关于数组,请查阅[数组](../chapter2/04_Collection_Types.html#arrays)。
|
||||
数组有 4 个元素,但 `0..<count` 只数到3(最后一个元素的下标),因为它是半开区间。关于数组,请查阅[数组](./04_Collection_Types.html#arrays)。
|
||||
|
||||
### 单侧区间
|
||||
|
||||
@ -493,8 +502,9 @@ if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
|
||||
|
||||
前两种情况,我们都不满足,所以前两个简单逻辑的结果是 `false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是 `true`。
|
||||
|
||||
> 注意:
|
||||
Swift 逻辑操作符 `&&` 和 `||` 是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。
|
||||
> 注意
|
||||
>
|
||||
> Swift 逻辑操作符 `&&` 和 `||` 是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。
|
||||
|
||||
### 使用括号来明确优先级
|
||||
|
||||
|
||||
@ -22,6 +22,9 @@
|
||||
> 4.0
|
||||
> 翻译:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [字符串字面量](#string_literals)
|
||||
@ -39,17 +42,18 @@
|
||||
- [比较字符串](#comparing_strings)
|
||||
- [字符串的 Unicode 表示形式](#unicode_representations_of_strings)
|
||||
|
||||
*字符串*是例如`"hello, world"`,`"albatross"`这样的有序的`Character`(字符)类型的值的集合。通过`String`类型来表示。
|
||||
*字符串*是是一系列字符的集合,例如`"hello, world"`,`"albatross"`。Swift的字符串通过`String`类型来表示。
|
||||
一个`String`的内容可以用许多方式读取,包括作为一个`Character`值的集合。
|
||||
|
||||
Swift 的`String`和`Character`类型提供了快速和兼容 Unicode 的方式供你的代码使用。创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。
|
||||
字符串连接操作只需要简单地通过`+`符号将两个字符串相连即可。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你也可以在字符串内插过程中使用字符串插入常量、变量、字面量表达成更长的字符串,这样可以很容易的创建自定义的字符串值,进行展示、存储以及打印。
|
||||
Swift 的`String`和`Character`类型提供了快速和兼容 Unicode 的方式供你的代码使用。创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。字符串连接操作只需要简单地通过`+`符号将两个字符串相连即可。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你也可以在字符串内插过程中使用字符串插入常量、变量、字面量表达成更长的字符串,这样可以很容易的创建自定义的字符串值,进行展示、存储以及打印。
|
||||
|
||||
尽管语法简易,但`String`类型是一种快速、现代化的字符串实现。
|
||||
每一个字符串都是由编码无关的 Unicode 字符组成,并支持访问字符的多种 Unicode 表示形式(representations)。
|
||||
每一个字符串都是由编码无关的 Unicode 字符组成,并支持访问字符的多种 Unicode 表示形式。
|
||||
|
||||
> 注意:
|
||||
> Swift 的`String`类型与 Foundation `NSString`类进行了无缝桥接。Foundation 也可以对`String`进行扩展,暴露在`NSString`中定义的方法。 这意味着,如果你在`String`中调用这些`NSString`的方法,将不用进行转换。
|
||||
> 注意
|
||||
>
|
||||
> Swift 的`String`类型与 Foundation `NSString`类进行了无缝桥接。Foundation也可以对`String`进行扩展,暴露在`NSString`中定义的方法。 这意味着,如果你在`String`中调用这些`NSString`的方法,将不用进行转换。
|
||||
>
|
||||
> 更多关于在 Foundation 和 Cocoa 中使用`String`的信息请查看 *[Using Swift with Cocoa and Objective-C (Swift 4)](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)*。
|
||||
|
||||
<a name="string_literals"></a>
|
||||
@ -68,7 +72,7 @@ let someString = "Some string literal value"
|
||||
<a name="multiline_string_literals"></a>
|
||||
### 多行字符串字面量
|
||||
|
||||
如果你需要一个字符串是跨越多行的,那就使用多行字符串字面量 —— 由一对三个双引号包裹着的具有固定顺序的文本字符集:
|
||||
如果你需要一个字符串是跨越多行的,那就使用多行字符串字面量 — 由一对三个双引号包裹着的具有固定顺序的文本字符集:
|
||||
|
||||
```swift
|
||||
let quotation = """
|
||||
@ -182,8 +186,9 @@ constantString += " and another Highlander"
|
||||
// 这会报告一个编译错误 (compile-time error) - 常量字符串不可以被修改。
|
||||
```
|
||||
|
||||
> 注意:
|
||||
在 Objective-C 和 Cocoa 中,您需要通过选择两个不同的类(`NSString`和`NSMutableString`)来指定字符串是否可以被修改。
|
||||
> 注意
|
||||
>
|
||||
> 在 Objective-C 和 Cocoa 中,您需要通过选择两个不同的类(`NSString`和`NSMutableString`)来指定字符串是否可以被修改。
|
||||
|
||||
<a name="strings_are_value_types"></a>
|
||||
## 字符串是值类型
|
||||
@ -259,8 +264,9 @@ welcome.append(exclamationMark)
|
||||
// welcome 现在等于 "hello there!"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。
|
||||
> 注意
|
||||
>
|
||||
> 您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。
|
||||
|
||||
如果你需要使用多行字符串字面量来拼接字符串,并且你需要字符串每一行都以换行符结尾,包括最后一行:
|
||||
|
||||
@ -310,7 +316,8 @@ let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
|
||||
该表达式计算`Double(multiplier) * 2.5`的值并将结果 (`7.5`) 插入到字符串中。
|
||||
在这个例子中,表达式写为`\(Double(multiplier) * 2.5)`并包含在字符串字面量中。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 插值字符串中写在括号中的表达式不能包含非转义反斜杠 (`\`),并且不能包含回车或换行符。不过,插值字符串可以包含其他字面量。
|
||||
|
||||
<a name="unicode"></a>
|
||||
@ -326,7 +333,8 @@ Swift 的`String`和`Character`类型是完全兼容 Unicode 标准的。
|
||||
Swift 的`String`类型是基于 *Unicode 标量* 建立的。
|
||||
Unicode 标量是对应字符或者修饰符的唯一的21位数字,例如`U+0061`表示小写的拉丁字母(`LATIN SMALL LETTER A`)("`a`"),`U+1F425`表示小鸡表情(`FRONT-FACING BABY CHICK`) ("`🐥`")。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> Unicode *码位(code poing)* 的范围是`U+0000`到`U+D7FF`或者`U+E000`到`U+10FFFF`。Unicode 标量不包括 Unicode *代理项(surrogate pair)* 码位,其码位范围是`U+D800`到`U+DFFF`。
|
||||
|
||||
注意不是所有的21位 Unicode 标量都代表一个字符,因为有一些标量是留作未来分配的。已经代表一个典型字符的标量都有自己的名字,例如上面例子中的`LATIN SMALL LETTER A`和`FRONT-FACING BABY CHICK`。
|
||||
@ -400,7 +408,8 @@ print("the number of characters in \(word) is \(word.count)")
|
||||
// 打印输出 "the number of characters in café is 4"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 可扩展的字符群集可以组成一个或者多个 Unicode 标量。这意味着不同的字符以及相同字符的不同表示方式可能需要不同数量的内存空间来存储。所以 Swift 中的字符在一个字符串中并不一定占用相同的内存空间数量。因此在没有获取字符串的可扩展的字符群的范围时候,就不能计算出字符串的字符数量。如果您正在处理一个长字符串,需要注意`count`属性必须遍历全部的 Unicode 标量,来确定字符串的字符数量。
|
||||
>
|
||||
> 另外需要注意的是通过`count`属性返回的字符数量并不总是与包含相同字符的`NSString`的`length`属性相同。`NSString`的`length`属性是利用 UTF-16 表示的十六位代码单元数字,而不是 Unicode 可扩展的字符群集。
|
||||
@ -452,7 +461,8 @@ for index in greeting.indices {
|
||||
// 打印输出 "G u t e n T a g ! "
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 您可以使用 `startIndex` 和 `endIndex` 属性或者 `index(before:)` 、`index(after:)` 和 `index(_:offsetBy:)` 方法在任意一个确认的并遵循 `Collection` 协议的类型里面,如上文所示是使用在 `String` 中,您也可以使用在 `Array`、`Dictionary` 和 `Set`中。
|
||||
|
||||
<a name="inserting_and_removing"></a>
|
||||
@ -480,7 +490,8 @@ welcome.removeSubrange(range)
|
||||
// welcome 现在等于 "hello"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 您可以使用 `insert(_:at:)`、`insert(contentsOf:at:)`、`remove(at:)` 和 `removeSubrange(_:)` 方法在任意一个确认的并遵循 `RangeReplaceableCollection` 协议的类型里面,如上文所示是使用在 `String` 中,您也可以使用在 `Array`、`Dictionary` 和 `Set` 中。
|
||||
|
||||
<a name="substrings"></a>
|
||||
@ -505,7 +516,8 @@ let newString = String(beginning)
|
||||

|
||||
|
||||
> 注意
|
||||
`String` 和 `SubString` 都遵循 `StringProtocol<//apple_ref/swift/intf/s:s14StringProtocolP>` 协议,这意味着操作字符串的函数使用 `StringProtocol` 会更加方便。你可以传入 `String` 或 `SubString` 去调用函数。
|
||||
>
|
||||
> `String` 和 `SubString` 都遵循 `StringProtocol<//apple_ref/swift/intf/s:s14StringProtocolP>` 协议,这意味着操作字符串的函数使用 `StringProtocol` 会更加方便。你可以传入 `String` 或 `SubString` 去调用函数。
|
||||
|
||||
<a name="comparing_strings"></a>
|
||||
## 比较字符串
|
||||
@ -556,7 +568,8 @@ if latinCapitalLetterA != cyrillicCapitalLetterA {
|
||||
// 打印 "These two characters are not equivalent"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 在 Swift 中,字符串和字符并不区分地域(not locale-sensitive)。
|
||||
|
||||
<a name="prefix_and_suffix_equality"></a>
|
||||
@ -611,7 +624,8 @@ print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
|
||||
// 打印输出 "6 mansion scenes; 2 cell scenes"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> `hasPrefix(_:)`和`hasSuffix(_:)`方法都是在每个字符串中逐字符比较其可扩展的字符群集是否标准相等,详细描述在[字符串/字符相等](#string_and_character_equality)。
|
||||
|
||||
<a name="unicode_representations_of_strings"></a>
|
||||
@ -785,6 +799,7 @@ print("")
|
||||
```
|
||||
|
||||
前三个`UnicodeScalar`值(`68`, `111`, `103`)的`value`属性仍然代表字符`D`、`o`和`g`。
|
||||
|
||||
第四个`codeUnit`值(`8252`)仍然是一个等于十六进制`203C`的十进制值。这个代表了`DOUBLE EXCLAMATION MARK`字符的 Unicode 标量`U+203C`。
|
||||
|
||||
第五个`UnicodeScalar`值的`value`属性,`128054`,是一个十六进制`1F436`的十进制表示。其等同于`DOG FACE`的 Unicode 标量`U+1F436`。
|
||||
|
||||
@ -18,6 +18,9 @@
|
||||
> 校对:[shanks](http://codebuild.me) ,2016-10-09
|
||||
> 3.0.1,shanks,2016-11-12
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [集合的可变性](#mutability_of_collections)
|
||||
@ -30,27 +33,29 @@ Swift 语言提供`Arrays`、`Sets`和`Dictionaries`三种基本的*集合类型
|
||||
|
||||

|
||||
|
||||
Swift 语言中的`Arrays`、`Sets`和`Dictionaries`中存储的数据值类型必须明确。这意味着我们不能把不正确的数据类型插入其中。同时这也说明我们完全可以对取回值的类型非常自信。
|
||||
Swift 语言中的`Arrays`、`Sets`和`Dictionaries`中存储的数据值类型必须明确。这意味着我们不能把错误的数据类型插入其中。同时这也说明你完全可以对取回值的类型非常放心。
|
||||
|
||||
> 注意:
|
||||
Swift 的`Arrays`、`Sets`和`Dictionaries`类型被实现为*泛型集合*。更多关于泛型类型和集合,参见 [泛型](./23_Generics.html)章节。
|
||||
> 注意
|
||||
>
|
||||
> Swift 的`Arrays`、`Sets`和`Dictionaries`类型被实现为*泛型集合*。更多关于泛型类型和集合,参见 [泛型](./23_Generics.html)章节。
|
||||
|
||||
<a name="mutability_of_collections"></a>
|
||||
## 集合的可变性
|
||||
|
||||
如果创建一个`Arrays`、`Sets`或`Dictionaries`并且把它分配成一个变量,这个集合将会是*可变的*。这意味着我们可以在创建之后添加更多或移除已存在的数据项,或者改变集合中的数据项。如果我们把`Arrays`、`Sets`或`Dictionaries`分配成常量,那么它就是*不可变的*,它的大小和内容都不能被改变。
|
||||
如果创建一个`Arrays`、`Sets`或`Dictionaries`并且把它分配成一个变量,这个集合将会是*可变的*。这意味着你可以在创建之后添加更多或移除已存在的数据项,或者改变集合中的数据项。如果我们把`Arrays`、`Sets`或`Dictionaries`分配成常量,那么它就是*不可变的*,它的大小和内容都不能被改变。
|
||||
|
||||
> 注意:
|
||||
在我们不需要改变集合的时候创建不可变集合是很好的实践。如此 Swift 编译器可以优化我们创建的集合。
|
||||
> 注意
|
||||
>
|
||||
> 在我们不需要改变集合的时候创建不可变集合是很好的实践。如此 Swift 编译器可以优化我们创建的集合。
|
||||
|
||||
<a name="arrays"></a>
|
||||
## 数组(Arrays)
|
||||
|
||||
*数组*使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。
|
||||
|
||||
> 注意:
|
||||
Swift 的`Array`类型被桥接到`Foundation`中的`NSArray`类。
|
||||
更多关于在`Foundation`和`Cocoa`中使用`Array`的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 3.0.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
|
||||
> 注意
|
||||
>
|
||||
> Swift 的`Array`类型被桥接到`Foundation`中的`NSArray`类。更多关于在`Foundation`和`Cocoa`中使用`Array`的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
|
||||
|
||||
<a name="array_type_shorthand_syntax"></a>
|
||||
### 数组的简单语法
|
||||
@ -118,8 +123,9 @@ var shoppingList: [String] = ["Eggs", "Milk"]
|
||||
|
||||
`shoppingList`变量被声明为“字符串值类型的数组“,记作`[String]`。 因为这个数组被规定只有`String`一种数据结构,所以只有`String`类型可以在其中被存取。 在这里,`shoppingList`数组由两个`String`值(`"Eggs"` 和`"Milk"`)构造,并且由数组字面量定义。
|
||||
|
||||
> 注意:
|
||||
`shoppingList`数组被声明为变量(`var`关键字创建)而不是常量(`let`创建)是因为以后可能会有更多的数据项被插入其中。
|
||||
> 注意
|
||||
>
|
||||
> `shoppingList`数组被声明为变量(`var`关键字创建)而不是常量(`let`创建)是因为以后可能会有更多的数据项被插入其中。
|
||||
|
||||
在这个例子中,字面量仅仅包含两个`String`值。匹配了该数组的变量声明(只能包含`String`的数组),所以这个字面量的分配过程可以作为用两个初始项来构造`shoppingList`的一种方式。
|
||||
|
||||
@ -177,8 +183,9 @@ var firstItem = shoppingList[0]
|
||||
// 第一项是 "Eggs"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
第一项在数组中的索引值是`0`而不是`1`。 Swift 中的数组索引总是从零开始。
|
||||
> 注意
|
||||
>
|
||||
> 第一项在数组中的索引值是`0`而不是`1`。 Swift 中的数组索引总是从零开始。
|
||||
|
||||
我们也可以用下标来改变某个已有索引值对应的数据值:
|
||||
|
||||
@ -194,8 +201,9 @@ shoppingList[4...6] = ["Bananas", "Apples"]
|
||||
// shoppingList 现在有6项
|
||||
```
|
||||
|
||||
> 注意:
|
||||
不可以用下标访问的形式去在数组尾部添加新项。
|
||||
> 注意
|
||||
>
|
||||
> 不可以用下标访问的形式去在数组尾部添加新项。
|
||||
|
||||
调用数组的`insert(_:at:)`方法来在某个具体索引值之前添加数据项:
|
||||
|
||||
@ -215,8 +223,9 @@ let mapleSyrup = shoppingList.remove(at: 0)
|
||||
// shoppingList 现在只有6项,而且不包括 Maple Syrup
|
||||
// mapleSyrup 常量的值等于被移除数据项的值 "Maple Syrup"
|
||||
```
|
||||
> 注意:
|
||||
如果我们试着对索引越界的数据进行检索或者设置新值的操作,会引发一个运行期错误。我们可以使用索引值和数组的`count`属性进行比较来在使用某个索引之前先检验是否有效。除了当`count`等于 0 时(说明这是个空数组),最大索引值一直是`count - 1`,因为数组都是零起索引。
|
||||
> 注意
|
||||
>
|
||||
> 如果我们试着对索引越界的数据进行检索或者设置新值的操作,会引发一个运行期错误。我们可以使用索引值和数组的`count`属性进行比较来在使用某个索引之前先检验是否有效。除了当`count`等于 0 时(说明这是个空数组),最大索引值一直是`count - 1`,因为数组都是零起索引。
|
||||
|
||||
数据项被移除后数组中的空出项会被自动填补,所以现在索引值为`0`的数据项的值再次等于`"Six eggs"`:
|
||||
|
||||
@ -270,9 +279,10 @@ for (index, value) in shoppingList. enumerated() {
|
||||
|
||||
*集合(Set)*用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以使用集合而不是数组。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
> Swift的`Set`类型被桥接到`Foundation`中的`NSSet`类。
|
||||
> 关于使用`Foundation`和`Cocoa`中`Set`的知识,参见 [*Using Swift with Cocoa and Obejective-C(Swift 3.0.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
|
||||
>
|
||||
> 关于使用`Foundation`和`Cocoa`中`Set`的知识,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
|
||||
|
||||
<a name="hash_values_for_set_types"></a>
|
||||
#### 集合类型的哈希值
|
||||
@ -281,9 +291,10 @@ for (index, value) in shoppingList. enumerated() {
|
||||
|
||||
Swift 的所有基本类型(比如`String`,`Int`,`Double`和`Bool`)默认都是可哈希化的,可以作为集合的值的类型或者字典的键的类型。没有关联值的枚举成员值(在[枚举](./08_Enumerations.html)有讲述)默认也是可哈希化的。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 你可以使用你自定义的类型作为集合的值的类型或者是字典的键的类型,但你需要使你的自定义类型符合 Swift 标准库中的`Hashable`协议。符合`Hashable`协议的类型需要提供一个类型为`Int`的可读属性`hashValue`。由类型的`hashValue`属性返回的值不需要在同一程序的不同执行周期或者不同程序之间保持相同。
|
||||
|
||||
>
|
||||
> 因为`Hashable`协议符合`Equatable`协议,所以遵循该协议的类型也必须提供一个"是否相等"运算符(`==`)的实现。这个`Equatable`协议要求任何符合`==`实现的实例间都是一种相等的关系。也就是说,对于`a,b,c`三个值来说,`==`的实现必须满足下面三种情况:
|
||||
|
||||
> * `a == a`(自反性)
|
||||
@ -308,7 +319,8 @@ print("letters is of type Set<Character> with \(letters.count) items.")
|
||||
// 打印 "letters is of type Set<Character> with 0 items."
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 通过构造器,这里的`letters`变量的类型被推断为`Set<Character>`。
|
||||
|
||||
此外,如果上下文提供了类型信息,比如作为函数的参数或者已知类型的变量或常量,我们可以通过一个空的数组字面量创建一个空的`Set`:
|
||||
@ -334,7 +346,8 @@ var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
|
||||
|
||||
这个`favoriteGenres`变量被声明为“一个`String`值的集合”,写为`Set<String>`。由于这个特定的集合含有指定`String`类型的值,所以它只允许存储`String`类型值。这里的`favoriteGenres`变量有三个`String`类型的初始值(`"Rock"`,`"Classical"`和`"Hip hop"`),并以数组字面量的方式出现。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> `favoriteGenres`被声明为一个变量(拥有`var`标示符)而不是一个常量(拥有`let`标示符),因为它里面的元素将会在下面的例子中被增加或者移除。
|
||||
|
||||
一个`Set`类型不能从数组字面量中被单独推断出来,因此`Set`类型必须显式声明。然而,由于 Swift 的类型推断功能,如果你想使用一个数组字面量构造一个`Set`并且该数组字面量中的所有元素类型相同,那么你无须写出`Set`的具体类型。`favoriteGenres`的构造形式可以采用简化的方式代替:
|
||||
@ -487,16 +500,19 @@ farmAnimals.isDisjoint(with: cityAnimals)
|
||||
|
||||
*字典*是一种存储多个相同类型的值的容器。每个值(value)都关联唯一的键(key),键作为字典中的这个值数据的标识符。和数组中的数据项不同,字典中的数据项并没有具体顺序。我们在需要通过标识符(键)访问数据的时候使用字典,这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> Swift 的`Dictionary`类型被桥接到`Foundation`的`NSDictionary`类。
|
||||
> 更多关于在`Foundation`和`Cocoa`中使用`Dictionary`类型的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 3.0.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
|
||||
>
|
||||
> 更多关于在`Foundation`和`Cocoa`中使用`Dictionary`类型的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
|
||||
|
||||
<a name="dictionary_type_shorthand_syntax"></a>
|
||||
### 字典类型简化语法
|
||||
|
||||
Swift 的字典使用`Dictionary<Key, Value>`定义,其中`Key`是字典中键的数据类型,`Value`是字典中对应于这些键所存储值的数据类型。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 一个字典的`Key`类型必须遵循`Hashable`协议,就像`Set`的值类型。
|
||||
|
||||
我们也可以用`[Key: Value]`这样简化的形式去创建一个字典类型。虽然这两种形式功能上相同,但是后者是首选,并且这本指导书涉及到字典类型时通篇采用后者。
|
||||
@ -541,7 +557,8 @@ var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
|
||||
|
||||
`airports`字典被声明为一种`[String: String]`类型,这意味着这个字典的键和值都是`String`类型。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> `airports`字典被声明为变量(用`var`关键字)而不是常量(`let`关键字)因为后来更多的机场信息会被添加到这个示例字典中。
|
||||
|
||||
`airports`字典使用字典字面量初始化,包含两个键值对。第一对的键是`YYZ`,值是`Toronto Pearson`。第二对的键是`DUB`,值是`Dublin`。
|
||||
|
||||
@ -26,6 +26,9 @@
|
||||
> 4.0
|
||||
> 翻译:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [For-In 循环](#for_in_loops)
|
||||
@ -204,7 +207,8 @@ print("Game over!")
|
||||
|
||||
掷完骰子后,玩家向前移动`diceRoll`个方格,如果玩家移动超过了第 25 个方格,这个时候游戏将会结束,为了应对这种情况,代码会首先判断`square`的值是否小于`board`的`count`属性,只有小于才会在`board[square]`上增加`square`,来向前或向后移动(遇到了梯子或者蛇)。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 如果没有这个检测(`square < board.count`),`board[square]`可能会越界访问`board`数组,导致错误。
|
||||
|
||||
当本轮`while`循环运行完毕,会再检测循环条件是否需要再运行一次循环。如果玩家移动到或者超过第 25 个方格,循环条件结果为`false`,此时游戏结束。
|
||||
@ -216,7 +220,8 @@ print("Game over!")
|
||||
|
||||
`while`循环的另外一种形式是`repeat-while`,它和`while`的区别是在判断循环条件之前,先执行一次循环的代码块。然后重复循环直到条件为`false`。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> Swift语言的`repeat-while`循环和其他语言中的`do-while`循环是类似的。
|
||||
|
||||
下面是 `repeat-while`循环的一般格式:
|
||||
@ -372,7 +377,8 @@ default:
|
||||
|
||||
与 C 和 Objective-C 中的`switch`语句不同,在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止`switch`语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用`break`语句。这使得`switch`语句更安全、更易用,也避免了因忘记写`break`语句而产生的错误。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
虽然在Swift中`break`不是必须的,但你依然可以在 case 分支中的代码执行完毕前使用`break`跳出,详情请参见[Switch 语句中的 break](#break_in_a_switch_statement)。
|
||||
|
||||
每一个 case 分支都*必须*包含至少一条语句。像下面这样书写代码是无效的,因为第一个 case 分支是空的:
|
||||
@ -404,8 +410,9 @@ default:
|
||||
```
|
||||
为了可读性,符合匹配可以写成多行形式,详情请参考[复合匹配](#compound_cases)
|
||||
|
||||
> 注意:
|
||||
如果想要显式贯穿case分支,请使用`fallthrough`语句,详情请参考[贯穿](#fallthrough)。
|
||||
> 注意
|
||||
>
|
||||
> 如果想要显式贯穿case分支,请使用`fallthrough`语句,详情请参考[贯穿](#fallthrough)。
|
||||
|
||||
<a name="interval_matching"></a>
|
||||
#### 区间匹配
|
||||
@ -525,7 +532,7 @@ case let (x, y):
|
||||
就像是值绑定中的例子,由于最后一个 case 分支匹配了余下所有可能的值,`switch`语句就已经完备了,因此不需要再书写默认分支。
|
||||
|
||||
<a name="compound_cases"></a>
|
||||
#### 复合匹配
|
||||
#### 复合型 Cases
|
||||
|
||||
当多个条件可以使用同一种方法来处理时,可以将这几种可能放在同一个`case`后面,并且用逗号隔开。当case后面的任意一种模式匹配的时候,这条分支就会被匹配。并且,如果匹配列表过长,还可以分行书写:
|
||||
|
||||
@ -615,8 +622,9 @@ print(puzzleOutput)
|
||||
|
||||
这种特性可以被用来匹配或者忽略一个或多个分支。因为 Swift 的`switch`需要包含所有的分支而且不允许有为空的分支,有时为了使你的意图更明显,需要特意匹配或者忽略某个分支。那么当你想忽略某个分支时,可以在该分支内写上`break`语句。当那个分支被匹配到时,分支内的`break`语句立即结束`switch`代码块。
|
||||
|
||||
>注意:
|
||||
当一个`switch`分支仅仅包含注释时,会被报编译时错误。注释不是代码语句而且也不能让`switch`分支达到被忽略的效果。你应该使用`break`来忽略某个分支。
|
||||
>注意
|
||||
>
|
||||
>当一个`switch`分支仅仅包含注释时,会被报编译时错误。注释不是代码语句而且也不能让`switch`分支达到被忽略的效果。你应该使用`break`来忽略某个分支。
|
||||
|
||||
下面的例子通过`switch`来判断一个`Character`值是否代表下面四种语言之一。为了简洁,多个值被包含在了同一个分支情况中。
|
||||
|
||||
@ -650,7 +658,7 @@ if let integerValue = possibleIntegerValue {
|
||||
在上面的例子中,想要把`Character`所有的的可能性都枚举出来是不现实的,所以使用`default`分支来包含所有上面没有匹配到字符的情况。由于这个`default`分支不需要执行任何动作,所以它只写了一条`break`语句。一旦落入到`default`分支中后,`break`语句就完成了该分支的所有代码操作,代码继续向下,开始执行`if let`语句。
|
||||
|
||||
<a name="fallthrough"></a>
|
||||
### 贯穿
|
||||
### 贯穿 (Fallthrough)
|
||||
|
||||
在 Swift 里,`switch`语句不会从上一个 case 分支跳转到下一个 case 分支中。相反,只要第一个匹配到的 case 分支完成了它需要执行的语句,整个`switch`代码块完成了它的执行。相比之下,C 语言要求你显式地插入`break`语句到每个 case 分支的末尾来阻止自动落入到下一个 case 分支中。Swift 的这种避免默认落入到下一个分支中的特性意味着它的`switch` 功能要比 C 语言的更加清晰和可预测,可以避免无意识地执行多个 case 分支从而引发的错误。
|
||||
|
||||
@ -676,7 +684,8 @@ print(description)
|
||||
|
||||
当`switch`代码块执行完后,使用`print(_:separator:terminator:)`函数打印该数字的描述。在这个例子中,数字`5`被准确的识别为了一个质数。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> `fallthrough`关键字不会检查它下一个将会落入执行的 case 中的匹配条件。`fallthrough`简单地使代码继续连接到下一个 case 中的代码,这和 C 语言标准中的`switch`语句特性是一样的。
|
||||
|
||||
<a name="labeled_statements"></a>
|
||||
@ -688,9 +697,11 @@ print(description)
|
||||
|
||||
声明一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,作为这个语句的前导关键字(introducor keyword),并且该标签后面跟随一个冒号。下面是一个针对`while`循环体的标签语法,同样的规则适用于所有的循环体和条件语句。
|
||||
|
||||
> `label name`: while `condition` {
|
||||
> `statements`
|
||||
> }
|
||||
```swift
|
||||
label name: while condition {
|
||||
statements
|
||||
}
|
||||
```
|
||||
|
||||
下面的例子是前面章节中*蛇和梯子*的适配版本,在此版本中,我们将使用一个带有标签的`while`循环体中调用`break`和`continue`语句。这次,游戏增加了一条额外的规则:
|
||||
|
||||
@ -743,9 +754,11 @@ print("Game over!")
|
||||
- 如果骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子。`continue gameLoop`语句结束本次`while`循环,开始下一次循环。
|
||||
- 在剩余的所有情况中,骰子数产生的都是合法的移动。玩家向前移动 `diceRoll` 个方格,然后游戏逻辑再处理玩家当前是否处于蛇头或者梯子的底部。接着本次循环结束,控制跳转到`while`循环体的条件判断语句处,再决定是否需要继续执行下次循环。
|
||||
|
||||
>注意:
|
||||
如果上述的`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`语句而不是`while`循环。使用`gameLoop`标签清晰的表明了`break`想要中断的是哪个代码块。
|
||||
同时请注意,当调用`continue gameLoop`去跳转到下一次循环迭代时,这里使用`gameLoop`标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以`continue`语句会影响到哪个循环体是没有歧义的。然而,`continue`语句使用`gameLoop`标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的`break gameLoop`,能够使游戏的逻辑更加清晰和易于理解。
|
||||
>注意
|
||||
>
|
||||
>如果上述的`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`语句而不是`while`循环。使用`gameLoop`标签清晰的表明了`break`想要中断的是哪个代码块。
|
||||
>
|
||||
>同时请注意,当调用`continue gameLoop`去跳转到下一次循环迭代时,这里使用`gameLoop`标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以`continue`语句会影响到哪个循环体是没有歧义的。然而,`continue`语句使用`gameLoop`标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的`break gameLoop`,能够使游戏的逻辑更加清晰和易于理解。
|
||||
|
||||
<a name="early_exit"></a>
|
||||
## 提前退出
|
||||
@ -797,7 +810,7 @@ if #available(iOS 10, macOS 10.12, *) {
|
||||
|
||||
以上可用性条件指定,`if`语句的代码块仅仅在 iOS 10 或 macOS 10.12 及更高版本才运行。最后一个参数,`*`,是必须的,用于指定在所有其它平台中,如果版本号高于你的设备指定的最低版本,if语句的代码块将会运行。
|
||||
|
||||
在它一般的形式中,可用性条件使用了一个平台名字和版本的列表。平台名字可以是`iOS`,`macOS`,`watchOS`和`tvOS`——请访问[声明属性](../chapter3/06_Attributes.html)来获取完整列表。除了指定像 iOS 8 或 macOS 10.10 的大版本号,也可以指定像 iOS 8.3 以及 macOS 10.10.3 的小版本号。
|
||||
在它一般的形式中,可用性条件使用了一个平台名字和版本的列表。平台名字可以是`iOS`,`macOS`,`watchOS`和`tvOS`——请访问[声明属性](../chapter3/06_Attributes.html)来获取完整列表。除了指定像 iOS 8 或 macOS 10.10 的大版本号,也可以指定像 iOS 11.2.6 以及 macOS 10.13.3 的小版本号。
|
||||
|
||||
```swift
|
||||
if #available(platform name version, ..., *) {
|
||||
|
||||
@ -23,6 +23,9 @@
|
||||
> 4.0
|
||||
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
- [函数定义与调用](#Defining_and_Calling_Functions)
|
||||
- [函数参数与返回值](#Function_Parameters_and_Return_Values)
|
||||
@ -66,7 +69,8 @@ print(greet(person: "Brian"))
|
||||
调用 `greet(person:)` 函数时,在圆括号中传给它一个 `String` 类型的实参,例如 `greet(person: "Anna")`。正如上面所示,因为这个函数返回一个 `String` 类型的值,所以`greet ` 可以被包含在 `print(_:separator:terminator:)` 的调用中,用来输出这个函数的返回值。
|
||||
|
||||
>注意
|
||||
`print(_:separator:terminator:)` 函数的第一个参数并没有设置一个标签,而其他的参数因为已经有了默认值,因此是可选的。关于这些函数语法上的变化详见下方关于 函数参数标签和参数名 以及 默认参数值。
|
||||
>
|
||||
>`print(_:separator:terminator:)` 函数的第一个参数并没有设置一个标签,而其他的参数因为已经有了默认值,因此是可选的。关于这些函数语法上的变化详见下方关于 函数参数标签和参数名 以及 默认参数值。
|
||||
|
||||
在 `greet(person:)` 的函数体中,先定义了一个新的名为 `greeting` 的 `String` 常量,同时,把对 `personName` 的问候消息赋值给了 `greeting` 。然后用 `return` 关键字把这个问候返回出去。一旦 `return greeting` 被调用,该函数结束它的执行并返回 `greeting` 的当前值。
|
||||
|
||||
@ -139,7 +143,8 @@ greet(person: "Dave")
|
||||
因为这个函数不需要返回值,所以这个函数的定义中没有返回箭头(->)和返回类型。
|
||||
|
||||
>注意
|
||||
严格上来说,虽然没有返回值被定义,`greet(person:)` 函数依然返回了值。没有定义返回类型的函数会返回一个特殊的`Void`值。它其实是一个空的元组(tuple),没有任何元素,可以写成()。
|
||||
>
|
||||
>严格上来说,虽然没有返回值被定义,`greet(person:)` 函数依然返回了值。没有定义返回类型的函数会返回一个特殊的`Void`值。它其实是一个空的元组,没有任何元素,可以写成()。
|
||||
|
||||
被调用时,一个函数的返回值可以被忽略:
|
||||
|
||||
@ -160,7 +165,7 @@ printWithoutCounting(string: "hello, world")
|
||||
第一个函数 `printAndCount(string:)`,输出一个字符串并返回 `Int` 类型的字符数。第二个函数 `printWithoutCounting(string:)`调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。
|
||||
|
||||
>注意:
|
||||
返回值可以被忽略,但定义了有返回值的函数必须返回一个值,如果在函数定义底部没有返回任何值,将导致编译时错误(compile-time error)。
|
||||
返回值可以被忽略,但定义了有返回值的函数必须返回一个值,如果在函数定义底部没有返回任何值,将导致编译时错误。
|
||||
|
||||
<a name="functions_with_multiple_return_values"></a>
|
||||
### 多重返回值函数
|
||||
@ -201,12 +206,13 @@ print("min is \(bounds.min) and max is \(bounds.max)")
|
||||
<a name="optional_tuple_return_types"></a>
|
||||
### 可选元组返回类型
|
||||
|
||||
如果函数返回的元组类型有可能整个元组都“没有值”,你可以使用可选的( `optional` ) 元组返回类型反映整个元组可以是`nil`的事实。你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如 `(Int, Int)?` 或 `(String, Int, Bool)?`
|
||||
如果函数返回的元组类型有可能整个元组都“没有值”,你可以使用*可选的* 元组返回类型反映整个元组可以是`nil`的事实。你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如 `(Int, Int)?` 或 `(String, Int, Bool)?`
|
||||
|
||||
>注意
|
||||
可选元组类型如 `(Int, Int)?` 与元组包含可选类型如 `(Int?, Int?)` 是不同的。可选的元组类型,整个元组是可选的,而不只是元组中的每个元素值。
|
||||
>
|
||||
>可选元组类型如 `(Int, Int)?` 与元组包含可选类型如 `(Int?, Int?)` 是不同的。可选的元组类型,整个元组是可选的,而不只是元组中的每个元素值。
|
||||
|
||||
前面的 `minMax(array:)` 函数返回了一个包含两个 `Int` 值的元组。但是函数不会对传入的数组执行任何安全检查,如果 `array` 参数是一个空数组,如上定义的 `minMax(array:)` 在试图访问 `array[0]` 时会触发一个运行时错误(runtime error)。
|
||||
前面的 `minMax(array:)` 函数返回了一个包含两个 `Int` 值的元组。但是函数不会对传入的数组执行任何安全检查,如果 `array` 参数是一个空数组,如上定义的 `minMax(array:)` 在试图访问 `array[0]` 时会触发一个运行时错误。
|
||||
|
||||
为了安全地处理这个“空数组”问题,将 `minMax(array:)` 函数改写为使用可选元组返回类型,并且当数组为空时返回 `nil`:
|
||||
|
||||
@ -324,20 +330,22 @@ arithmeticMean(3, 8.25, 18.75)
|
||||
// 返回 10.0, 是这 3 个数的平均数。
|
||||
```
|
||||
|
||||
>注意:
|
||||
一个函数最多只能拥有一个可变参数。
|
||||
>注意
|
||||
>
|
||||
>一个函数最多只能拥有一个可变参数。
|
||||
|
||||
<a name="in_out_parameters"></a>
|
||||
### 输入输出参数
|
||||
|
||||
函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误(compile-time error)。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为*输入输出参数(In-Out Parameters)*。
|
||||
函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为*输入输出参数(In-Out Parameters)*。
|
||||
|
||||
定义一个输入输出参数时,在参数定义前加 `inout` 关键字。一个`输入输出参数`有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看[输入输出参数](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID545)一节。
|
||||
|
||||
你只能传递变量给输入输出参数。你不能传入常量或者字面量,因为这些量是不能被修改的。当传入的参数作为输入输出参数时,需要在参数名前加 `&` 符,表示这个值可以被函数修改。
|
||||
|
||||
>注意
|
||||
输入输出参数不能有默认值,而且可变参数不能用 `inout` 标记。
|
||||
>
|
||||
>输入输出参数不能有默认值,而且可变参数不能用 `inout` 标记。
|
||||
|
||||
下例中,`swapTwoInts(_:_:)` 函数有两个分别叫做 `a` 和 `b` 的输入输出参数:
|
||||
|
||||
@ -363,8 +371,9 @@ print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
|
||||
|
||||
从上面这个例子中,我们可以看到 `someInt` 和 `anotherInt` 的原始值在 `swapTwoInts(_:_:)` 函数中被修改,尽管它们的定义在函数体外。
|
||||
|
||||
>注意:
|
||||
输入输出参数和返回值是不一样的。上面的 `swapTwoInts` 函数并没有定义任何返回值,但仍然修改了 `someInt` 和 `anotherInt` 的值。输入输出参数是函数对函数体外产生影响的另一种方式。
|
||||
>注意
|
||||
>
|
||||
>输入输出参数和返回值是不一样的。上面的 `swapTwoInts` 函数并没有定义任何返回值,但仍然修改了 `someInt` 和 `anotherInt` 的值。输入输出参数是函数对函数体外产生影响的另一种方式。
|
||||
|
||||
<a name="Function_Types"></a>
|
||||
## 函数类型
|
||||
@ -384,7 +393,9 @@ func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
|
||||
|
||||
这个例子中定义了两个简单的数学函数:`addTwoInts` 和 `multiplyTwoInts`。这两个函数都接受两个 `Int` 值, 返回一个 `Int` 值。
|
||||
|
||||
这两个函数的类型是 `(Int, Int) -> Int`,可以解读为“这个函数类型有两个 `Int` 型的参数并返回一个 `Int` 型的值。”。
|
||||
这两个函数的类型是 `(Int, Int) -> Int`,可以解读为:
|
||||
|
||||
“这个函数类型有两个 `Int` 型的参数并返回一个 `Int` 型的值”。
|
||||
|
||||
下面是另一个例子,一个没有参数,也没有返回值的函数:
|
||||
|
||||
|
||||
@ -22,6 +22,9 @@
|
||||
> 4.0
|
||||
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [闭包表达式](#closure_expressions)
|
||||
@ -38,7 +41,7 @@
|
||||
> 注意
|
||||
> 如果你不熟悉捕获(capturing)这个概念也不用担心,你可以在[值捕获](#capturing_values)章节对其进行详细了解。
|
||||
|
||||
在[函数](./06_Functions.html)章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
|
||||
在[函数](./06_Functions.md)章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
|
||||
|
||||
* 全局函数是一个有名字但不会捕获任何值的闭包
|
||||
* 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
|
||||
@ -54,12 +57,12 @@ Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进
|
||||
<a name="closure_expressions"></a>
|
||||
## 闭包表达式
|
||||
|
||||
[嵌套函数](./06_Functions.html#nested_function)是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候编写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在你处理一些函数并需要将另外一些函数作为该函数的参数时。
|
||||
[嵌套函数](./06_Functions.md#Nested_Functions)是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候编写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在你处理一些函数并需要将另外一些函数作为该函数的参数时。
|
||||
|
||||
*闭包表达式*是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。下面闭包表达式的例子通过使用几次迭代展示了 `sorted(by:)` 方法定义和语法优化的方式。每一次迭代都用更简洁的方式描述了相同的功能。
|
||||
|
||||
<a name="the_sorted_function"></a>
|
||||
### sorted 方法
|
||||
### 排序方法
|
||||
|
||||
Swift 标准库提供了名为 `sorted(by:)` 的方法,它会根据你所提供的用于排序的闭包函数将已知类型数组中的值进行排序。一旦排序完成,`sorted(by:)` 方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被 `sorted(by:)` 方法修改。
|
||||
|
||||
@ -93,12 +96,12 @@ var reversedNames = names.sorted(by: backward)
|
||||
闭包表达式语法有如下的一般形式:
|
||||
|
||||
```swift
|
||||
{ (parameters) -> returnType in
|
||||
{ (parameters) -> return type in
|
||||
statements
|
||||
}
|
||||
```
|
||||
|
||||
*闭包表达式参数* 可以是 in-out 参数,但不能设定默认值。也可以使用具名的可变参数(译者注:但是如果可变参数不放在参数列表的最后一位的话,调用闭包的时时编译器将报错。可参考[这里](http://stackoverflow.com/questions/39548852/swift-3-0-closure-expression-what-if-the-variadic-parameters-not-at-the-last-pl))。元组也可以作为参数和返回值。
|
||||
*闭包表达式参数* 可以是 in-out 参数,但不能设定默认值。如果你命名了可变参数,也可以使用此可变参数。元组也可以作为参数和返回值。
|
||||
|
||||
下面的例子展示了之前 `backward(_:_:)` 函数对应的闭包表达式版本的代码:
|
||||
|
||||
@ -165,7 +168,7 @@ reversedNames = names.sorted(by: { $0 > $1 } )
|
||||
reversedNames = names.sorted(by: >)
|
||||
```
|
||||
|
||||
更多关于运算符方法的内容请查看[运算符方法](./25_Advanced_Operators.html#operator_methods)。
|
||||
更多关于运算符方法的内容请查看[运算符方法](./26_Advanced_Operators.html#operator_methods)。
|
||||
|
||||
<a name="trailing_closures"></a>
|
||||
## 尾随闭包
|
||||
@ -239,7 +242,8 @@ let strings = numbers.map {
|
||||
|
||||
闭包表达式在每次被调用的时候创建了一个叫做 `output` 的字符串并返回。其使用求余运算符(`number % 10`)计算最后一位数字并利用 `digitNames` 字典获取所映射的字符串。这个闭包能够用于创建任意正整数的字符串表示。
|
||||
|
||||
> 注意:
|
||||
> 注意
|
||||
>
|
||||
> 字典 `digitNames` 下标后跟着一个叹号(`!`),因为字典下标返回一个可选值(optional value),表明该键不存在时会查找失败。在上例中,由于可以确定 `number % 10` 总是 `digitNames` 字典的有效下标,因此叹号可以用于强制解包 (force-unwrap) 存储在下标的可选类型的返回值中的`String`类型的值。
|
||||
|
||||
从 `digitNames` 字典中获取的字符串被添加到 `output` 的*前部*,逆序建立了一个字符串版本的数字。(在表达式 `number % 10` 中,如果 `number` 为 `16`,则返回 `6`,`58` 返回 `8`,`510` 返回 `0`。)
|
||||
@ -288,7 +292,9 @@ func incrementer() -> Int {
|
||||
`incrementer()` 函数并没有任何参数,但是在函数体内访问了 `runningTotal` 和 `amount` 变量。这是因为它从外围函数捕获了 `runningTotal` 和 `amount` 变量的*引用*。捕获引用保证了 `runningTotal` 和 `amount` 变量在调用完 `makeIncrementer` 后不会消失,并且保证了在下一次执行 `incrementer` 函数时,`runningTotal` 依旧存在。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 为了优化,如果一个值不会被闭包改变,或者在闭包创建后不会改变,Swift 可能会改为捕获并保存一份对值的拷贝。
|
||||
>
|
||||
> Swift 也会负责被捕获变量的所有内存管理工作,包括释放不再需要的变量。
|
||||
|
||||
下面是一个使用 `makeIncrementer` 的例子:
|
||||
@ -323,8 +329,9 @@ incrementByTen()
|
||||
// 返回的值为40
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 如果你将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,你将在闭包和该实例间创建一个循环强引用。Swift 使用捕获列表来打破这种循环强引用。更多信息,请参考[闭包引起的循环强引用](./16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)。
|
||||
> 注意
|
||||
>
|
||||
> 如果你将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,你将在闭包和该实例间创建一个循环强引用。Swift 使用捕获列表来打破这种循环强引用。更多信息,请参考[闭包引起的循环强引用](./23_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)。
|
||||
|
||||
<a name="closures_are_reference_types"></a>
|
||||
## 闭包是引用类型
|
||||
@ -431,6 +438,7 @@ serve(customer: customersInLine.remove(at: 0))
|
||||
```
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 过度使用 `autoclosures` 会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。
|
||||
|
||||
如果你想让一个自动闭包可以“逃逸”,则应该同时使用 `@autoclosure` 和 `@escaping` 属性。`@escaping` 属性的讲解见上面的[逃逸闭包](#escaping_closures)。
|
||||
|
||||
@ -22,6 +22,9 @@
|
||||
> 4.0
|
||||
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页内容包含:
|
||||
|
||||
- [枚举语法](#enumeration_syntax)
|
||||
@ -38,7 +41,7 @@
|
||||
|
||||
在 Swift 中,枚举类型是一等(first-class)类型。它们采用了很多在传统上只被类(class)所支持的特性,例如计算属性(computed properties),用于提供枚举值的附加信息,实例方法(instance methods),用于提供和枚举值相关联的功能。枚举也可以定义构造函数(initializers)来提供一个初始值;可以在原始实现的基础上扩展它们的功能;还可以遵循协议(protocols)来提供标准的功能。
|
||||
|
||||
想了解更多相关信息,请参见[属性](./10_Properties.html),[方法](./11_Methods.html),[构造过程](./14_Initialization.html),[扩展](./21_Extensions.html)和[协议](./22_Protocols.html)。
|
||||
想了解更多相关信息,请参见[属性](./10_Properties.html),[方法](./11_Methods.html),[构造过程](./14_Initialization.html),[扩展](./20_Extensions.html)和[协议](./21_Protocols.html)。
|
||||
|
||||
<a name="enumeration_syntax"></a>
|
||||
## 枚举语法
|
||||
@ -65,6 +68,7 @@ enum CompassPoint {
|
||||
枚举中定义的值(如 `north `,`south`,`east`和`west`)是这个枚举的*成员值*(或*成员*)。你可以使用`case`关键字来定义一个新的枚举成员值。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 与 C 和 Objective-C 不同,Swift 的枚举成员在被创建时不会被赋予一个默认的整型值。在上面的`CompassPoint`例子中,`north`,`south`,`east`和`west`不会被隐式地赋值为`0`,`1`,`2`和`3`。相反,这些枚举成员本身就是完备的值,这些值的类型是已经明确定义好的`CompassPoint`类型。
|
||||
|
||||
多个成员值可以出现在同一行上,用逗号隔开:
|
||||
@ -75,7 +79,7 @@ enum Planet {
|
||||
}
|
||||
```
|
||||
|
||||
每个枚举定义了一个全新的类型。像 Swift 中其他类型一样,它们的名字(例如`CompassPoint`和`Planet`)应该以一个大写字母开头。给枚举类型起一个单数名字而不是复数名字,以便于读起来更加容易理解:
|
||||
每个枚举定义了一个全新的类型。像 Swift 中其他类型一样,它们的名字(例如`CompassPoint`和`Planet`)应该以一个大写字母开头。给枚举类型起一个单数名字而不是复数名字,以便于:
|
||||
|
||||
```swift
|
||||
var directionToHead = CompassPoint.west
|
||||
@ -222,6 +226,7 @@ enum ASCIIControlCharacter: Character {
|
||||
原始值可以是字符串,字符,或者任意整型值或浮点型值。每个原始值在枚举声明中必须是唯一的。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 原始值和关联值是不同的。原始值是在定义枚举时被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终不变。关联值是创建一个基于枚举成员的常量或变量时才设置的值,枚举成员的关联值可以变化。
|
||||
|
||||
<a name="implicitly_assigned_raw_values"></a>
|
||||
@ -278,6 +283,7 @@ let possiblePlanet = Planet(rawValue: 7)
|
||||
然而,并非所有`Int`值都可以找到一个匹配的行星。因此,原始值构造器总是返回一个*可选*的枚举成员。在上面的例子中,`possiblePlanet`是`Planet?`类型,或者说“可选的`Planet`”。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见[可失败构造器](../chapter3/05_Declarations.html#failable_initializers)
|
||||
|
||||
如果你试图寻找一个位置为`11`的行星,通过原始值构造器返回的可选`Planet`值将是`nil`:
|
||||
|
||||
@ -18,6 +18,9 @@
|
||||
> 4.0
|
||||
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [类和结构体对比](#comparing_classes_and_structures)
|
||||
@ -31,6 +34,7 @@
|
||||
与其他编程语言所不同的是,Swift 并不要求你为自定义类和结构去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其它代码的外部接口。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 通常一个*类*的实例被称为*对象*。然而在 Swift 中,类和结构体的关系要比在其他语言中更加的密切,本章中所讨论的大部分功能都可以用在类和结构体上。因此,我们会主要使用*实例*。
|
||||
|
||||
<a name="comparing_classes_and_structures"></a>
|
||||
@ -40,12 +44,12 @@ Swift 中类和结构体有很多共同点。共同处在于:
|
||||
|
||||
* 定义属性用于存储值
|
||||
* 定义方法用于提供功能
|
||||
* 定义下标操作使得可以通过下标语法来访问实例所包含的值
|
||||
* 定义下标操作通过下标语法可以访问它们的值
|
||||
* 定义构造器用于生成初始化值
|
||||
* 通过扩展以增加默认实现的功能
|
||||
* 实现协议以提供某种标准功能
|
||||
* 遵循协议以提供某种标准功能
|
||||
|
||||
更多信息请参见[属性](./10_Properties.html),[方法](./11_Methods.html),[下标](./12_Subscripts.html),[构造过程](./14_Initialization.html),[扩展](./21_Extensions.html),和[协议](./22_Protocols.html)。
|
||||
更多信息请参见[属性](./10_Properties.html),[方法](./11_Methods.html),[下标](./12_Subscripts.html),[构造过程](./14_Initialization.html),[扩展](./20_Extensions.html),和[协议](./21_Protocols.html)。
|
||||
|
||||
与结构体相比,类还有如下的附加功能:
|
||||
|
||||
@ -54,9 +58,10 @@ Swift 中类和结构体有很多共同点。共同处在于:
|
||||
* 析构器允许一个类实例释放任何其所被分配的资源
|
||||
* 引用计数允许对一个类的多次引用
|
||||
|
||||
更多信息请参见[继承](./13_Inheritance.html),[类型转换](./19_Type_Casting.html),[析构过程](./15_Deinitialization.html),和[自动引用计数](./16_Automatic_Reference_Counting.html)。
|
||||
更多信息请参见[继承](./13_Inheritance.html),[类型转换](./18_Type_Casting.html),[析构过程](./15_Deinitialization.html),和[自动引用计数](./23_Automatic_Reference_Counting.html)。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 结构体总是通过被复制的方式在代码中传递,不使用引用计数。
|
||||
|
||||
<a name="definition_syntax"></a>
|
||||
@ -74,6 +79,7 @@ struct SomeStructure {
|
||||
```
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 在你每次定义一个新类或者结构体的时候,实际上你是定义了一个新的 Swift 类型。因此请使用`UpperCamelCase`这种方式来命名(如`SomeClass`和`SomeStructure`等),以便符合标准 Swift 类型的大写命名风格(如`String`,`Int`和`Bool`)。相反的,请使用`lowerCamelCase`这种方式为属性和方法命名(如`framerate`和`incrementCount`),以便和类型名区分。
|
||||
|
||||
以下是定义结构体和定义类的示例:
|
||||
@ -137,6 +143,7 @@ print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
|
||||
```
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 与 Objective-C 语言不同的是,Swift 允许直接设置结构体属性的子属性。上面的最后一个例子,就是直接设置了`someVideoMode`中`resolution`属性的`width`这个子属性,以上操作并不需要重新为整个`resolution`属性设置新值。
|
||||
|
||||
<a name="memberwise_initializers_for_structure_types"></a>
|
||||
@ -155,7 +162,7 @@ let vga = Resolution(width: 640, height: 480)
|
||||
|
||||
*值类型*被赋予给一个变量、常量或者被传递给一个函数的时候,其值会被*拷贝*。
|
||||
|
||||
在之前的章节中,我们已经大量使用了值类型。实际上,在 Swift 中,所有的基本类型:整数(Integer)、浮点数(floating-point)、布尔值(Boolean)、字符串(string)、数组(array)和字典(dictionary),都是值类型,并且在底层都是以结构体的形式所实现。
|
||||
在之前的章节中,我们已经大量使用了值类型。实际上,在 Swift 中,所有的基本类型:整数(Integers)、浮点数(floating-point numbers)、布尔值(Booleans)、字符串(strings)、数组(arrays)和字典(dictionaries),都是值类型,并且在底层都是以结构体的形式所实现。
|
||||
|
||||
在 Swift 中,所有的结构体和枚举类型都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制。
|
||||
|
||||
@ -268,7 +275,7 @@ if tenEighty === alsoTenEighty {
|
||||
* “等价于”表示两个类类型(class type)的常量或者变量引用同一个类实例。
|
||||
* “等于”表示两个实例的值“相等”或“相同”,判定时要遵照设计者定义的评判标准,因此相对于“相等”来说,这是一种更加合适的叫法。
|
||||
|
||||
当你在定义你的自定义类和结构体的时候,你有义务来决定判定两个实例“相等”的标准。在章节[等价操作符](./25_Advanced_Operators.html#equivalence_operators)中将会详细介绍实现自定义“等于”和“不等于”运算符的流程。
|
||||
当你在定义你的自定义类和结构体的时候,你有义务来决定判定两个实例“相等”的标准。在章节[等价操作符](./26_Advanced_Operators.html#equivalence_operators)中将会详细介绍实现自定义“等于”和“不等于”运算符的流程。
|
||||
|
||||
<a name="pointers"></a>
|
||||
### 指针
|
||||
@ -305,4 +312,5 @@ Swift 中,许多基本类型,诸如`String`,`Array`和`Dictionary`类型
|
||||
Objective-C 中`NSString`,`NSArray`和`NSDictionary`类型均以类的形式实现,而并非结构体。它们在被赋值或者被传入函数或方法时,不会发生值拷贝,而是传递现有实例的引用。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 以上是对字符串、数组、字典的“拷贝”行为的描述。在你的代码中,拷贝行为看起来似乎总会发生。然而,Swift 在幕后只在绝对必要时才执行实际的拷贝。Swift 管理所有的值拷贝以确保性能最优化,所以你没必要去回避赋值来保证性能最优化。
|
||||
|
||||
@ -20,6 +20,9 @@
|
||||
> 4.0
|
||||
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [存储属性](#stored_properties)
|
||||
@ -80,6 +83,7 @@ rangeOfFourItems.firstValue = 6
|
||||
*延迟存储属性*是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用 `lazy` 来标示一个延迟存储属性。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 必须将延迟存储属性声明成变量(使用 `var` 关键字),因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
|
||||
|
||||
延迟属性很有用,当属性的值依赖于在实例的构造过程结束后才会知道影响值的外部因素时,或者当获得属性的初始值需要复杂或大量计算时,可以只在需要的时候计算它。
|
||||
@ -123,6 +127,7 @@ print(manager.importer.fileName)
|
||||
```
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 如果一个被标记为 `lazy` 的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。
|
||||
|
||||
<a name="stored_properties_and_instance_variables"></a>
|
||||
@ -130,7 +135,7 @@ print(manager.importer.fileName)
|
||||
|
||||
如果您有过 Objective-C 经验,应该知道 Objective-C 为类实例存储值和引用提供两种方法。除了属性之外,还可以使用实例变量作为属性值的后端存储。
|
||||
|
||||
Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义。
|
||||
Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。属性的全部信息——包括命名、类型和内存管理特征——作为类型定义的一部分,都定义在一个地方。
|
||||
|
||||
<a name="computed_properties"></a>
|
||||
## 计算属性
|
||||
@ -184,7 +189,7 @@ print("square.origin is now at (\(square.origin.x), \(square.origin.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" />
|
||||
|
||||
<a name="shorthand_setter_declaration"></a>
|
||||
### 简化 setter 声明
|
||||
### 简化 Setter 声明
|
||||
|
||||
如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称 `newValue`。下面是使用了简化 setter 声明的 `Rect` 结构体代码:
|
||||
|
||||
@ -212,6 +217,7 @@ struct AlternativeRect {
|
||||
只有 getter 没有 setter 的计算属性就是*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 必须使用 `var` 关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。`let` 关键字只用来声明常量属性,表示初始化后再也无法修改的值。
|
||||
|
||||
只读计算属性的声明可以去掉 `get` 关键字和花括号:
|
||||
@ -233,9 +239,9 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
|
||||
<a name="property_observers"></a>
|
||||
## 属性观察器
|
||||
|
||||
*属性观察器*监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外。
|
||||
属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外。
|
||||
|
||||
可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。你不必为非重写的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。 属性重写请参考[重写](./13_Inheritance.html#overriding)。
|
||||
你可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。你不必为非重写的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。 属性重写请参考[重写](./13_Inheritance.html#overriding)。
|
||||
|
||||
可以为属性添加如下的一个或全部观察器:
|
||||
|
||||
@ -247,7 +253,9 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
|
||||
同样,`didSet` 观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名 `oldValue`。如果在 `didSet` 方法中再次对该属性赋值,那么新值会覆盖旧的值。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 父类的属性在子类的构造器中被赋值时,它在父类中的 `willSet` 和 `didSet` 观察器会被调用,随后才会调用子类的观察器。在父类初始化方法调用之前,子类给属性赋值时,观察器不会被调用。
|
||||
>
|
||||
> 有关构造器代理的更多信息,请参考[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)和[类的构造器代理规则](./14_Initialization.html#initializer_delegation_for_class_types)。
|
||||
|
||||
下面是一个 `willSet` 和 `didSet` 实际运用的例子,其中定义了一个名为 `StepCounter` 的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。
|
||||
@ -294,12 +302,14 @@ stepCounter.totalSteps = 896
|
||||
|
||||
计算属性和属性观察器所描述的功能也可以用于*全局变量*和*局部变量*。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。
|
||||
|
||||
前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它为特定类型的值提供存储空间,并允许读取和写入。
|
||||
前面章节提到的全局或局部变量都属于*存储型变量*,跟存储属性类似,它为特定类型的值提供存储空间,并允许读取和写入。
|
||||
|
||||
另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器。计算型变量跟计算属性一样,返回一个计算结果而不是存储值,声明格式也完全一样。
|
||||
另外,在全局或局部范围都可以定义*计算型变量*和为存储型变量定义观察器。计算型变量跟计算属性一样,返回一个计算结果而不是存储值,声明格式也完全一样。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记`lazy`修饰符。
|
||||
>
|
||||
> 局部范围的常量或变量从不延迟计算。
|
||||
|
||||
<a name="type_properties"></a>
|
||||
@ -309,12 +319,14 @@ stepCounter.totalSteps = 896
|
||||
|
||||
也可以为类型本身定义属性,无论创建了多少个该类型的实例,这些属性都只有唯一一份。这种属性就是*类型属性*。
|
||||
|
||||
类型属性用于定义某个类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。
|
||||
类型属性用于定义某个类型所有实例共享的数据,比如*所有*实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。
|
||||
|
||||
存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算型属性一样只能定义成变量属性。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 跟实例的存储型属性不同,必须给存储型类型属性指定默认值,因为类型本身没有构造器,也就无法在初始化过程中使用构造器给类型属性赋值。
|
||||
>
|
||||
> 存储型类型属性是延迟初始化的,它们只有在第一次被访问的时候才会被初始化。即使它们被多个线程同时访问,系统也保证只会对其进行一次初始化,并且不需要对其使用 `lazy` 修饰符。
|
||||
|
||||
<a name="type_property_syntax"></a>
|
||||
@ -349,6 +361,7 @@ class SomeClass {
|
||||
```
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟计算型实例属性的语法相同。
|
||||
|
||||
<a name="querying_and_setting_type_properties"></a>
|
||||
@ -407,6 +420,7 @@ struct AudioChannel {
|
||||
- 如果修正后的 `currentLevel` 值大于静态类型属性 `maxInputLevelForAllChannels` 的值,属性观察器就将新值保存在 `maxInputLevelForAllChannels` 中。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 在第一个检查过程中,`didSet` 属性观察器将 `currentLevel` 设置成了不同的值,但这不会造成属性观察器被再次调用。
|
||||
|
||||
可以使用结构体 `AudioChannel` 创建两个声道 `leftChannel` 和 `rightChannel`,用以表示立体声系统的音量:
|
||||
|
||||
@ -18,6 +18,9 @@
|
||||
> 4.0
|
||||
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [实例方法](#instance_methods)
|
||||
@ -188,6 +191,7 @@ ovenLight.next()
|
||||
实例方法是被某个类型的实例调用的方法。你也可以定义在类型本身上调用的方法,这种方法就叫做*类型方法*。在方法的`func`关键字之前加上关键字`static`,来指定类型方法。类还可以用关键字`class`来允许子类重写父类的方法实现。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 在 Objective-C 中,你只能为 Objective-C 的类类型(classes)定义类型方法(type-level methods)。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法。每一个类型方法都被它所支持的类型显式包含。
|
||||
|
||||
类型方法和实例方法一样用点语法调用。但是,你是在类型上调用这个方法,而不是在实例上调用。下面是如何在`SomeClass`类上调用类型方法的例子:
|
||||
@ -240,7 +244,7 @@ struct LevelTracker {
|
||||
|
||||
除了类型属性和类型方法,`LevelTracker`还监测每个玩家的进度。它用实例属性`currentLevel`来监测每个玩家当前的等级。
|
||||
|
||||
为了便于管理`currentLevel`属性,`LevelTracker`定义了实例方法`advance(to:)`。这个方法会在更新`currentLevel`之前检查所请求的新等级是否已经解锁。`advance(to:)`方法返回布尔值以指示是否能够设置`currentLevel`。因为允许在调用`advance(to:)`时候忽略返回值,不会产生编译警告,所以函数被标注为`@ discardableResult`属性,更多关于属性信息,请参考[属性](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Attributes.html#//apple_ref/doc/uid/TP40014097-CH35-ID347)章节。
|
||||
为了便于管理`currentLevel`属性,`LevelTracker`定义了实例方法`advance(to:)`。这个方法会在更新`currentLevel`之前检查所请求的新等级是否已经解锁。`advance(to:)`方法返回布尔值以指示是否能够设置`currentLevel`。因为允许在调用`advance(to:)`时候忽略返回值,不会产生编译警告,所以函数被标注为`@ discardableResult`属性,更多关于属性信息,请参考[属性](../chapter3/07_Attributes.html)章节。
|
||||
|
||||
下面,`Player`类使用`LevelTracker`来监测和更新每个玩家的发展进度:
|
||||
|
||||
|
||||
@ -18,6 +18,9 @@
|
||||
> 4.0
|
||||
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [下标语法](#subscript_syntax)
|
||||
@ -74,6 +77,7 @@ print("six times three is \(threeTimesTable[6])")
|
||||
你可以通过下标访问`threeTimesTable`实例,例如上面演示的`threeTimesTable[6]`。这条语句查询了`3`的乘法表中的第六个元素,返回`3`的`6`倍即`18`。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> `TimesTable`例子基于一个固定的数学公式,对`threeTimesTable[someIndex]`进行赋值操作并不合适,因此下标定义为只读的。
|
||||
|
||||
<a name="subscript_usage"></a>
|
||||
@ -93,6 +97,7 @@ numberOfLegs["bird"] = 2
|
||||
更多关于`Dictionary`下标的信息请参考[读取和修改字典](./04_Collection_Types.html#accessing_and_modifying_a_dictionary)
|
||||
|
||||
> 注意
|
||||
>
|
||||
> Swift 的`Dictionary`类型的下标接受并返回可选类型的值。上例中的`numberOfLegs`字典通过下标返回的是一个`Int?`或者说“可选的int”。`Dictionary`类型之所以如此实现下标,是因为不是每个键都有个对应的值,同时这也提供了一种通过键删除对应值的方式,只需将键对应的值赋值为`nil`即可。
|
||||
|
||||
<a name="subscript_options"></a>
|
||||
@ -129,7 +134,7 @@ struct Matrix {
|
||||
}
|
||||
```
|
||||
|
||||
`Matrix`提供了一个接受两个入参的构造方法,入参分别是`rows`和`columns`,创建了一个足够容纳`rows * columns`个`Double`类型的值的数组。通过传入数组长度和初始值`0.0`到数组的构造器,将矩阵中每个位置的值初始化为`0.0`。关于数组的这种构造方法请参考[创建一个空数组](./04_Collection_Types.html#creating_an_empty_array)。
|
||||
`Matrix`提供了一个接受两个入参的构造方法,入参分别是`rows`和`columns`,创建了一个足够容纳`rows * columns`个`Double`类型的值的数组。通过传入数组长度和初始值`0.0`到数组的构造器,将矩阵中每个位置的值初始化为`0.0`。关于数组的这种构造方法请参考[创建一个带有默认值的数组](./04_Collection_Types.html#creating_an_array_with_a_default_value)。
|
||||
|
||||
你可以通过传入合适的`row`和`column`的数量来构造一个新的`Matrix`实例:
|
||||
|
||||
|
||||
@ -15,6 +15,9 @@
|
||||
> 4.0
|
||||
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [定义一个基类](#defining_a_base_class)
|
||||
@ -34,7 +37,8 @@
|
||||
不继承于其它类的类,称之为*基类*。
|
||||
|
||||
> 注意
|
||||
Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。
|
||||
>
|
||||
> Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。
|
||||
|
||||
下面的例子定义了一个叫`Vehicle`的基类。这个基类声明了一个名为`currentSpeed `,默认值是`0.0`的存储属性(属性类型推断为`Double`)。`currentSpeed`属性的值被一个`String`类型的只读计算型属性`description`使用,用来创建车辆的描述。
|
||||
|
||||
@ -180,7 +184,8 @@ train.makeNoise()
|
||||
你可以将一个继承来的只读属性重写为一个读写属性,只需要在重写版本的属性里提供 getter 和 setter 即可。但是,你不可以将一个继承来的读写属性重写为一个只读属性。
|
||||
|
||||
> 注意
|
||||
如果你在重写属性中提供了 setter,那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接通过`super.someProperty`来返回继承来的值,其中`someProperty`是你要重写的属性的名字。
|
||||
>
|
||||
> 如果你在重写属性中提供了 setter,那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接通过`super.someProperty`来返回继承来的值,其中`someProperty`是你要重写的属性的名字。
|
||||
|
||||
以下的例子定义了一个新类,叫`Car`,它是`Vehicle`的子类。这个类引入了一个新的存储型属性叫做`gear`,默认值为整数`1`。`Car`类重写了继承自`Vehicle`的`description`属性,提供包含当前档位的自定义描述:
|
||||
|
||||
@ -211,7 +216,8 @@ print("Car: \(car.description)")
|
||||
你可以通过重写属性为一个继承来的属性添加属性观察器。这样一来,当继承来的属性值发生改变时,你就会被通知到,无论那个属性原本是如何实现的。关于属性观察器的更多内容,请看[属性观察器](../chapter2/10_Properties.html#property_observers)。
|
||||
|
||||
> 注意
|
||||
你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供`willSet`或`didSet`实现是不恰当。
|
||||
>
|
||||
> 你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供`willSet`或`didSet`实现是不恰当。
|
||||
此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已经为那个属性提供了定制的 setter,那么你在 setter 中就可以观察到任何值变化了。
|
||||
|
||||
下面的例子定义了一个新类叫`AutomaticCar`,它是`Car`的子类。`AutomaticCar`表示自动挡汽车,它可以根据当前的速度自动选择合适的挡位:
|
||||
@ -226,7 +232,7 @@ class AutomaticCar: Car {
|
||||
}
|
||||
```
|
||||
|
||||
当你设置`AutomaticCar`的`currentSpeed`属性,属性的`didSet`观察器就会自动地设置`gear`属性,为新的速度选择一个合适的挡位。具体来说就是,属性观察器将新的速度值除以`10`,然后向下取得最接近的整数值,最后加`1`来得到档位`gear`的值。例如,速度为`35.0`时,挡位为`4`:
|
||||
无论何时当你设置`AutomaticCar`的`currentSpeed`属性,属性的`didSet`观察器就会自动地设置`gear`属性,为新的速度选择一个合适的挡位。具体来说就是,属性观察器将新的速度值除以`10`,然后向下取得最接近的整数值,最后加`1`来得到档位`gear`的值。例如,速度为`35.0`时,挡位为`4`:
|
||||
|
||||
```swift
|
||||
let automatic = AutomaticCar()
|
||||
@ -240,6 +246,6 @@ print("AutomaticCar: \(automatic.description)")
|
||||
|
||||
你可以通过把方法,属性或下标标记为*`final`*来防止它们被重写,只需要在声明关键字前加上`final`修饰符即可(例如:`final var`,`final func`,`final class func`,以及`final subscript`)。
|
||||
|
||||
如果你重写了带有`final`标记的方法、属性或下标,在编译时会报错。在类扩展中的方法,属性或下标也可以在扩展的定义里标记为 final 的。
|
||||
任何试图对带有`final`标记的方法、属性或下标进行重写,都会在编译时会报错。在类扩展中的方法,属性或下标也可以在扩展的定义里标记为 final 的。
|
||||
|
||||
你可以通过在关键字`class`前添加`final`修饰符(`final class`)来将整个类标记为 final 的。这样的类是不可被继承的,试图继承这样的类会导致编译报错。
|
||||
|
||||
@ -23,6 +23,9 @@
|
||||
> 4.0
|
||||
> 翻译:[muhlenXi](https://github.com/muhlenxi) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [存储属性的初始赋值](#setting_initial_values_for_stored_properties)
|
||||
@ -48,7 +51,8 @@
|
||||
你可以在构造器中为存储型属性赋初值,也可以在定义属性时为其设置默认值。以下小节将详细介绍这两种方法。
|
||||
|
||||
> 注意
|
||||
当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观察者。
|
||||
>
|
||||
> 当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观察者。
|
||||
|
||||
<a name="initializers"></a>
|
||||
### 构造器
|
||||
@ -83,7 +87,8 @@ print("The default temperature is \(f.temperature)° Fahrenheit")
|
||||
如前所述,你可以在构造器中为存储型属性设置初始值。同样,你也可以在属性声明时为其设置默认值。
|
||||
|
||||
> 注意
|
||||
如果一个属性总是使用相同的初始值,那么为其设置一个默认值比每次都在构造器中赋值要好。两种方法的效果是一样的,只不过使用默认值让属性的初始化和声明结合得更紧密。使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型;同时,它也能让你充分利用默认构造器、构造器继承等特性,后续章节将讲到。
|
||||
>
|
||||
> 如果一个属性总是使用相同的初始值,那么为其设置一个默认值比每次都在构造器中赋值要好。两种方法的效果是一样的,只不过使用默认值让属性的初始化和声明结合得更紧密。使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型;同时,它也能让你充分利用默认构造器、构造器继承等特性,后续章节将讲到。
|
||||
|
||||
你可以使用更简单的方式在定义结构体 `Fahrenheit` 时为属性 `temperature` 设置默认值:
|
||||
|
||||
@ -101,7 +106,7 @@ struct Fahrenheit {
|
||||
<a name="initialization_parameters"></a>
|
||||
### 构造参数
|
||||
|
||||
自定义`构造过程`时,可以在定义中提供构造参数,指定参数值的类型和名字。构造参数的功能和语法跟函数和方法的参数相同。
|
||||
自定义构造过程时,可以在定义中提供*构造参数*,指定参数值的类型和名字。构造参数的功能和语法跟函数和方法的参数相同。
|
||||
|
||||
下面例子中定义了一个包含摄氏度温度的结构体 `Celsius`。它定义了两个不同的构造器:`init(fromFahrenheit:)`和`init(fromKelvin:)`,二者分别通过接受不同温标下的温度值来创建新的实例:
|
||||
|
||||
@ -124,12 +129,12 @@ let freezingPointOfWater = Celsius(fromKelvin: 273.15)
|
||||
|
||||
第一个构造器拥有一个构造参数,其外部名字为`fromFahrenheit`,内部名字为`fahrenheit`;第二个构造器也拥有一个构造参数,其外部名字为`fromKelvin`,内部名字为`kelvin`。这两个构造器都将唯一的参数值转换成摄氏温度值,并保存在属性 `temperatureInCelsius` 中。
|
||||
|
||||
<a name="local_and_external_parameter_names"></a>
|
||||
### 参数的内部名称和外部名称
|
||||
<a name="parameter_names_and_argument_labels"></a>
|
||||
### 参数名和参数标签
|
||||
|
||||
跟函数和方法参数相同,构造参数也拥有一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字。
|
||||
跟函数和方法参数相同,构造参数也拥有一个在构造器内部使用的参数名和一个在调用构造器时使用的参数标签。
|
||||
|
||||
然而,构造器并不像函数和方法那样在括号前有一个可辨别的名字。因此在调用构造器时,主要通过构造器中的参数名和类型来确定应该被调用的构造器。正因为参数如此重要,如果你在定义构造器时没有提供参数的外部名字,Swift 会为构造器的每个参数自动生成一个跟内部名字相同的外部名。
|
||||
然而,构造器并不像函数和方法那样在括号前有一个可辨别的名字。因此在调用构造器时,主要通过构造器中的参数名和类型来确定应该被调用的构造器。正因为参数如此重要,如果你在定义构造器时没有提供参数标签,Swift 会为构造器的*每个*参数自动生成一个参数标签。
|
||||
|
||||
以下例子中定义了一个结构体 `Color`,它包含了三个常量:`red`、`green` 和 `blue`。这些属性可以存储 `0.0` 到 `1.0` 之间的值,用来指示颜色中红、绿、蓝成分的含量。
|
||||
|
||||
@ -158,7 +163,7 @@ let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
|
||||
let halfGray = Color(white: 0.5)
|
||||
```
|
||||
|
||||
注意,如果不通过外部参数名字传值,你是没法调用这个构造器的。只要构造器定义了某个外部参数名,你就必须使用它,忽略它将导致编译错误:
|
||||
注意,如果不通过参数标签传值,你是没法调用这个构造器的。只要构造器定义了某个参数标签,你就必须使用它,忽略它将导致编译错误:
|
||||
|
||||
```swift
|
||||
let veryGreen = Color(0.0, 1.0, 0.0)
|
||||
@ -166,9 +171,9 @@ let veryGreen = Color(0.0, 1.0, 0.0)
|
||||
```
|
||||
|
||||
<a name="initializer_parameters_without_external_names"></a>
|
||||
### 不带外部名的构造器参数
|
||||
### 不带参数标签的构造器参数
|
||||
|
||||
如果你不希望为构造器的某个参数提供外部名字,你可以使用下划线(`_`)来显式描述它的外部名,以此重写上面所说的默认行为。
|
||||
如果你不希望为构造器的某个参数提供参数标签,你可以使用下划线(`_`)来显式描述它的外部名,以此重写上面所说的默认行为。
|
||||
|
||||
下面是之前 `Celsius` 例子的扩展,跟之前相比添加了一个带有 `Double` 类型参数的构造器,其外部名用 `_` 代替:
|
||||
|
||||
@ -190,7 +195,7 @@ let bodyTemperature = Celsius(37.0)
|
||||
// bodyTemperature.temperatureInCelsius 为 37.0
|
||||
```
|
||||
|
||||
调用 `Celsius(37.0)` 意图明确,不需要外部参数名称。因此适合使用 `init(_ celsius: Double)` 这样的构造器,从而可以通过提供 `Double` 类型的参数值调用构造器,而不需要加上外部名。
|
||||
调用 `Celsius(37.0)` 意图明确,不需要参数标签。因此适合使用 `init(_ celsius: Double)` 这样的构造器,从而可以通过提供未命名的`Double`值调用构造器,而不需要加上参数标签。
|
||||
|
||||
<a name="optional_property_types"></a>
|
||||
### 可选属性类型
|
||||
@ -225,7 +230,8 @@ cheeseQuestion.response = "Yes, I do like cheese."
|
||||
你可以在构造过程中的任意时间点给常量属性指定一个值,只要在构造过程结束时是一个确定的值。一旦常量属性被赋值,它将永远不可更改。
|
||||
|
||||
> 注意
|
||||
对于类的实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。
|
||||
>
|
||||
> 对于类的实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。
|
||||
|
||||
你可以修改上面的 `SurveyQuestion` 示例,用常量属性替代变量属性 `text`,表示问题内容 `text` 在`SurveyQuestion`的实例被创建之后不会再被修改。尽管 `text` 属性现在是常量,我们仍然可以在类的构造器中设置它的值:
|
||||
|
||||
@ -249,7 +255,7 @@ beetsQuestion.response = "I also like beets. (But not with cheese.)"
|
||||
<a name="default_initializers"></a>
|
||||
## 默认构造器
|
||||
|
||||
如果结构体或类的所有属性都有默认值,同时没有自定义的构造器,那么 Swift 会给这些结构体或类提供一个默认构造器(default initializers)。这个默认构造器将简单地创建一个所有属性值都设置为默认值的实例。
|
||||
如果结构体或类的所有属性都有默认值,同时没有自定义的构造器,那么 Swift 会给这些结构体或类提供一个*默认构造器(default initializers)*。这个默认构造器将简单地创建一个所有属性值都设置为默认值的实例。
|
||||
|
||||
下面例子中创建了一个类 `ShoppingListItem`,它封装了购物清单中的某一物品的属性:名字(`name`)、数量(`quantity`)和购买状态 `purchase state`:
|
||||
|
||||
@ -267,7 +273,7 @@ var item = ShoppingListItem()
|
||||
<a name="memberwise_initializers_for_structure_types"></a>
|
||||
### 结构体的逐一成员构造器
|
||||
|
||||
除了上面提到的默认构造器,如果结构体没有提供自定义的构造器,它们将自动获得一个逐一成员构造器,即使结构体的存储型属性没有默认值。
|
||||
除了上面提到的默认构造器,如果结构体没有提供自定义的构造器,它们将自动获得一个*逐一成员构造器(memberwise initializer)*,即使结构体的存储型属性没有默认值。
|
||||
|
||||
逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法。我们在调用逐一成员构造器时,通过与成员属性名相同的参数名进行传值来完成对成员属性的初始赋值。
|
||||
|
||||
@ -285,7 +291,7 @@ let twoByTwo = Size(width: 2.0, height: 2.0)
|
||||
<a name="initializer_delegation_for_value_types"></a>
|
||||
## 值类型的构造器代理
|
||||
|
||||
构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能避免多个构造器间的代码重复。
|
||||
构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为*构造器代理*,它能避免多个构造器间的代码重复。
|
||||
|
||||
构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给自己的其它构造器。类则不同,它可以继承自其它类(请参考[继承](./13_Inheritance.html)),这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节[类的继承和构造过程](#class_inheritance_and_initialization)中介绍。
|
||||
|
||||
@ -294,7 +300,8 @@ let twoByTwo = Size(width: 2.0, height: 2.0)
|
||||
请注意,如果你为某个值类型定义了一个自定义的构造器,你将无法访问到默认构造器(如果是结构体,还将无法访问逐一成员构造器)。这种限制可以防止你为值类型增加了一个额外的且十分复杂的构造器之后,仍然有人错误的使用自动生成的构造器
|
||||
|
||||
> 注意
|
||||
假如你希望默认构造器、逐一成员构造器以及你自己的自定义构造器都能用来创建实例,可以将自定义的构造器写到扩展(`extension`)中,而不是写在值类型的原始定义中。想查看更多内容,请查看[扩展](./21_Extensions.html)章节。
|
||||
>
|
||||
> 假如你希望默认构造器、逐一成员构造器以及你自己的自定义构造器都能用来创建实例,可以将自定义的构造器写到扩展(`extension`)中,而不是写在值类型的原始定义中。想查看更多内容,请查看[扩展](./20_Extensions.html)章节。
|
||||
|
||||
下面例子将定义一个结构体 `Rect`,用来代表几何矩形。这个例子需要两个辅助的结构体 `Size` 和 `Point`,它们各自为其所有的属性提供了默认初始值 `0.0`。
|
||||
|
||||
@ -355,13 +362,14 @@ let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
|
||||
构造器 `init(center:size:)` 可以直接将 `origin` 和 `size` 的新值赋值到对应的属性中。然而,构造器 `init(center:size:)` 通过使用提供了相关功能的现有构造器将会更加便捷。
|
||||
|
||||
> 注意
|
||||
如果你想用另外一种不需要自己定义`init()`和`init(origin:size:)`的方式来实现这个例子,请参考[扩展](./21_Extensions.html)。
|
||||
>
|
||||
> 如果你想用另外一种不需要自己定义`init()`和`init(origin:size:)`的方式来实现这个例子,请参考[扩展](./21_Extensions.html)。
|
||||
|
||||
<a name="class_inheritance_and_initialization"></a>
|
||||
## 类的继承和构造过程
|
||||
|
||||
类里面的所有存储型属性——包括所有继承自父类的属性——都必须在构造过程中设置初始值。
|
||||
|
||||
类里面的所有存储型属性——包括所有继承自父类的属性——都*必须*在构造过程中设置初始值。
|
||||
[]()
|
||||
Swift 为类类型提供了两种构造器来确保实例中所有存储型属性都能获得初始值,它们分别是指定构造器和便利构造器。
|
||||
|
||||
<a name="designated_initializers_and_convenience_initializers"></a>
|
||||
@ -403,15 +411,15 @@ convenience init(parameters) {
|
||||
|
||||
##### 规则 1
|
||||
|
||||
指定构造器必须调用其直接父类的的指定构造器。
|
||||
指定构造器必须调用其直接父类的的指定构造器。
|
||||
|
||||
##### 规则 2
|
||||
|
||||
便利构造器必须调用*同*类中定义的其它构造器。
|
||||
便利构造器必须调用*同*类中定义的其它构造器。
|
||||
|
||||
##### 规则 3
|
||||
|
||||
便利构造器最后必须调用指定构造器。
|
||||
便利构造器最后必须调用指定构造器。
|
||||
|
||||
一个更方便记忆的方法是:
|
||||
|
||||
@ -427,7 +435,8 @@ convenience init(parameters) {
|
||||
子类中包含两个指定构造器和一个便利构造器。便利构造器必须调用两个指定构造器中的任意一个,因为它只能调用同一个类里的其他构造器。这满足了上面提到的规则 2 和 3。而两个指定构造器必须调用父类中唯一的指定构造器,这满足了规则 1。
|
||||
|
||||
> 注意
|
||||
这些规则不会影响类的实例如何创建。任何上图中展示的构造器都可以用来创建完全初始化的实例。这些规则只影响类的构造器如何实现。
|
||||
>
|
||||
> 这些规则不会影响类的实例如何创建。任何上图中展示的构造器都可以用来创建完全初始化的实例。这些规则只影响类的构造器如何实现。
|
||||
|
||||
下面图例中展示了一种涉及四个类的更复杂的类层级结构。它演示了指定构造器是如何在类层级中充当“管道”的作用,在类的构造器链上简化了类之间的相互关系。
|
||||
|
||||
@ -441,27 +450,28 @@ Swift 中类的构造过程包含两个阶段。第一个阶段,类中的每
|
||||
两段式构造过程的使用让构造过程更安全,同时在整个类层级结构中给予了每个类完全的灵活性。两段式构造过程可以防止属性值在初始化之前被访问,也可以防止属性被另外一个构造器意外地赋予不同的值。
|
||||
|
||||
> 注意
|
||||
Swift 的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1,Objective-C 给每一个属性赋值 `0` 或空值(比如说`0`或`nil`)。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以 `0` 或 `nil` 作为合法默认值的情况。
|
||||
>
|
||||
> Swift 的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1,Objective-C 给每一个属性赋值 `0` 或空值(比如说`0`或`nil`)。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以 `0` 或 `nil` 作为合法默认值的情况。
|
||||
|
||||
Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程不出错地完成:
|
||||
|
||||
##### 安全检查 1
|
||||
|
||||
指定构造器必须保证它所在类的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。
|
||||
指定构造器必须保证它所在类的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。
|
||||
|
||||
如上所述,一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足这一规则,指定构造器必须保证它所在类的属性在它往上代理之前先完成初始化。
|
||||
|
||||
##### 安全检查 2
|
||||
|
||||
指定构造器必须在为继承的属性设置新值之前向上代理调用父类构造器,如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。
|
||||
指定构造器必须在为继承的属性设置新值之前向上代理调用父类构造器,如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。
|
||||
|
||||
##### 安全检查 3
|
||||
|
||||
便利构造器必须为任意属性(包括同类中定义的)赋新值之前代理调用同一类中的其它构造器,如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。
|
||||
便利构造器必须为任意属性(包括同类中定义的)赋新值之前代理调用同一类中的其它构造器,如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。
|
||||
|
||||
##### 安全检查 4
|
||||
|
||||
构造器在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引用`self`作为一个值。
|
||||
构造器在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引用`self`作为一个值。
|
||||
|
||||
类实例在第一阶段结束以前并不是完全有效的。只有第一阶段完成后,该实例才会成为有效实例,才能访问属性和调用方法。
|
||||
|
||||
@ -509,7 +519,8 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
|
||||
跟 Objective-C 中的子类不同,Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更精细的子类继承,并被错误地用来创建子类的实例。
|
||||
|
||||
> 注意
|
||||
父类的构造器仅会在安全和适当的情况下被继承。具体内容请参考后续章节[构造器的自动继承](#automatic_initializer_inheritance)。
|
||||
>
|
||||
> 父类的构造器仅会在安全和适当的情况下被继承。具体内容请参考后续章节[构造器的自动继承](#automatic_initializer_inheritance)。
|
||||
|
||||
假如你希望自定义的子类中能提供一个或多个跟父类相同的构造器,你可以在子类中提供这些构造器的自定义实现。
|
||||
|
||||
@ -518,7 +529,8 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
|
||||
正如重写属性,方法或者是下标,`override` 修饰符会让编译器去检查父类中是否有相匹配的指定构造器,并验证构造器参数是否正确。
|
||||
|
||||
> 注意
|
||||
当你重写一个父类的指定构造器时,你总是需要写`override`修饰符,即使是为了实现子类的便利构造器。
|
||||
>
|
||||
> 当你重写一个父类的指定构造器时,你总是需要写`override`修饰符,即使是为了实现子类的便利构造器。
|
||||
|
||||
相反,如果你编写了一个和父类便利构造器相匹配的子类构造器,由于子类不能直接调用父类的便利构造器(每个规则都在上文[类的构造器代理规则](#initializer_delegation_for_class_types)有所描述),因此,严格意义上来讲,你的子类并未对一个父类构造器提供重写。最后的结果就是,你在子类中“重写”一个父类便利构造器时,不需要加 `override` 修饰符。
|
||||
|
||||
@ -566,7 +578,8 @@ print("Bicycle: \(bicycle.description)")
|
||||
```
|
||||
|
||||
> 注意
|
||||
子类可以在初始化时修改继承来的变量属性,但是不能修改继承来的常量属性。
|
||||
>
|
||||
> 子类可以在初始化时修改继承来的变量属性,但是不能修改继承来的常量属性。
|
||||
|
||||
<a name="automatic_initializer_inheritance"></a>
|
||||
### 构造器的自动继承
|
||||
@ -577,16 +590,17 @@ print("Bicycle: \(bicycle.description)")
|
||||
|
||||
##### 规则 1
|
||||
|
||||
如果子类没有定义任何指定构造器,它将自动继承父类所有的指定构造器。
|
||||
如果子类没有定义任何指定构造器,它将自动继承父类所有的指定构造器。
|
||||
|
||||
##### 规则 2
|
||||
|
||||
如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承父类所有的便利构造器。
|
||||
如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承父类所有的便利构造器。
|
||||
|
||||
即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。
|
||||
|
||||
> 注意
|
||||
对于规则 2,子类可以将父类的指定构造器实现为便利构造器。
|
||||
>
|
||||
> 对于规则 2,子类可以将父类的指定构造器实现为便利构造器。
|
||||
|
||||
<a name="designated_and_convenience_initializers_in_action"></a>
|
||||
### 指定构造器和便利构造器实践
|
||||
@ -681,7 +695,8 @@ class ShoppingListItem: RecipeIngredient {
|
||||
```
|
||||
|
||||
> 注意
|
||||
`ShoppingListItem` 没有定义构造器来为 `purchased` 提供初始值,因为添加到购物单的物品的初始状态总是未购买。
|
||||
>
|
||||
> `ShoppingListItem` 没有定义构造器来为 `purchased` 提供初始值,因为添加到购物单的物品的初始状态总是未购买。
|
||||
|
||||
由于它为自己引入的所有属性都提供了默认值,并且自己没有定义任何构造器,`ShoppingListItem` 将自动继承所有父类中的指定构造器和便利构造器。
|
||||
|
||||
@ -717,12 +732,14 @@ for item in breakfastList {
|
||||
为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在 `init` 关键字后面添加问号 (`init?`)。
|
||||
|
||||
> 注意
|
||||
可失败构造器的参数名和参数类型,不能与其它非可失败构造器的参数名,及其参数类型相同。
|
||||
>
|
||||
> 可失败构造器的参数名和参数类型,不能与其它非可失败构造器的参数名,及其参数类型相同。
|
||||
|
||||
可失败构造器会创建一个类型为自身类型的可选类型的对象。你通过 `return nil` 语句来表明可失败构造器在何种情况下应该 “失败”。
|
||||
|
||||
> 注意
|
||||
严格来说,构造器都不支持返回值。因为构造器本身的作用,只是为了确保对象能被正确构造。因此你只是用`return nil`表明可失败构造器构造失败,而不要用关键字`return`来表明构造成功。
|
||||
>
|
||||
> 严格来说,构造器都不支持返回值。因为构造器本身的作用,只是为了确保对象能被正确构造。因此你只是用`return nil`表明可失败构造器构造失败,而不要用关键字`return`来表明构造成功。
|
||||
|
||||
例如,实现针对数字类型转换的可失败构造器。确保数字类型之间的转换能保持精确的值,使用这个 `init(exactly:)` 构造器。如果类型转换不能保持值不变,则这个构造器构造失败。
|
||||
|
||||
@ -783,7 +800,8 @@ if anonymousCreature == nil {
|
||||
```
|
||||
|
||||
> 注意
|
||||
空字符串(如 `""`,而不是 `"Giraffe"` )和一个值为 `nil` 的可选类型的字符串是两个完全不同的概念。上例中的空字符串(`""`)其实是一个有效的,非可选类型的字符串。这里我们之所以让 `Animal` 的可失败构造器构造失败,只是因为对于 `Animal` 这个类的 `species` 属性来说,它更适合有一个具体的值,而不是空字符串。
|
||||
>
|
||||
> 空字符串(如 `""`,而不是 `"Giraffe"` )和一个值为 `nil` 的可选类型的字符串是两个完全不同的概念。上例中的空字符串(`""`)其实是一个有效的,非可选类型的字符串。这里我们之所以让 `Animal` 的可失败构造器构造失败,只是因为对于 `Animal` 这个类的 `species` 属性来说,它更适合有一个具体的值,而不是空字符串。
|
||||
|
||||
<a name="failable_nitializers_for_enumerations"></a>
|
||||
### 枚举类型的可失败构造器
|
||||
@ -859,7 +877,8 @@ if unknownUnit == nil {
|
||||
无论是向上代理还是横向代理,如果你代理到的其他可失败构造器触发构造失败,整个构造过程将立即终止,接下来的任何构造代码不会再被执行。
|
||||
|
||||
> 注意
|
||||
可失败构造器也可以代理到其它的非可失败构造器。通过这种方式,你可以增加一个可能的失败状态到现有的构造过程中。
|
||||
>
|
||||
> 可失败构造器也可以代理到其它的非可失败构造器。通过这种方式,你可以增加一个可能的失败状态到现有的构造过程中。
|
||||
|
||||
下面这个例子,定义了一个名为`CartItem`的`Product`类的子类。这个类建立了一个在线购物车中的物品的模型,它有一个名为`quantity`的常量存储型属性,并确保该属性的值至少为`1`:
|
||||
|
||||
@ -923,7 +942,8 @@ if let oneUnnamed = CartItem(name: "", quantity: 1) {
|
||||
注意,当你用子类的非可失败构造器重写父类的可失败构造器时,向上代理到父类的可失败构造器的唯一方式是对父类的可失败构造器的返回值进行强制解包。
|
||||
|
||||
> 注意
|
||||
你可以用非可失败构造器重写可失败构造器,但反过来却不行。
|
||||
>
|
||||
> 你可以用非可失败构造器重写可失败构造器,但反过来却不行。
|
||||
|
||||
下例定义了一个名为 `Document` 的类,`name` 属性的值必须为一个非空字符串或 `nil`,但不能是一个空字符串:
|
||||
|
||||
@ -974,7 +994,7 @@ class UntitledDocument: Document {
|
||||
在这个例子中,如果在调用父类的可失败构造器 `init?(name:)` 时传入的是空字符串,那么强制解包操作会引发运行时错误。不过,因为这里是通过非空的字符串常量来调用它,所以并不会发生运行时错误。
|
||||
|
||||
<a name="the_init!_failable_initializer"></a>
|
||||
### 可失败构造器 init!
|
||||
### init!可失败构造器
|
||||
|
||||
通常来说我们通过在`init`关键字后添加问号的方式(`init?`)来定义一个可失败构造器,但你也可以通过在`init`后面添加惊叹号的方式来定义一个可失败构造器(`init!`),该可失败构造器将会构建一个对应类型的隐式解包可选类型的对象。
|
||||
|
||||
@ -1004,7 +1024,8 @@ class SomeSubclass: SomeClass {
|
||||
```
|
||||
|
||||
> 注意
|
||||
如果子类继承的构造器能满足必要构造器的要求,则无须在子类中显式提供必要构造器的实现。
|
||||
>
|
||||
> 如果子类继承的构造器能满足必要构造器的要求,则无须在子类中显式提供必要构造器的实现。
|
||||
|
||||
<a name="setting_a_default_property_value_with_a_closure_or_function"></a>
|
||||
## 通过闭包或函数设置属性的默认值
|
||||
@ -1028,7 +1049,8 @@ class SomeClass {
|
||||
注意闭包结尾的花括号后面接了一对空的小括号。这用来告诉 Swift 立即执行此闭包。如果你忽略了这对括号,相当于将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。
|
||||
|
||||
> 注意
|
||||
如果你使用闭包来初始化属性,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能在闭包里访问其它属性,即使这些属性有默认值。同样,你也不能使用隐式的 `self` 属性,或者调用任何实例方法。
|
||||
>
|
||||
> 如果你使用闭包来初始化属性,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能在闭包里访问其它属性,即使这些属性有默认值。同样,你也不能使用隐式的 `self` 属性,或者调用任何实例方法。
|
||||
|
||||
下面例子中定义了一个结构体 `Chessboard`,它构建了西洋跳棋游戏的棋盘,西洋跳棋游戏在一副黑白格交替的 8 x 8 的棋盘中进行的:
|
||||
|
||||
|
||||
@ -18,6 +18,9 @@
|
||||
> 4.0
|
||||
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [析构过程原理](#how_deinitialization_works)
|
||||
@ -28,7 +31,7 @@
|
||||
<a name="how_deinitialization_works"></a>
|
||||
## 析构过程原理
|
||||
|
||||
Swift 会自动释放不再需要的实例以释放资源。如[自动引用计数](./16_Automatic_Reference_Counting.html)章节中所讲述,Swift 通过`自动引用计数(ARC)`处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件。
|
||||
Swift 会自动释放不再需要的实例以释放资源。如[自动引用计数](./23_Automatic_Reference_Counting.html)章节中所讲述,Swift 通过*自动引用计数(ARC)* 处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件。
|
||||
|
||||
在类的定义中,每个类最多只能有一个析构器,而且析构器不带任何参数,如下所示:
|
||||
|
||||
|
||||
@ -19,6 +19,9 @@
|
||||
> 4.0
|
||||
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [使用可选链式调用代替强制展开](#optional_chaining_as_an_alternative_to_forced_unwrapping)
|
||||
@ -32,7 +35,8 @@
|
||||
*可选链式调用*是一种可以在当前值可能为`nil`的可选值上请求和调用属性、方法及下标的方法。如果可选值有值,那么调用就会成功;如果可选值是`nil`,那么调用将返回`nil`。多个调用可以连接在一起形成一个调用链,如果其中任何一个节点为`nil`,整个调用链都会失败,即返回`nil`。
|
||||
|
||||
> 注意
|
||||
Swift 的可选链式调用和 Objective-C 中向`nil`发送消息有些相像,但是 Swift 的可选链式调用可以应用于任意类型,并且能检查调用是否成功。
|
||||
>
|
||||
> Swift 的可选链式调用和 Objective-C 中向`nil`发送消息有些相像,但是 Swift 的可选链式调用可以应用于任意类型,并且能检查调用是否成功。
|
||||
|
||||
<a name="optional_chaining_as_an_alternative_to_forced_unwrapping"></a>
|
||||
## 使用可选链式调用代替强制展开
|
||||
@ -274,7 +278,8 @@ if (john.residence?.address = someAddress) != nil {
|
||||
通过可选链式调用,我们可以在一个可选值上访问下标,并且判断下标调用是否成功。
|
||||
|
||||
> 注意
|
||||
通过可选链式调用访问可选值的下标时,应该将问号放在下标方括号的前面而不是后面。可选链式调用的问号一般直接跟在可选表达式的后面。
|
||||
>
|
||||
> 通过可选链式调用访问可选值的下标时,应该将问号放在下标方括号的前面而不是后面。可选链式调用的问号一般直接跟在可选表达式的后面。
|
||||
|
||||
下面这个例子用下标访问`john.residence`属性存储的`Residence`实例的`rooms`数组中的第一个房间的名称,因为`john.residence`为`nil`,所以下标调用失败了:
|
||||
|
||||
@ -405,4 +410,5 @@ if let beginsWithThe =
|
||||
```
|
||||
|
||||
> 注意
|
||||
在上面的例子中,在方法的圆括号后面加上问号是因为你要在`buildingIdentifier()`方法的可选返回值上进行可选链式调用,而不是方法本身。
|
||||
>
|
||||
> 在上面的例子中,在方法的圆括号后面加上问号是因为你要在`buildingIdentifier()`方法的可选返回值上进行可选链式调用,而不是`buildingIdentifier()`方法本身。
|
||||
@ -15,6 +15,9 @@
|
||||
> 4.0
|
||||
> 翻译+校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [表示并抛出错误](#representing_and_throwing_errors)
|
||||
@ -28,7 +31,8 @@
|
||||
举个例子,假如有个从磁盘上的某个文件读取数据并进行处理的任务,该任务会有多种可能失败的情况,包括指定路径下文件并不存在,文件不具有可读权限,或者文件编码格式不兼容。区分这些不同的失败情况可以让程序解决并处理某些错误,然后把它解决不了的错误报告给用户。
|
||||
|
||||
> 注意
|
||||
Swift 中的错误处理涉及到错误处理模式,这会用到 Cocoa 和 Objective-C 中的`NSError`。关于这个类的更多信息请参见 [Using Swift with Cocoa and Objective-C (Swift 4)](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的[错误处理](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10)。
|
||||
>
|
||||
> Swift 中的错误处理涉及到错误处理模式,这会用到 Cocoa 和 Objective-C 中的`NSError`。关于这个类的更多信息请参见 [Using Swift with Cocoa and Objective-C (Swift 4.1)](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的[错误处理](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10)。
|
||||
|
||||
<a name="representing_and_throwing_errors"></a>
|
||||
## 表示并抛出错误
|
||||
@ -61,6 +65,7 @@ Swift 中有`4`种处理错误的方式。你可以把函数抛出的错误传
|
||||
当一个函数抛出一个错误时,你的程序流程会发生改变,所以重要的是你能迅速识别代码中会抛出错误的地方。为了标识出这些地方,在调用一个能抛出错误的函数、方法或者构造器之前,加上`try`关键字,或者`try?`或`try!`这种变体。这些关键字在下面的小节中有具体讲解。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> Swift 中的错误处理和其他语言中用`try`,`catch`和`throw`进行异常处理很像。和其他语言中(包括 Objective-C )的异常处理不同的是,Swift 中的错误处理并不涉及解除调用栈,这是一个计算代价高昂的过程。就此而言,`throw`语句的性能特性是可以和`return`语句相媲美的。
|
||||
|
||||
<a name="propagating_errors_using_throwing_functions"></a>
|
||||
@ -76,7 +81,8 @@ func cannotThrowErrors() -> String
|
||||
一个 throwing 函数可以在其内部抛出错误,并将错误传递到函数被调用时的作用域。
|
||||
|
||||
> 注意
|
||||
只有 throwing 函数可以传递错误。任何在某个非 throwing 函数内部抛出的错误只能在函数内部处理。
|
||||
>
|
||||
> 只有 throwing 函数可以传递错误。任何在某个非 throwing 函数内部抛出的错误只能在函数内部处理。
|
||||
|
||||
下面的例子中,`VendingMachine`类有一个`vend(itemNamed:)`方法,如果请求的物品不存在、缺货或者投入金额小于物品价格,该方法就会抛出一个相应的`VendingMachineError`:
|
||||
|
||||
@ -153,7 +159,7 @@ struct PurchasedSnack {
|
||||
|
||||
### 用 Do-Catch 处理错误
|
||||
|
||||
可以使用一个`do-catch`语句运行一段闭包代码来处理错误。如果在`do`子句中的代码抛出了一个错误,这个错误会与`catch`子句做匹配,从而决定哪条子句能处理它。
|
||||
你可以使用一个`do-catch`语句运行一段闭包代码来处理错误。如果在`do`子句中的代码抛出了一个错误,这个错误会与`catch`子句做匹配,从而决定哪条子句能处理它。
|
||||
|
||||
下面是`do-catch`语句的一般形式:
|
||||
|
||||
@ -233,9 +239,9 @@ let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
|
||||
<a name="specifying_cleanup_actions"></a>
|
||||
## 指定清理操作
|
||||
|
||||
可以使用`defer`语句在即将离开当前代码块时执行一系列语句。该语句让你能执行一些必要的清理工作,不管是以何种方式离开当前代码块的——无论是由于抛出错误而离开,或是由于诸如`return`、`break`的语句。例如,你可以用`defer`语句来确保文件描述符得以关闭,以及手动分配的内存得以释放。
|
||||
你可以使用`defer`语句在即将离开当前代码块时执行一系列语句。该语句让你能执行一些必要的清理工作,不管是以何种方式离开当前代码块的——无论是由于抛出错误而离开,或是由于诸如`return`、`break`的语句。例如,你可以用`defer`语句来确保文件描述符得以关闭,以及手动分配的内存得以释放。
|
||||
|
||||
`defer`语句将代码的执行延迟到当前的作用域退出之前。该语句由`defer`关键字和要被延迟执行的语句组成。延迟执行的语句不能包含任何控制转移语句,例如`break`、`return`语句,或是抛出一个错误。延迟执行的操作会按照它们声明的顺序从后往前执行——也就是说,第一条`defer`语句中的代码最后才执行,第二条`defer`语句中的代码倒数第二个执行,以此类推。最后一条语句会第一个执行
|
||||
`defer`语句将代码的执行延迟到当前的作用域退出之前。该语句由`defer`关键字和要被延迟执行的语句组成。延迟执行的语句不能包含任何控制转移语句,例如`break`、`return`语句,或是抛出一个错误。延迟执行的操作会按照它们声明的顺序从后往前执行——也就是说,第一条`defer`语句中的代码最后才执行,第二条`defer`语句中的代码倒数第二个执行,以此类推。最后一条语句会第一个执行。
|
||||
|
||||
```swift
|
||||
func processFile(filename: String) throws {
|
||||
@ -255,4 +261,5 @@ func processFile(filename: String) throws {
|
||||
上面的代码使用一条`defer`语句来确保`open(_:)`函数有一个相应的对`close(_:)`函数的调用。
|
||||
|
||||
> 注意
|
||||
> 即使没有涉及到错误处理,你也可以使用`defer`语句。
|
||||
>
|
||||
> 即使没有涉及到错误处理的代码,你也可以使用`defer`语句。
|
||||
@ -18,6 +18,9 @@
|
||||
> 4.0
|
||||
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [定义一个类层次作为例子](#defining_a_class_hierarchy_for_type_casting)
|
||||
@ -25,14 +28,14 @@
|
||||
- [向下转型](#downcasting)
|
||||
- [`Any` 和 `AnyObject` 的类型转换](#type_casting_for_any_and_anyobject)
|
||||
|
||||
_类型转换_ 可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。
|
||||
*类型转换*可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。
|
||||
|
||||
类型转换在 Swift 中使用 `is` 和 `as` 操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。
|
||||
|
||||
你也可以用它来检查一个类型是否实现了某个协议,就像在[检验协议的一致性](./22_Protocols.html#checking_for_protocol_conformance)部分讲述的一样。
|
||||
你也可以用它来检查一个类型是否实现了某个协议,就像在[检验协议的一致性](./21_Protocols.html#checking_for_protocol_conformance)部分讲述的一样。
|
||||
|
||||
<a name="defining_a_class_hierarchy_for_type_casting"></a>
|
||||
## 定义一个类层次作为例子
|
||||
## 为类型转换定义类层次
|
||||
|
||||
你可以将类型转换用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。下面的三个代码段定义了一个类层次和一个包含了这些类实例的数组,作为类型转换的例子。
|
||||
|
||||
@ -85,7 +88,7 @@ let library = [
|
||||
<a name="checking_type"></a>
|
||||
## 检查类型
|
||||
|
||||
用类型检查操作符(`is`)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 `true`,否则返回 `false`。
|
||||
用*类型检查操作符*(`is`)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 `true`,否则返回 `false`。
|
||||
|
||||
下面的例子定义了两个变量,`movieCount` 和 `songCount`,用来计算数组 `library` 中 `Movie` 和 `Song` 类型的实例数量:
|
||||
|
||||
@ -154,6 +157,7 @@ for item in library {
|
||||
若向下转型成功,然后 `movie` 的属性将用于打印一个 `Movie` 实例的描述,包括它的导演的名字 `director`。相似的原理被用来检测 `Song` 实例,当 `Song` 被找到时则打印它的描述(包含 `artist` 的名字)。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 转换没有真的改变实例或它的值。根本的实例保持不变;只是简单地把它作为它被转换成的类型来使用。
|
||||
|
||||
<a name="type_casting_for_any_and_anyobject"></a>
|
||||
@ -164,7 +168,7 @@ Swift 为不确定类型提供了两种特殊的类型别名:
|
||||
* `Any` 可以表示任何类型,包括函数类型。
|
||||
* `AnyObject` 可以表示任何类类型的实例。
|
||||
|
||||
只有当你确实需要它们的行为和功能时才使用 `Any` 和 `AnyObject`。在你的代码里使用你期望的明确类型总是更好的。
|
||||
只有当你确实需要它们的行为和功能时才使用 `Any` 和 `AnyObject`。当你期望你的代码可以工作,最好还是要确定类型。
|
||||
|
||||
这里有个示例,使用 `Any` 类型来和混合的不同类型一起工作,包括函数类型和非类类型。它创建了一个可以存储 `Any` 类型的数组 `things`:
|
||||
|
||||
@ -222,8 +226,10 @@ for thing in things {
|
||||
```
|
||||
|
||||
> 注意
|
||||
>
|
||||
> `Any`类型可以表示所有类型的值,包括可选类型。Swift 会在你用`Any`类型来表示一个可选值的时候,给你一个警告。如果你确实想使用`Any`类型来承载可选值,你可以使用`as`操作符显式转换为`Any`,如下所示:
|
||||
>
|
||||
>
|
||||
```swift
|
||||
let optionalNumber: Int? = 3
|
||||
things.append(optionalNumber) // 警告
|
||||
@ -18,6 +18,9 @@
|
||||
> 4.0
|
||||
> 翻译+校对:[EyreFree](https://www.eyrefree.org/) 2017-10-19
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [嵌套类型实践](#nested_types_in_action)
|
||||
@ -1,5 +1,5 @@
|
||||
# 扩展(Extensions)
|
||||
----
|
||||
# 扩展
|
||||
---------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[lyuka](https://github.com/lyuka)
|
||||
@ -18,6 +18,9 @@
|
||||
> 4.0
|
||||
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [扩展语法](#extension_syntax)
|
||||
@ -38,10 +41,11 @@ Swift 中的扩展可以:
|
||||
- 定义和使用新的嵌套类型
|
||||
- 使一个已有类型符合某个协议
|
||||
|
||||
在 Swift 中,你甚至可以对协议进行扩展,提供协议要求的实现,或者添加额外的功能,从而可以让符合协议的类型拥有这些功能。你可以从[协议扩展](./22_Protocols.html#protocol_extensions)获取更多的细节。
|
||||
在 Swift 中,你甚至可以对协议进行扩展,提供协议要求的实现,或者添加额外的功能,从而可以让符合协议的类型拥有这些功能。你可以从[协议扩展](./21_Protocols.html#protocol_extensions)获取更多的细节。
|
||||
|
||||
> 注意
|
||||
扩展可以为一个类型添加新的功能,但是不能重写已有的功能。
|
||||
>
|
||||
> 扩展可以为一个类型添加新的功能,但是不能重写已有的功能。
|
||||
|
||||
<a name="extension_syntax"></a>
|
||||
## 扩展语法
|
||||
@ -62,10 +66,11 @@ extension SomeType: SomeProtocol, AnotherProctocol {
|
||||
}
|
||||
```
|
||||
|
||||
通过这种方式添加协议一致性的详细描述请参阅[利用扩展添加协议一致性](./22_Protocols.html#adding_protocol_conformance_with_an_extension)。
|
||||
通过这种方式添加协议一致性的详细描述请参阅[利用扩展添加协议一致性](./21_Protocols.html#adding_protocol_conformance_with_an_extension)。
|
||||
|
||||
> 注意
|
||||
如果你通过扩展为一个已有类型添加新功能,那么新功能对该类型的所有已有实例都是可用的,即使它们是在这个扩展定义之前创建的。
|
||||
>
|
||||
> 如果你通过扩展为一个已有类型添加新功能,那么新功能对该类型的所有已有实例都是可用的,即使它们是在这个扩展定义之前创建的。
|
||||
|
||||
<a name="computed_properties"></a>
|
||||
## 计算型属性
|
||||
@ -103,7 +108,8 @@ print("A marathon is \(aMarathon) meters long")
|
||||
```
|
||||
|
||||
> 注意
|
||||
扩展可以添加新的计算型属性,但是不可以添加存储型属性,也不可以为已有属性添加属性观察器。
|
||||
>
|
||||
> 扩展可以添加新的计算型属性,但是不可以添加存储型属性,也不可以为已有属性添加属性观察器。
|
||||
|
||||
<a name="initializers"></a>
|
||||
## 构造器
|
||||
@ -113,7 +119,8 @@ print("A marathon is \(aMarathon) meters long")
|
||||
扩展能为类添加新的便利构造器,但是它们不能为类添加新的指定构造器或析构器。指定构造器和析构器必须总是由原始的类实现来提供。
|
||||
|
||||
> 注意
|
||||
如果你使用扩展为一个值类型添加构造器,同时该值类型的原始实现中未定义任何定制的构造器且所有存储属性提供了默认值,那么我们就可以在扩展中的构造器里调用默认构造器和逐一成员构造器。
|
||||
>
|
||||
> 如果你使用扩展为一个值类型添加构造器,同时该值类型的原始实现中未定义任何定制的构造器且所有存储属性提供了默认值,那么我们就可以在扩展中的构造器里调用默认构造器和逐一成员构造器。
|
||||
正如在[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)中描述的,如果你把定制的构造器写在值类型的原始实现中,上述规则将不再适用。
|
||||
|
||||
下面的例子定义了一个用于描述几何矩形的结构体 `Rect`。这个例子同时定义了两个辅助结构体 `Size` 和 `Point`,它们都把 `0.0` 作为所有属性的默认值:
|
||||
@ -159,7 +166,8 @@ let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
|
||||
```
|
||||
|
||||
> 注意
|
||||
如果你使用扩展提供了一个新的构造器,你依旧有责任确保构造过程能够让实例完全初始化。
|
||||
>
|
||||
> 如果你使用扩展提供了一个新的构造器,你依旧有责任确保构造过程能够让实例完全初始化。
|
||||
|
||||
<a name="methods"></a>
|
||||
## 方法
|
||||
@ -189,16 +197,6 @@ extension Int {
|
||||
// Hello!
|
||||
```
|
||||
|
||||
可以使用尾随闭包让调用更加简洁:
|
||||
|
||||
```swift
|
||||
3.repetitions {
|
||||
print("Goodbye!")
|
||||
}
|
||||
// Goodbye!
|
||||
// Goodbye!
|
||||
// Goodbye!
|
||||
```
|
||||
|
||||
<a name="mutating_instance_methods"></a>
|
||||
### 可变实例方法
|
||||
@ -264,16 +262,16 @@ extension Int {
|
||||
```swift
|
||||
extension Int {
|
||||
enum Kind {
|
||||
case Negative, Zero, Positive
|
||||
case negative, zero, positive
|
||||
}
|
||||
var kind: Kind {
|
||||
switch self {
|
||||
case 0:
|
||||
return .Zero
|
||||
return .zero
|
||||
case let x where x > 0:
|
||||
return .Positive
|
||||
return .positive
|
||||
default:
|
||||
return .Negative
|
||||
return .negative
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -289,11 +287,11 @@ extension Int {
|
||||
func printIntegerKinds(_ numbers: [Int]) {
|
||||
for number in numbers {
|
||||
switch number.kind {
|
||||
case .Negative:
|
||||
case .negative:
|
||||
print("- ", terminator: "")
|
||||
case .Zero:
|
||||
case .zero:
|
||||
print("0 ", terminator: "")
|
||||
case .Positive:
|
||||
case .positive:
|
||||
print("+ ", terminator: "")
|
||||
}
|
||||
}
|
||||
@ -306,4 +304,5 @@ printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
|
||||
函数 `printIntegerKinds(_:)` 接受一个 `Int` 数组,然后对该数组进行迭代。在每次迭代过程中,对当前整数的计算型属性 `kind` 的值进行评估,并打印出适当的描述。
|
||||
|
||||
> 注意
|
||||
由于已知 `number.kind` 是 `Int.Kind` 类型,因此在 `switch` 语句中,`Int.Kind` 中的所有成员值都可以使用简写形式,例如使用 `. Negative` 而不是 `Int.Kind.Negative`。
|
||||
>
|
||||
> 由于已知 `number.kind` 是 `Int.Kind` 类型,因此在 `switch` 语句中,`Int.Kind` 中的所有成员值都可以使用简写形式,例如使用 `.negative` 而不是 `Int.Kind.negative`。
|
||||
@ -17,7 +17,12 @@
|
||||
>
|
||||
> 3.0
|
||||
> 校对:[CMB](https://github.com/chenmingbiao),版本日期:2016-09-13
|
||||
> 3.0.1,shanks,2016-11-13
|
||||
|
||||
> 3.0.1
|
||||
> shanks,2016-11-13
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -72,7 +77,7 @@ class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
|
||||
<a name="property_requirements"></a>
|
||||
## 属性要求
|
||||
|
||||
协议可以要求遵循协议的类型提供特定名称和类型的实例属性或类型属性。协议不指定属性是存储型属性还是计算型属性,它只指定属性的名称和类型。此外,协议还指定属性是可读的还是可读可写的。
|
||||
协议可以要求遵循协议的类型提供特定名称和类型的实例属性或类型属性。协议不指定属性是存储型属性还是计算型属性,它只指定属性的名称和类型。此外,协议还指定属性是可读的还是*可读可写的*。
|
||||
|
||||
如果协议要求属性是可读可写的,那么该属性不能是常量属性或只读的计算型属性。如果协议只要求属性是可读的,那么该属性不仅可以是可读的,如果代码需要的话,还可以是可写的。
|
||||
|
||||
@ -158,7 +163,7 @@ protocol RandomNumberGenerator {
|
||||
}
|
||||
```
|
||||
|
||||
`RandomNumberGenerator` 协议要求遵循协议的类型必须拥有一个名为 `random`, 返回值类型为 `Double` 的实例方法。尽管这里并未指明,但是我们假设返回值在 `[0.0,1.0)` 区间内。
|
||||
`RandomNumberGenerator` 协议要求遵循协议的类型必须拥有一个名为 `random`, 返回值类型为 `Double` 的实例方法。尽管这里并未指明,但是我们假设返回值是从0.0到(但不包括)1.0。
|
||||
|
||||
`RandomNumberGenerator` 协议并不关心每一个随机数是怎样生成的,它只要求必须提供一个随机数生成器。
|
||||
|
||||
@ -183,13 +188,14 @@ print("And another one: \(generator.random())")
|
||||
```
|
||||
|
||||
<a name="mutating_method_requirements"></a>
|
||||
## Mutating 方法要求
|
||||
## 异变方法要求
|
||||
|
||||
有时需要在方法中改变方法所属的实例。例如,在值类型(即结构体和枚举)的实例方法中,将 `mutating` 关键字作为方法的前缀,写在 `func` 关键字之前,表示可以在该方法中修改它所属的实例以及实例的任意属性的值。这一过程在[在实例方法中修改值类型](./11_Methods.html#modifying_value_types_from_within_instance_methods)章节中有详细描述。
|
||||
有时需要在方法中改变(或*异变*)方法所属的实例。例如,在值类型(即结构体和枚举)的实例方法中,将 `mutating` 关键字作为方法的前缀,写在 `func` 关键字之前,表示可以在该方法中修改它所属的实例以及实例的任意属性的值。这一过程在[在实例方法中修改值类型](./11_Methods.html#modifying_value_types_from_within_instance_methods)章节中有详细描述。
|
||||
|
||||
如果你在协议中定义了一个实例方法,该方法会改变遵循该协议的类型的实例,那么在定义协议时需要在方法前加 `mutating` 关键字。这使得结构体和枚举能够遵循此协议并满足此方法要求。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 实现协议中的 `mutating` 方法时,若是类类型,则不用写 `mutating` 关键字。而对于结构体和枚举,则必须写 `mutating` 关键字。
|
||||
|
||||
如下所示,`Togglable` 协议只要求实现一个名为 `toggle` 的实例方法。根据名称的暗示,`toggle()` 方法将改变实例属性,从而切换遵循该协议类型的实例的状态。
|
||||
@ -220,7 +226,7 @@ enum OnOffSwitch: Togglable {
|
||||
}
|
||||
var lightSwitch = OnOffSwitch.off
|
||||
lightSwitch.toggle()
|
||||
// lightSwitch 现在的值为 .On
|
||||
// lightSwitch 现在的值为 .on
|
||||
```
|
||||
|
||||
<a name="initializer_requirements"></a>
|
||||
@ -234,7 +240,7 @@ protocol SomeProtocol {
|
||||
}
|
||||
```
|
||||
|
||||
### 构造器要求在类中的实现
|
||||
### 协议构造器要求的类实现
|
||||
|
||||
你可以在遵循协议的类中实现构造器,无论是作为指定构造器,还是作为便利构造器。无论哪种情况,你都必须为构造器实现标上 `required` 修饰符:
|
||||
|
||||
@ -251,6 +257,7 @@ class SomeClass: SomeProtocol {
|
||||
关于 `required` 构造器的更多内容,请参考[必要构造器](./14_Initialization.html#required_initializers)。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 如果类已经被标记为 `final`,那么不需要在协议构造器的实现中使用 `required` 修饰符,因为 `final` 类不能有子类。关于 `final` 修饰符的更多内容,请参见[防止重写](./13_Inheritance.html#preventing_overrides)。
|
||||
|
||||
如果一个子类重写了父类的指定构造器,并且该构造器满足了某个协议的要求,那么该构造器的实现需要同时标注 `required` 和 `override` 修饰符:
|
||||
@ -293,6 +300,7 @@ class SomeSubClass: SomeSuperClass, SomeProtocol {
|
||||
* 作为数组、字典或其他容器中的元素类型
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 协议是一种类型,因此协议类型的名称应与其他类型(例如 `Int`,`Double`,`String`)的写法相同,使用大写字母开头的驼峰式写法,例如(`FullyNamed` 和 `RandomNumberGenerator`)。
|
||||
|
||||
下面是将协议作为类型使用的例子:
|
||||
@ -334,7 +342,7 @@ for _ in 1...5 {
|
||||
```
|
||||
|
||||
<a name="delegation"></a>
|
||||
## 委托(代理)模式
|
||||
## 委托
|
||||
|
||||
*委托*是一种设计模式,它允许类或结构体将一些需要它们负责的功能委托给其他类型的实例。委托模式的实现很简单:定义协议来封装那些需要被委托的功能,这样就能确保遵循协议的类型能提供这些功能。委托模式可以用来响应特定的动作,或者接收外部数据源提供的数据,而无需关心外部数据源的类型。
|
||||
|
||||
@ -389,7 +397,7 @@ class SnakesAndLadders: DiceGame {
|
||||
}
|
||||
```
|
||||
|
||||
关于这个蛇梯棋游戏的详细描述请参阅 [控制流](./05_Control_Flow.html) 章节中的 [Break](./05_Control_Flow.html#break) 部分。
|
||||
关于这个*蛇梯棋*游戏的详细描述请参阅 [中断(Break)](./05_Control_Flow.html#break)。
|
||||
|
||||
这个版本的游戏封装到了 `SnakesAndLadders` 类中,该类遵循了 `DiceGame` 协议,并且提供了相应的可读的 `dice` 属性和 `play()` 方法。( `dice` 属性在构造之后就不再改变,且协议只要求 `dice` 为可读的,因此将 `dice` 声明为常量属性。)
|
||||
|
||||
@ -446,11 +454,12 @@ game.play()
|
||||
```
|
||||
|
||||
<a name="adding_protocol_conformance_with_an_extension"></a>
|
||||
## 通过扩展添加协议一致性
|
||||
## 在扩展里添加协议遵循
|
||||
|
||||
即便无法修改源代码,依然可以通过扩展令已有类型遵循并符合协议。扩展可以为已有类型添加属性、方法、下标以及构造器,因此可以符合协议中的相应要求。详情请在[扩展](./21_Extensions.html)章节中查看。
|
||||
即便无法修改源代码,依然可以通过扩展令已有类型遵循并符合协议。扩展可以为已有类型添加属性、方法、下标以及构造器,因此可以符合协议中的相应要求。详情请在[扩展](./20_Extensions.html)章节中查看。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 通过扩展令已有类型遵循并符合协议时,该类型的所有实例也会随之获得协议中定义的各项功能。
|
||||
|
||||
例如下面这个 `TextRepresentable` 协议,任何想要通过文本表示一些内容的类型都可以实现该协议。这些想要表示的内容可以是实例本身的描述,也可以是实例当前状态的文本描述:
|
||||
@ -493,10 +502,29 @@ print(game.textualDescription)
|
||||
// 打印 “A game of Snakes and Ladders with 25 squares”
|
||||
```
|
||||
|
||||
<a name="declaring_protocol_adoption_with_an_extension"></a>
|
||||
## 通过扩展遵循协议
|
||||
<a name="Conditionally_Conforming_to_a_Protocol"></a>
|
||||
## 有条件地遵循协议
|
||||
|
||||
当一个类型已经符合了某个协议中的所有要求,却还没有声明遵循该协议时,可以通过空扩展体的扩展来遵循该协议:
|
||||
泛型类型可能只在某些情况下满足一个协议的要求,比如当类型的泛型形式参数遵循对应协议时。你可以通过在扩展类型时列出限制让泛型类型有条件地遵循某协议。在你采纳协议的名字后面写泛型 `where`分句。更多关于泛型 `where` 分句,见[泛型Where分句](./22_Generics.html##where_clauses)。
|
||||
|
||||
下面的扩展让 `Array` 类型只要在存储遵循 `TextRepresentable`协议的元素时就遵循 `TextRepresentable` 协议。
|
||||
|
||||
```swift
|
||||
extension Array: TextRepresentable where Element: TextRepresentable {
|
||||
var textualDescription: String {
|
||||
let itemsAsText = self.map { $0.textualDescription }
|
||||
return "[" + itemsAsText.joined(separator: ", ") + "]"
|
||||
}
|
||||
}
|
||||
let myDice = [d6, d12]
|
||||
print(myDice.textualDescription)
|
||||
// 打印 "[A 6-sided dice, A 12-sided dice]"
|
||||
```
|
||||
|
||||
<a name="declaring_protocol_adoption_with_an_extension"></a>
|
||||
## 在扩展里声明采纳协议
|
||||
|
||||
当一个类型已经符合了某个协议中的所有要求,却还没有声明采纳该协议时,可以通过空扩展体的扩展采纳该协议:
|
||||
|
||||
```swift
|
||||
struct Hamster {
|
||||
@ -518,12 +546,13 @@ print(somethingTextRepresentable.textualDescription)
|
||||
```
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 即使满足了协议的所有要求,类型也不会自动遵循协议,必须显式地遵循协议。
|
||||
|
||||
<a name="collections_of_protocol_types"></a>
|
||||
## 协议类型的集合
|
||||
|
||||
协议类型可以在数组或者字典这样的集合中使用,在[协议类型](./22_Protocols.html##protocols_as_types)提到了这样的用法。下面的例子创建了一个元素类型为 `TextRepresentable` 的数组:
|
||||
协议类型可以在数组或者字典这样的集合中使用,在[协议类型](./21_Protocols.html##protocols_as_types)提到了这样的用法。下面的例子创建了一个元素类型为 `TextRepresentable` 的数组:
|
||||
|
||||
```swift
|
||||
let things: [TextRepresentable] = [game, d12, simonTheHamster]
|
||||
@ -599,25 +628,28 @@ print(game.prettyTextualDescription)
|
||||
```
|
||||
|
||||
<a name="class_only_protocol"></a>
|
||||
## 类类型专属协议
|
||||
## 类专属的协议
|
||||
|
||||
你可以在协议的继承列表中,通过添加 `class` 关键字来限制协议只能被类类型遵循,而结构体或枚举不能遵循该协议。`class` 关键字必须第一个出现在协议的继承列表中,在其他继承的协议之前:
|
||||
你通过添加 `AnyObject` 关键字到协议的继承列表,就可以限制协议只能被类类型采纳(以及非结构体或者非枚举的类型)。
|
||||
|
||||
```swift
|
||||
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
|
||||
// 这里是类类型专属协议的定义部分
|
||||
// 这里是类专属协议的定义部分
|
||||
}
|
||||
```
|
||||
|
||||
在以上例子中,协议 `SomeClassOnlyProtocol` 只能被类类型遵循。如果尝试让结构体或枚举类型遵循该协议,则会导致编译错误。
|
||||
在以上例子中,协议 `SomeClassOnlyProtocol` 只能被类类型采纳。如果尝试让结构体或枚举类型采纳`SomeClassOnlyProtocol`,则会导致编译时错误。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 当协议定义的要求需要遵循协议的类型必须是引用语义而非值语义时,应该采用类类型专属协议。关于引用语义和值语义的更多内容,请查看[结构体和枚举是值类型](./09_Classes_and_Structures.html#structures_and_enumerations_are_value_types)和[类是引用类型](./09_Classes_and_Structures.html#classes_are_reference_types)。
|
||||
|
||||
<a name="protocol_composition"></a>
|
||||
## 协议合成
|
||||
|
||||
有时候需要同时遵循多个协议,你可以将多个协议采用 `SomeProtocol & AnotherProtocol` 这样的格式进行组合,称为 *协议合成(protocol composition)*。你可以罗列任意多个你想要遵循的协议,以与符号(`&`)分隔。
|
||||
要求一个类型同时遵循多个协议是很有用的。你可以使用*协议组合*来复合多个协议到一个要求里。协议组合行为就和你定义的临时局部协议一样拥有构成中所有协议的需求。协议组合不定义任何新的协议类型。
|
||||
|
||||
协议组合使用 `SomeProtocol & AnotherProtocol` 的形式。你可以列举任意数量的协议,用和符号(`&`)分开。除了协议列表,协议组合也能包含类类型,这允许你标明一个需要的父类。
|
||||
|
||||
下面的例子中,将 `Named` 和 `Aged` 两个协议按照上述语法组合成一个协议,作为函数参数的类型:
|
||||
|
||||
@ -640,9 +672,9 @@ wishHappyBirthday(to: birthdayPerson)
|
||||
// 打印 “Happy birthday Malcolm - you're 21!”
|
||||
```
|
||||
|
||||
`Named` 协议包含 `String` 类型的 `name` 属性。`Aged` 协议包含 `Int` 类型的 `age` 属性。`Person` 结构体遵循了这两个协议。
|
||||
`Named` 协议包含 `String` 类型的 `name` 属性。`Aged` 协议包含 `Int` 类型的 `age` 属性。`Person` 结构体采纳了这两个协议。
|
||||
|
||||
`wishHappyBirthday(to:)` 函数的参数 `celebrator` 的类型为 `Named & Aged`。这意味着它不关心参数的具体类型,只要参数符合这两个协议即可。
|
||||
`wishHappyBirthday(to:)` 函数的参数 `celebrator` 的类型为 `Named & Aged`, 这意味着“任何同时遵循 Named 和 Aged 的协议”。它不关心参数的具体类型,只要参数符合这两个协议即可。
|
||||
|
||||
上面的例子创建了一个名为 `birthdayPerson` 的 `Person` 的实例,作为参数传递给了 `wishHappyBirthday(to:)` 函数。因为 `Person` 同时符合这两个协议,所以这个参数合法,函数将打印生日问候语。
|
||||
|
||||
@ -680,7 +712,7 @@ beginConcert(in: seattle)
|
||||
<a name="checking_for_protocol_conformance"></a>
|
||||
## 检查协议一致性
|
||||
|
||||
你可以使用[类型转换](./20_Type_Casting.html)中描述的 `is` 和 `as` 操作符来检查协议一致性,即是否符合某协议,并且可以转换到指定的协议类型。检查和转换到某个协议类型在语法上和类型的检查和转换完全相同:
|
||||
你可以使用[类型转换](./18_Type_Casting.html)中描述的 `is` 和 `as` 操作符来检查协议一致性,即是否符合某协议,并且可以转换到指定的协议类型。检查和转换到某个协议类型在语法上和类型的检查和转换完全相同:
|
||||
|
||||
* `is` 用来检查实例是否符合某个协议,若符合则返回 `true`,否则返回 `false`。
|
||||
* `as?` 返回一个可选值,当实例符合某个协议时,返回类型为协议类型的可选值,否则返回 `nil`。
|
||||
@ -758,7 +790,7 @@ for object in objects {
|
||||
|
||||
使用可选要求时(例如,可选的方法或者属性),它们的类型会自动变成可选的。比如,一个类型为 `(Int) -> String` 的方法会变成 `((Int) -> String)?`。需要注意的是整个函数类型是可选的,而不是函数的返回值。
|
||||
|
||||
协议中的可选要求可通过可选链式调用来使用,因为遵循协议的类型可能没有实现这些可选要求。类似 `someOptionalMethod?(someArgument)` 这样,你可以在可选方法名称后加上 `?` 来调用可选方法。详细内容可在[可选链式调用](./17_Optional_Chaining.html)章节中查看。
|
||||
协议中的可选要求可通过可选链式调用来使用,因为遵循协议的类型可能没有实现这些可选要求。类似 `someOptionalMethod?(someArgument)` 这样,你可以在可选方法名称后加上 `?` 来调用可选方法。详细内容可在[可选链式调用](./16_Optional_Chaining.html)章节中查看。
|
||||
|
||||
下面的例子定义了一个名为 `Counter` 的用于整数计数的类,它使用外部的数据源来提供每次的增量。数据源由 `CounterDataSource` 协议定义,包含两个可选要求:
|
||||
|
||||
@ -796,7 +828,7 @@ class Counter {
|
||||
|
||||
这里使用了两层可选链式调用。首先,由于 `dataSource` 可能为 `nil`,因此在 `dataSource` 后边加上了 `?`,以此表明只在 `dataSource` 非空时才去调用 `increment(forCount:)` 方法。其次,即使 `dataSource` 存在,也无法保证其是否实现了 `increment(forCount:)` 方法,因为这个方法是可选的。因此,`increment(forCount:)` 方法同样使用可选链式调用进行调用,只有在该方法被实现的情况下才能调用它,所以在 `increment(forCount:)` 方法后边也加上了 `?`。
|
||||
|
||||
调用 `increment(forCount:)` 方法在上述两种情形下都有可能失败,所以返回值为 `Int?` 类型。虽然在 `CounterDataSource` 协议中,`increment(forCount:)` 的返回值类型是非可选 `Int`。另外,即使这里使用了两层可选链式调用,最后的返回结果依旧是单层的可选类型。关于这一点的更多信息,请查阅[连接多层可选链式调用](./17_Optional_Chaining)
|
||||
调用 `increment(forCount:)` 方法在上述两种情形下都有可能失败,所以返回值为 `Int?` 类型。虽然在 `CounterDataSource` 协议中,`increment(forCount:)` 的返回值类型是非可选 `Int`。另外,即使这里使用了两层可选链式调用,最后的返回结果依旧是单层的可选类型。关于这一点的更多信息,请查阅[连接多层可选链式调用](./16_Optional_Chaining)
|
||||
|
||||
在调用 `increment(forCount:)` 方法后,`Int?` 型的返回值通过可选绑定解包并赋值给常量 `amount`。如果可选值确实包含一个数值,也就是说,数据源和方法都存在,数据源方法返回了一个有效值。之后便将解包后的 `amount` 加到 `count` 上,增量操作完成。
|
||||
|
||||
@ -892,6 +924,7 @@ print("And here's a random Boolean: \(generator.randomBool())")
|
||||
可以通过协议扩展来为协议要求的属性、方法以及下标提供默认的实现。如果遵循协议的类型为这些要求提供了自己的实现,那么这些自定义实现将会替代扩展中的默认实现被使用。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 通过协议扩展为协议要求提供的默认实现和可选的协议要求不同。虽然在这两种情况下,遵循协议的类型都无需自己实现这些要求,但是通过扩展提供的默认实现可以直接调用,而无需使用可选链式调用。
|
||||
|
||||
例如,`PrettyTextRepresentable` 协议继承自 `TextRepresentable` 协议,可以为其提供一个默认的 `prettyTextualDescription` 属性,只是简单地返回 `textualDescription` 属性的值:
|
||||
@ -907,36 +940,42 @@ extension PrettyTextRepresentable {
|
||||
<a name="adding_constraints_to_protocol_extensions"></a>
|
||||
### 为协议扩展添加限制条件
|
||||
|
||||
在扩展协议的时候,可以指定一些限制条件,只有遵循协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如[Where子句](./23_Generics.html#where_clauses)中所描述的。
|
||||
在扩展协议的时候,可以指定一些限制条件,只有遵循协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如[泛型Where子句](./22_Generics.html#where_clauses)中所描述的。
|
||||
|
||||
例如,你可以扩展 `CollectionType` 协议,但是只适用于集合中的元素遵循了 `TextRepresentable` 协议的情况:
|
||||
例如,你可以扩展 `Collection` 协议,适用于集合中的元素遵循了 `Equatable` 协议的情况。通过限制集合元素遵 `Equatable` 协议, 作为标准库的一部分, 你可以使用`==`和`!=`操作符来检查两个元素的等价性和非等价性。
|
||||
|
||||
```swift
|
||||
extension Collection where Iterator.Element: TextRepresentable {
|
||||
var textualDescription: String {
|
||||
let itemsAsText = self.map { $0.textualDescription }
|
||||
return "[" + itemsAsText.joined(separator: ", ") + "]"
|
||||
}
|
||||
extension Collection where Element: Equatable {
|
||||
func allEqual() -> Bool {
|
||||
for element in self {
|
||||
if element != self.first {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`textualDescription` 属性返回整个集合的文本描述,它将集合中的每个元素的文本描述以逗号分隔的方式连接起来,包在一对方括号中。
|
||||
如果集合中的所有元素都一致,`allEqual()` 方法才返回 `true`。
|
||||
|
||||
现在我们来看看先前的 `Hamster` 结构体,它符合 `TextRepresentable` 协议,同时这里还有个装有 `Hamster` 的实例的数组:
|
||||
|
||||
看看两个整数数组,一个数组的所有元素都是一样的,另一个不一样:
|
||||
|
||||
```swift
|
||||
let murrayTheHamster = Hamster(name: "Murray")
|
||||
let morganTheHamster = Hamster(name: "Morgan")
|
||||
let mauriceTheHamster = Hamster(name: "Maurice")
|
||||
let hamsters = [murrayTheHamster, morganTheHamster, mauriceTheHamster]
|
||||
let equalNumbers = [100, 100, 100, 100, 100]
|
||||
let differentNumbers = [100, 100, 200, 100, 200]
|
||||
```
|
||||
由于数组遵循`Collection`而且整数遵循`Equatable`, `equalNumbers` 和 `differentNumbers` 都可以使用 `allEqual()` 方法。
|
||||
|
||||
因为 `Array` 符合 `CollectionType` 协议,而数组中的元素又符合 `TextRepresentable` 协议,所以数组可以使用 `textualDescription` 属性得到数组内容的文本表示:
|
||||
|
||||
```swift
|
||||
print(hamsters.textualDescription)
|
||||
// 打印 “[A hamster named Murray, A hamster named Morgan, A hamster named Maurice]”
|
||||
print(equalNumbers.allEqual())
|
||||
// 打印 "true"
|
||||
print(differentNumbers.allEqual())
|
||||
// 打印 "false"
|
||||
```
|
||||
|
||||
> 注意
|
||||
> 如果多个协议扩展都为同一个协议要求提供了默认实现,而遵循协议的类型又同时满足这些协议扩展的限制条件,那么将会使用限制条件最多的那个协议扩展提供的默认实现。
|
||||
>
|
||||
> 如果一个遵循的类型满足了为同一方法或属性提供实现的多个限制型扩展的要求, Swift 使用这个实现方法去匹配那个最特殊的限制。
|
||||
@ -1,6 +1,5 @@
|
||||
# 泛型
|
||||
|
||||
------
|
||||
-------------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[takalard](https://github.com/takalard)
|
||||
@ -22,6 +21,9 @@
|
||||
> 4.0
|
||||
> 翻译+校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [泛型所解决的问题](#the_problem_that_generics_solve)
|
||||
@ -87,7 +89,8 @@ func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
|
||||
在实际应用中,通常需要一个更实用更灵活的函数来交换两个任意类型的值,幸运的是,泛型代码帮你解决了这种问题。(这些函数的泛型版本已经在下面定义好了。)
|
||||
|
||||
> 注意
|
||||
在上面三个函数中,`a` 和 `b` 类型必须相同。如果 `a` 和 `b` 类型不同,那它们俩就不能互换值。Swift 是类型安全的语言,所以它不允许一个 `String` 类型的变量和一个 `Double` 类型的变量互换值。试图这样做将导致编译错误。
|
||||
>
|
||||
> 在上面三个函数中,`a` 和 `b` 类型必须相同。如果 `a` 和 `b` 类型不同,那它们俩就不能互换值。Swift 是类型安全的语言,所以它不允许一个 `String` 类型的变量和一个 `Double` 类型的变量互换值。试图这样做将导致编译错误。
|
||||
|
||||
<a name="generic_functions"></a>
|
||||
## 泛型函数
|
||||
@ -131,7 +134,8 @@ swapTwoValues(&someString, &anotherString)
|
||||
```
|
||||
|
||||
> 注意
|
||||
上面定义的 `swapTwoValues(_:_:)` 函数是受 `swap(_:_:)` 函数启发而实现的。后者存在于 Swift 标准库,你可以在你的应用程序中使用它。如果你在代码中需要类似 `swapTwoValues(_:_:)` 函数的功能,你可以使用已存在的 `swap(_:_:)` 函数。
|
||||
>
|
||||
> 上面定义的 `swapTwoValues(_:_:)` 函数是受 `swap(_:_:)` 函数启发而实现的。后者存在于 Swift 标准库,你可以在你的应用程序中使用它。如果你在代码中需要类似 `swapTwoValues(_:_:)` 函数的功能,你可以使用已存在的 `swap(_:_:)` 函数。
|
||||
|
||||
<a name="type_parameters"></a>
|
||||
## 类型参数
|
||||
@ -148,17 +152,19 @@ swapTwoValues(&someString, &anotherString)
|
||||
在大多数情况下,类型参数具有一个描述性名字,例如 `Dictionary<Key, Value>` 中的 `Key` 和 `Value`,以及 `Array<Element>` 中的 `Element`,这可以告诉阅读代码的人这些类型参数和泛型函数之间的关系。然而,当它们之间没有有意义的关系时,通常使用单个字母来命名,例如 `T`、`U`、`V`,正如上面演示的 `swapTwoValues(_:_:)` 函数中的 `T` 一样。
|
||||
|
||||
> 注意
|
||||
请始终使用大写字母开头的驼峰命名法(例如 `T` 和 `MyTypeParameter`)来为类型参数命名,以表明它们是占位类型,而不是一个值。
|
||||
>
|
||||
> 请始终使用大写字母开头的驼峰命名法(例如 `T` 和 `MyTypeParameter`)来为类型参数命名,以表明它们是占位类型,而不是一个值。
|
||||
|
||||
<a name="generic_types"></a>
|
||||
## 泛型类型
|
||||
|
||||
除了泛型函数,Swift 还允许你定义泛型类型。这些自定义类、结构体和枚举可以适用于任何类型,类似于 `Array` 和 `Dictionary`。
|
||||
除了泛型函数,Swift 还允许你定义*泛型*类型。这些自定义类、结构体和枚举可以适用于*任何*类型,类似于 `Array` 和 `Dictionary`。
|
||||
|
||||
这部分内容将向你展示如何编写一个名为 `Stack` (栈)的泛型集合类型。栈是一系列值的有序集合,和 `Array` 类似,但它相比 Swift 的 `Array` 类型有更多的操作限制。数组允许在数组的任意位置插入新元素或是删除其中任意位置的元素。而栈只允许在集合的末端添加新的元素(称之为入*栈*)。类似的,栈也只能从末端移除元素(称之为*出*栈)。
|
||||
这部分内容将向你展示如何编写一个名为 `Stack` (栈)的泛型集合类型。栈是一系列值的有序集合,和 `Array` 类似,但它相比 Swift 的 `Array` 类型有更多的操作限制。数组允许在数组的任意位置插入新元素或是删除其中任意位置的元素。而栈只允许在集合的末端添加新的元素(称之为*入*栈)。类似的,栈也只能从末端移除元素(称之为*出*栈)。
|
||||
|
||||
> 注意
|
||||
栈的概念已被 `UINavigationController` 类用来构造视图控制器的导航结构。你通过调用 `UINavigationController` 的 `pushViewController(_:animated:)` 方法来添加新的视图控制器到导航栈,通过 `popViewControllerAnimated(_:)` 方法来从导航栈中移除视图控制器。每当你需要一个严格的“后进先出”方式来管理集合,栈都是最实用的模型。
|
||||
>
|
||||
> 栈的概念已被 `UINavigationController` 类用来构造视图控制器的导航结构。你通过调用 `UINavigationController` 的 `pushViewController(_:animated:)` 方法来添加新的视图控制器到导航栈,通过 `popViewControllerAnimated(_:)` 方法来从导航栈中移除视图控制器。每当你需要一个严格的“后进先出”方式来管理集合,栈都是最实用的模型。
|
||||
|
||||
下图展示了一个栈的入栈(push)和出栈(pop)的行为:
|
||||
|
||||
@ -241,7 +247,7 @@ let fromTheTop = stackOfStrings.pop()
|
||||
<a name="extending_a_generic_type"></a>
|
||||
## 扩展一个泛型类型
|
||||
|
||||
当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。原始类型定义中声明的类型参数列表在扩展中可以直接使用,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。
|
||||
当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。*原始*类型定义中声明的类型参数列表在扩展中可以直接使用,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。
|
||||
|
||||
下面的例子扩展了泛型类型 `Stack`,为其添加了一个名为 `topItem` 的只读计算型属性,它将会返回当前栈顶端的元素而不会将其从栈中移除:
|
||||
|
||||
@ -454,9 +460,9 @@ struct Stack<Element>: Container {
|
||||
<a name="extending_an_existing_type_to_specify_an_associated_type"></a>
|
||||
### 通过扩展一个存在的类型来指定关联类型
|
||||
|
||||
[通过扩展添加协议一致性](./22_Protocols.html#adding_protocol_conformance_with_an_extension)中描述了如何利用扩展让一个已存在的类型符合一个协议,这包括使用了关联类型的协议。
|
||||
[通过扩展添加协议一致性](./21_Protocols.html#adding_protocol_conformance_with_an_extension)中描述了如何利用扩展让一个已存在的类型符合一个协议,这包括使用了关联类型的协议。
|
||||
|
||||
Swift 的 `Array` 类型已经提供 `append(_:)` 方法,一个 `count` 属性,以及一个接受 `Int` 类型索引值的下标用以检索其元素。这三个功能都符合 `Container` 协议的要求,也就意味着你只需简单地声明 `Array` 采纳该协议就可以扩展 `Array`,使其遵从 `Container` 协议。你可以通过一个空扩展来实现这点,正如[通过扩展采纳协议](./22_Protocols.html#declaring_protocol_adoption_with_an_extension)中的描述:
|
||||
Swift 的 `Array` 类型已经提供 `append(_:)` 方法,一个 `count` 属性,以及一个接受 `Int` 类型索引值的下标用以检索其元素。这三个功能都符合 `Container` 协议的要求,也就意味着你只需简单地声明 `Array` 采纳该协议就可以扩展 `Array`,使其遵从 `Container` 协议。你可以通过一个空扩展来实现这点,正如[通过扩展采纳协议](./21_Protocols.html#declaring_protocol_adoption_with_an_extension)中的描述:
|
||||
|
||||
```swift
|
||||
extension Array: Container {}
|
||||
@ -465,7 +471,7 @@ extension Array: Container {}
|
||||
如同上面的泛型 `Stack` 结构体一样,`Array` 的 `append(_:)` 方法和下标确保了 Swift 可以推断出 `ItemType` 的类型。定义了这个扩展后,你可以将任意 `Array` 当作 `Container` 来使用。
|
||||
|
||||
<a name="using_type_annotations_to_constrain_an_associated_type"></a>
|
||||
### 约束关联类型
|
||||
### 给关联类型添加约束
|
||||
|
||||
你可以给协议里的关联类型添加类型注释,让遵守协议的类型必须遵循这个约束条件。例如,下面的代码定义了一个 `Item` 必须遵循 `Equatable` 的 `Container` 类型:
|
||||
|
||||
@ -480,8 +486,59 @@ protocol Container {
|
||||
|
||||
为了遵守了 `Container` 协议,Item 类型也必须遵守 `Equatable` 协议。
|
||||
|
||||
<a name="Using_a_Protocol_in_Its_Associated_Type’s_Constraints"></a>
|
||||
### 在关联类型约束里使用协议
|
||||
|
||||
协议可以作为它自身的要求出现。例如,有一个协议细化了 `Container` 协议,添加了一个 `suffix(_:)` 方法。 `suffix(_:)` 方法返回容器中从后往前给定数量的元素,把它们存储在一个 `Suffix` 类型的实例里。
|
||||
|
||||
```swift
|
||||
protocol SuffixableContainer: Container {
|
||||
associatedtype Suffix: SuffixableContainer where Suffix.Item == Item
|
||||
func suffix(_ size: Int) -> Suffix
|
||||
}
|
||||
```
|
||||
|
||||
在这个协议里, `Suffix` 是一个关联类型,就像上边例子中 `Container` 的 `Item` 类型一样。 `Suffix` 拥有两个约束:它必须遵循 `SuffixableContainer` 协议(就是当前定义的协议),以及它的 `Item` 类型必须是和容器里的 `Item` 类型相同。 `Item` 的约束是一个 `wher`e 分句,它在下面[带有泛型 Where 分句的扩展](#extensions_with_a_generic_where_clause)中有讨论。
|
||||
|
||||
这里有一个来自[闭包的循环强引用](./23_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)的 Stack 类型的扩展,它添加了对 `SuffixableContainer` 协议的遵循:
|
||||
|
||||
```swift
|
||||
extension Stack: SuffixableContainer {
|
||||
func suffix(_ size: Int) -> Stack {
|
||||
var result = Stack()
|
||||
for index in (count-size)..<count {
|
||||
result.append(self[index])
|
||||
}
|
||||
return result
|
||||
}
|
||||
// Inferred that Suffix is Stack.
|
||||
}
|
||||
var stackOfInts = Stack<Int>()
|
||||
stackOfInts.append(10)
|
||||
stackOfInts.append(20)
|
||||
stackOfInts.append(30)
|
||||
let suffix = stackOfInts.suffix(2)
|
||||
// suffix contains 20 and 30
|
||||
```
|
||||
|
||||
在上面的例子中, `Suffix` 是 `Stack` 的关联类型,也就是 `Stack` ,所以 `Stack` 的后缀运算返回另一个 `Stack` 。另外,遵循 `SuffixableContainer` 的类型可以拥有一个与它自己不同的 `Suffix` 类型——也就是说后缀运算可以返回不同的类型。比如说,这里有一个非泛型 `IntStack` 类型的扩展,它添加了 `SuffixableContainer` 遵循,使用 `Stack<Int>` 作为它的后缀类型而不是 `IntStack` :
|
||||
|
||||
```swift
|
||||
extension IntStack: SuffixableContainer {
|
||||
func suffix(_ size: Int) -> Stack<Int> {
|
||||
var result = Stack<Int>()
|
||||
for index in (count-size)..<count {
|
||||
result.append(self[index])
|
||||
}
|
||||
return result
|
||||
}
|
||||
// Inferred that Suffix is Stack<Int>.
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
<a name="where_clauses"></a>
|
||||
## 泛型 where 语句
|
||||
## 泛型 Where 语句
|
||||
|
||||
[类型约束](#type_constraints)让你能够为泛型函数,下标,类型的类型参数定义一些强制要求。
|
||||
|
||||
@ -562,7 +619,7 @@ if allItemsMatch(stackOfStrings, arrayOfStrings) {
|
||||
上面的例子创建了一个 `Stack` 实例来存储一些 `String` 值,然后将三个字符串压入栈中。这个例子还通过数组字面量创建了一个 `Array` 实例,数组中包含同栈中一样的三个字符串。即使栈和数组是不同的类型,但它们都遵从 `Container` 协议,而且它们都包含相同类型的值。因此你可以用这两个容器作为参数来调用 `allItemsMatch(_:_:)` 函数。在上面的例子中,`allItemsMatch(_:_:)` 函数正确地显示了这两个容器中的所有元素都是相互匹配的。
|
||||
|
||||
<a name="extensions_with_a_generic_where_clause"></a>
|
||||
## 具有泛型 where 子句的扩展
|
||||
## 具有泛型 Where 子句的扩展
|
||||
|
||||
你也可以使用泛型 `where` 子句作为扩展的一部分。基于以前的例子,下面的示例扩展了泛型 `Stack` 结构体,添加一个 `isTop(_:)` 方法。
|
||||
|
||||
@ -642,7 +699,7 @@ print([1260.0, 1200.0, 98.6, 37.0].average())
|
||||
就像可以在其他地方写泛型 `where` 子句一样,你可以在一个泛型 `where` 子句中包含多个条件作为扩展的一部分。用逗号分隔列表中的每个条件。
|
||||
|
||||
<a name="associated_types_with_a_generic_where_clause"></a>
|
||||
## 具有泛型 where 子句的关联类型
|
||||
## 具有泛型 Where 子句的关联类型
|
||||
|
||||
你可以在关联类型后面加上具有泛型 `where` 的字句。例如,建立一个包含迭代器(Iterator)的容器,就像是标准库中使用的 `Sequence` 协议那样。你应该这么写:
|
||||
|
||||
@ -693,4 +750,4 @@ extension Container {
|
||||
- 泛型 `where` 子句要求 Sequence(Indices)的迭代器,其所有的元素都是 `Int` 类型。这样就能确保在序列(Sequence)中的索引和容器(Container)里面的索引类型是一致的。
|
||||
|
||||
综合一下,这些约束意味着,传入到 `indices` 下标,是一个整型的序列。
|
||||
(译者:原来的 `Container` 协议,`subscript` 必须是 `Int` 型的,扩展中新的 `subscript`,允许下标是一个的序列,而非单一的值。)
|
||||
|
||||
@ -14,7 +14,12 @@
|
||||
>
|
||||
> 2.2
|
||||
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-14
|
||||
> 3.0.1,shanks,2016-11-13
|
||||
|
||||
> 3.0.1
|
||||
> shanks,2016-11-13
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -30,7 +35,8 @@ Swift 使用*自动引用计数(ARC)*机制来跟踪和管理你的应用程
|
||||
然而在少数情况下,为了能帮助你管理内存,ARC 需要更多的,代码之间关系的信息。本章描述了这些情况,并且为你示范怎样才能使 ARC 来管理你的应用程序的所有内存。在 Swift 使用 ARC 与在 Obejctive-C 中使用 ARC 非常类似,具体请参考[过渡到 ARC 的发布说明](https://developer.apple.com/library/content/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226)
|
||||
|
||||
> 注意
|
||||
引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。
|
||||
>
|
||||
> 引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。
|
||||
|
||||
<a name="how_arc_works"></a>
|
||||
## 自动引用计数的工作机制
|
||||
@ -203,6 +209,7 @@ Swift 提供了两种办法用来解决你在使用类的属性时所遇到的
|
||||
你可以像其他可选值一样,检查弱引用的值是否存在,你将永远不会访问已销毁的实例的引用。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 当 ARC 设置弱引用为`nil`时,属性观察不会被触发。
|
||||
|
||||
下面的例子跟上面`Person`和`Apartment`的例子一致,但是有一个重要的区别。这一次,`Apartment`的`tenant`属性被声明为弱引用:
|
||||
@ -275,7 +282,9 @@ unit4A = nil
|
||||
无主引用通常都被期望拥有值。不过 ARC 无法在实例被销毁后将无主引用设为`nil`,因为非可选类型的变量不允许被赋值为`nil`。
|
||||
|
||||
> 重要
|
||||
>
|
||||
> 使用无主引用,你*必须*确保引用始终指向一个未销毁的实例。
|
||||
>
|
||||
> 如果你试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误。
|
||||
|
||||
下面的例子定义了两个类,`Customer`和`CreditCard`,模拟了银行客户和客户的信用卡。这两个类中,每一个都将另外一个类的实例作为自身的属性。这种关系可能会造成循环强引用。
|
||||
@ -295,9 +304,7 @@ class Customer {
|
||||
}
|
||||
deinit { print("\(name) is being deinitialized") }
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
class CreditCard {
|
||||
let number: UInt64
|
||||
unowned let customer: Customer
|
||||
@ -310,6 +317,7 @@ class CreditCard {
|
||||
```
|
||||
|
||||
> 注意
|
||||
>
|
||||
> `CreditCard`类的`number`属性被定义为`UInt64`类型而不是`Int`类型,以确保`number`属性的存储量在 32 位和 64 位系统上都能足够容纳 16 位的卡号。
|
||||
|
||||
下面的代码片段定义了一个叫`john`的可选类型`Customer`变量,用来保存某个特定客户的引用。由于是可选类型,所以变量被初始化为`nil`:
|
||||
@ -346,11 +354,13 @@ john = nil
|
||||
最后的代码展示了在`john`变量被设为`nil`后`Customer`实例和`CreditCard`实例的构造函数都打印出了“销毁”的信息。
|
||||
|
||||
> 注意
|
||||
>
|
||||
>上面的例子展示了如何使用安全的无主引用。对于需要禁用运行时的安全检查的情况(例如,出于性能方面的原因),Swift还提供了不安全的无主引用。与所有不安全的操作一样,你需要负责检查代码以确保其安全性。
|
||||
>
|
||||
>你可以通过`unowned(unsafe)`来声明不安全无主引用。如果你试图在实例被销毁后,访问该实例的不安全无主引用,你的程序会尝试访问该实例之前所在的内存地址,这是一个不安全的操作。
|
||||
|
||||
<a name="unowned_references_and_implicitly_unwrapped_optional_properties"></a>
|
||||
### 无主引用以及隐式解析可选属性
|
||||
### 无主引用和隐式解析可选属性
|
||||
|
||||
上面弱引用和无主引用的例子涵盖了两种常用的需要打破循环强引用的场景。
|
||||
|
||||
@ -403,7 +413,7 @@ print("\(country.name)'s capital city is called \(country.capitalCity.name)")
|
||||
在上面的例子中,使用隐式解析可选值意味着满足了类的构造函数的两个构造阶段的要求。`capitalCity`属性在初始化完成后,能像非可选值一样使用和存取,同时还避免了循环强引用。
|
||||
|
||||
<a name="strong_reference_cycles_for_closures"></a>
|
||||
## 闭包引起的循环强引用
|
||||
## 闭包的循环强引用
|
||||
|
||||
前面我们看到了循环强引用是在两个类实例属性互相保持对方的强引用时产生的,还知道了如何用弱引用和无主引用来打破这些循环强引用。
|
||||
|
||||
@ -462,7 +472,8 @@ print(heading.asHTML())
|
||||
```
|
||||
|
||||
> 注意
|
||||
`asHTML`声明为`lazy`属性,因为只有当元素确实需要被处理为 HTML 输出的字符串时,才需要使用`asHTML`。也就是说,在默认的闭包中可以使用`self`,因为只有当初始化完成以及`self`确实存在后,才能访问`lazy`属性。
|
||||
>
|
||||
> `asHTML`声明为`lazy`属性,因为只有当元素确实需要被处理为 HTML 输出的字符串时,才需要使用`asHTML`。也就是说,在默认的闭包中可以使用`self`,因为只有当初始化完成以及`self`确实存在后,才能访问`lazy`属性。
|
||||
|
||||
`HTMLElement`类只提供了一个构造函数,通过`name`和`text`(如果有的话)参数来初始化一个新元素。该类也定义了一个析构函数,当`HTMLElement`实例被销毁时,打印一条消息。
|
||||
|
||||
@ -475,7 +486,8 @@ print(paragraph!.asHTML())
|
||||
```
|
||||
|
||||
> 注意
|
||||
上面的`paragraph`变量定义为可选类型的`HTMLElement`,因此我们可以赋值`nil`给它来演示循环强引用。
|
||||
>
|
||||
> 上面的`paragraph`变量定义为可选类型的`HTMLElement`,因此我们可以赋值`nil`给它来演示循环强引用。
|
||||
|
||||
不幸的是,上面写的`HTMLElement`类产生了类实例和作为`asHTML`默认值的闭包之间的循环强引用。循环强引用如下图所示:
|
||||
|
||||
@ -484,7 +496,8 @@ print(paragraph!.asHTML())
|
||||
实例的`asHTML`属性持有闭包的强引用。但是,闭包在其闭包体内使用了`self`(引用了`self.name`和`self.text`),因此闭包捕获了`self`,这意味着闭包又反过来持有了`HTMLElement`实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考[值捕获](./07_Closures.html#capturing_values))。
|
||||
|
||||
> 注意
|
||||
虽然闭包多次使用了`self`,它只捕获`HTMLElement`实例的一个强引用。
|
||||
>
|
||||
> 虽然闭包多次使用了`self`,它只捕获`HTMLElement`实例的一个强引用。
|
||||
|
||||
如果设置`paragraph`变量为`nil`,打破它持有的`HTMLElement`实例的强引用,`HTMLElement`实例和它的闭包都不会被销毁,也是因为循环强引用:
|
||||
|
||||
@ -495,12 +508,13 @@ paragraph = nil
|
||||
注意,`HTMLElement`的析构函数中的消息并没有被打印,证明了`HTMLElement`实例并没有被销毁。
|
||||
|
||||
<a name="resolving_strong_reference_cycles_for_closures"></a>
|
||||
## 解决闭包引起的循环强引用
|
||||
## 解决闭包的循环强引用
|
||||
|
||||
在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环强引用。捕获列表定义了闭包体内捕获一个或者多个引用类型的规则。跟解决两个类实例间的循环强引用一样,声明每个捕获的引用为弱引用或无主引用,而不是强引用。应当根据代码关系来决定使用弱引用还是无主引用。
|
||||
|
||||
> 注意
|
||||
Swift 有如下要求:只要在闭包内使用`self`的成员,就要用`self.someProperty`或者`self.someMethod()`(而不只是`someProperty`或`someMethod()`)。这提醒你可能会一不小心就捕获了`self`。
|
||||
>
|
||||
> Swift 有如下要求:只要在闭包内使用`self`的成员,就要用`self.someProperty`或者`self.someMethod()`(而不只是`someProperty`或`someMethod()`)。这提醒你可能会一不小心就捕获了`self`。
|
||||
|
||||
<a name="defining_a_capture_list"></a>
|
||||
### 定义捕获列表
|
||||
@ -533,7 +547,8 @@ lazy var someClosure: Void -> String = {
|
||||
相反的,在被捕获的引用可能会变为`nil`时,将闭包内的捕获定义为`弱引用`。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为`nil`。这使我们可以在闭包体内检查它们是否存在。
|
||||
|
||||
> 注意
|
||||
如果被捕获的引用绝对不会变为`nil`,应该用无主引用,而不是弱引用。
|
||||
>
|
||||
> 如果被捕获的引用绝对不会变为`nil`,应该用无主引用,而不是弱引用。
|
||||
|
||||
前面的`HTMLElement`例子中,无主引用是正确的解决循环强引用的方法。这样编写`HTMLElement`类来避免循环强引用:
|
||||
|
||||
@ -1,4 +1,12 @@
|
||||
# 内存安全
|
||||
--------------------
|
||||
|
||||
> 4.0
|
||||
> 翻译:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -122,7 +130,8 @@ balance(&playerOneScore, &playerOneScore)
|
||||
上面的 `balance(_:_:)` 函数会将传入的两个参数平均化。将 `playerOneScore` 和 `playerTwoScore` 作为参数传入不会产生错误 —— 有两个访问重叠了,但它们访问的是不同的内存位置。相反,将 `playerOneScore` 作为参数同时传入就会产生冲突,因为它会发起两个写访问,同时访问同一个的存储地址。
|
||||
|
||||
> 注意
|
||||
因为操作符也是函数,它们也会对 in-out 参数进行长期访问。例如,假设 `balance(_:_:)` 是一个名为 `<^>` 的操作符函数,那么 `playerOneScore <^> playerOneScore` 也会造成像 `balance(&playerOneScore, &playerOneScore)` 一样的冲突。
|
||||
>
|
||||
> 因为操作符也是函数,它们也会对 in-out 参数进行长期访问。例如,假设 `balance(_:_:)` 是一个名为 `<^>` 的操作符函数,那么 `playerOneScore <^> playerOneScore` 也会造成像 `balance(&playerOneScore, &playerOneScore)` 一样的冲突。
|
||||
|
||||
<a name="conflicting_access_to_self_in_methods"></a>
|
||||
## 方法里 self 的访问冲突
|
||||
@ -210,8 +219,4 @@ func someFunction() {
|
||||
|
||||
如果编译器无法保证访问的安全性,它就不会允许访问。
|
||||
|
||||
<a name="revision_history"></a>
|
||||
## 更新历史
|
||||
|
||||
> 4.0
|
||||
> 翻译:[kemchenj](https://kemchenj.github.io/) 2017-09-21
|
||||
@ -21,6 +21,9 @@
|
||||
> 4.0
|
||||
> 翻译:kemchenj,2017-09-23
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页内容包括:
|
||||
|
||||
- [模块和源文件](#modules_and_source_files)
|
||||
@ -42,14 +45,15 @@
|
||||
Swift 不仅提供了多种不同的访问级别,还为某些典型场景提供了默认的访问级别,这样就不需要我们在每段代码中都申明显式访问级别。其实,如果只是开发一个单一 target 的应用程序,我们完全可以不用显式声明代码的访问级别。
|
||||
|
||||
> 注意
|
||||
为了简单起见,对于代码中可以设置访问级别的特性(属性、基本类型、函数等),在下面的章节中我们会称之为“实体”。
|
||||
>
|
||||
> 为了简单起见,对于代码中可以设置访问级别的特性(属性、基本类型、函数等),在下面的章节中我们会称之为“实体”。
|
||||
|
||||
<a name="modules_and_source_files"></a>
|
||||
## 模块和源文件
|
||||
|
||||
Swift 中的访问控制模型基于模块和源文件这两个概念。
|
||||
|
||||
模块指的是独立的代码单元,框架或应用程序会作为一个独立的模块来构建和发布。在 Swift 中,一个模块可以使用 `import` 关键字导入另外一个模块。
|
||||
*模块*指的是独立的代码单元,框架或应用程序会作为一个独立的模块来构建和发布。在 Swift 中,一个模块可以使用 `import` 关键字导入另外一个模块。
|
||||
|
||||
在 Swift 中,Xcode 的每个 target(例如框架或应用程序)都被当作独立的模块处理。如果你是为了实现某个通用的功能,或者是为了封装一些常用方法而将代码打包成独立的框架,这个框架就是 Swift 中的一个模块。当它被导入到某个应用程序或者其他框架时,框架内容都将属于这个独立的模块。
|
||||
|
||||
@ -104,7 +108,8 @@ Swift 中的访问级别遵循一个基本原则:*不可以在某个实体中
|
||||
当你开发框架时,就需要把一些对外的接口定义为 Open 或 Public,以便使用者导入该框架后可以正常使用其功能。这些被你定义为对外的接口,就是这个框架的 API。
|
||||
|
||||
> 注意
|
||||
框架依然会使用默认的 `internal` ,也可以指定为 `fileprivate` 访问或者 `private` 访问级别。当你想把某个实体作为框架的 API 的时候,需显式为其指定开放访问或公开访问级别。
|
||||
>
|
||||
> 框架依然会使用默认的 `internal` ,也可以指定为 `fileprivate` 访问或者 `private` 访问级别。当你想把某个实体作为框架的 API 的时候,需显式为其指定开放访问或公开访问级别。
|
||||
|
||||
<a name="access_levels_for_unit_test_targets"></a>
|
||||
### 单元测试 target 的访问级别
|
||||
@ -143,7 +148,8 @@ var someInternalConstant = 0 // 隐式 internal
|
||||
一个类型的访问级别也会影响到类型*成员*(属性、方法、构造器、下标)的默认访问级别。如果你将类型指定为 `private` 或者 `fileprivate` 级别,那么该类型的所有成员的默认访问级别也会变成 `private` 或者 `fileprivate` 级别。如果你将类型指定为公开或者 `internal` (或者不明确指定访问级别,而使用默认的 `internal` ),那么该类型的所有成员的默认访问级别将是内部访问。
|
||||
|
||||
> 重要
|
||||
上面提到,一个 `public` 类型的所有成员的访问级别默认为 `internal` 级别,而不是 `public` 级别。如果你想将某个成员指定为 `public` 级别,那么你必须显式指定。这样做的好处是,在你定义公共接口的时候,可以明确地选择哪些接口是需要公开的,哪些是内部使用的,避免不小心将内部使用的接口公开。
|
||||
>
|
||||
> 上面提到,一个 `public` 类型的所有成员的访问级别默认为 `internal` 级别,而不是 `public` 级别。如果你想将某个成员指定为 `public` 级别,那么你必须显式指定。这样做的好处是,在你定义公共接口的时候,可以明确地选择哪些接口是需要公开的,哪些是内部使用的,避免不小心将内部使用的接口公开。
|
||||
|
||||
```swift
|
||||
public class SomePublicClass { // 显式 public 类
|
||||
@ -175,7 +181,8 @@ private class SomePrivateClass { // 显式 private 类
|
||||
元组的访问级别将由元组中访问级别最严格的类型来决定。例如,如果你构建了一个包含两种不同类型的元组,其中一个类型为 `internal`,另一个类型为 `private`,那么这个元组的访问级别为 `private`。
|
||||
|
||||
> 注意
|
||||
元组不同于类、结构体、枚举、函数那样有单独的定义。元组的访问级别是在它被使用时自动推断出的,而无法明确指定。
|
||||
>
|
||||
> 元组不同于类、结构体、枚举、函数那样有单独的定义。元组的访问级别是在它被使用时自动推断出的,而无法明确指定。
|
||||
|
||||
<a name="function_types"></a>
|
||||
### 函数类型
|
||||
@ -282,7 +289,8 @@ private var privateInstance = SomePrivateClass()
|
||||
`Setter` 的访问级别可以低于对应的 `Getter` 的访问级别,这样就可以控制变量、属性或下标的读写权限。在 `var` 或 `subscript` 关键字之前,你可以通过 `fileprivate(set)`,`private(set)` 或 `internal(set)` 为它们的写入权限指定更低的访问级别。
|
||||
|
||||
> 注意
|
||||
这个规则同时适用于存储型属性和计算型属性。即使你不明确指定存储型属性的 `Getter` 和 `Setter`,Swift 也会隐式地为其创建 `Getter` 和 `Setter`,用于访问该属性的后备存储。使用 `fileprivate(set)`,`private(set)` 和 `internal(set)` 可以改变 `Setter` 的访问级别,这对计算型属性也同样适用。
|
||||
>
|
||||
> 这个规则同时适用于存储型属性和计算型属性。即使你不明确指定存储型属性的 `Getter` 和 `Setter`,Swift 也会隐式地为其创建 `Getter` 和 `Setter`,用于访问该属性的后备存储。使用 `fileprivate(set)`,`private(set)` 和 `internal(set)` 可以改变 `Setter` 的访问级别,这对计算型属性也同样适用。
|
||||
|
||||
下面的例子中定义了一个名为 `TrackedString` 的结构体,它记录了 `value` 属性被修改的次数:
|
||||
|
||||
@ -357,7 +365,8 @@ public struct TrackedString {
|
||||
协议中的每一个要求都具有和该协议相同的访问级别。你不能将协议中的要求设置为其他访问级别。这样才能确保该协议的所有要求对于任意采纳者都将可用。
|
||||
|
||||
> 注意
|
||||
如果你定义了一个 `public` 访问级别的协议,那么该协议的所有实现也会是 `public` 访问级别。这一点不同于其他类型,例如,当类型是 `public` 访问级别时,其成员的访问级别却只是 `internal`。
|
||||
>
|
||||
> 如果你定义了一个 `public` 访问级别的协议,那么该协议的所有实现也会是 `public` 访问级别。这一点不同于其他类型,例如,当类型是 `public` 访问级别时,其成员的访问级别却只是 `internal`。
|
||||
|
||||
<a name="protocol_inheritance"></a>
|
||||
### 协议继承
|
||||
@ -374,7 +383,8 @@ public struct TrackedString {
|
||||
如果你采纳了协议,那么实现了协议的所有要求后,你必须确保这些实现的访问级别不能低于协议的访问级别。例如,一个 `public` 级别的类型,采纳了 `internal` 级别的协议,那么协议的实现至少也得是 `internal` 级别。
|
||||
|
||||
> 注意
|
||||
Swift 和 Objective-C 一样,协议的一致性是全局的,也就是说,在同一程序中,一个类型不可能用两种不同的方式实现同一个协议。
|
||||
>
|
||||
> Swift 和 Objective-C 一样,协议的一致性是全局的,也就是说,在同一程序中,一个类型不可能用两种不同的方式实现同一个协议。
|
||||
|
||||
<a name="extensions"></a>
|
||||
## Extension
|
||||
@ -427,4 +437,5 @@ extension SomeStruct: SomeProtocol {
|
||||
你定义的任何类型别名都会被当作不同的类型,以便于进行访问控制。类型别名的访问级别不可高于其表示的类型的访问级别。例如,`private` 级别的类型别名可以作为 `private`,`file-private`,`internal`,`public`或者`open`类型的别名,但是 `public` 级别的类型别名只能作为 `public` 类型的别名,不能作为 `internal`,`file-private`,或 `private` 类型的别名。
|
||||
|
||||
> 注意
|
||||
这条规则也适用于为满足协议一致性而将类型别名用于关联类型的情况。
|
||||
>
|
||||
> 这条规则也适用于为满足协议一致性而将类型别名用于关联类型的情况。
|
||||
|
||||
@ -16,14 +16,19 @@
|
||||
>
|
||||
> 3.0
|
||||
> 翻译+校对:[mmoaay](https://github.com/mmoaay) 2016-09-20
|
||||
> 3.0.1:shanks,2016-11-13
|
||||
|
||||
> 3.0.1
|
||||
> shanks,2016-11-13
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页内容包括:
|
||||
|
||||
- [位运算符](#bitwise_operators)
|
||||
- [溢出运算符](#overflow_operators)
|
||||
- [优先级和结合性](#precedence_and_associativity)
|
||||
- [运算符函数](#operator_functions)
|
||||
- [运算符函数](#operator_methods)
|
||||
- [自定义运算符](#custom_operators)
|
||||
|
||||
除了在之前介绍过的[基本运算符](./02_Basic_Operators.html),Swift 中还有许多可以对数值进行复杂运算的高级运算符。这些高级运算符包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算符。
|
||||
@ -308,6 +313,7 @@ signedOverflow = signedOverflow &- 1
|
||||
如果想查看完整的 Swift 运算符优先级和结合性规则,请参考[表达式](../chapter3/04_Expressions.html)。如果想查看 Swift 标准库提供所有的运算符,请查看 [Swift Standard Library Operators Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 相对 C 语言和 Objective-C 来说,Swift 的运算符优先级和结合性规则更加简洁和可预测。但是,这也意味着它们相较于 C 语言及其衍生语言并不是完全一致的。在对现有的代码进行移植的时候,要注意确保运算符的行为仍然符合你的预期。
|
||||
|
||||
<a name="operator_functions"></a>
|
||||
@ -397,6 +403,7 @@ original += vectorToAdd
|
||||
```
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 不能对默认的赋值运算符(`=`)进行重载。只有组合赋值运算符可以被重载。同样地,也无法对三目条件运算符 (`a ? b : c`) 进行重载。
|
||||
|
||||
<a name="equivalence_operators"></a>
|
||||
@ -404,16 +411,13 @@ original += vectorToAdd
|
||||
|
||||
自定义的类和结构体没有对*等价运算符*进行默认实现,等价运算符通常被称为“相等”运算符(`==`)与“不等”运算符(`!=`)。对于自定义类型,Swift 无法判断其是否“相等”,因为“相等”的含义取决于这些自定义类型在你的代码中所扮演的角色。
|
||||
|
||||
为了使用等价运算符能对自定义的类型进行判等运算,需要为其提供自定义实现,实现的方法与其它中缀运算符一样:
|
||||
为了使用等价运算符能对自定义的类型进行判等运算,需要为其提供自定义实现,实现的方法与其它中缀运算符一样, 并且增加对标准库 `Equatable` 协议的遵循:
|
||||
|
||||
```swift
|
||||
extension Vector2D {
|
||||
extension Vector2D: Equatable {
|
||||
static func == (left: Vector2D, right: Vector2D) -> Bool {
|
||||
return (left.x == right.x) && (left.y == right.y)
|
||||
}
|
||||
static func != (left: Vector2D, right: Vector2D) -> Bool {
|
||||
return !(left == right)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -429,6 +433,28 @@ if twoThree == anotherTwoThree {
|
||||
}
|
||||
// 打印 “These two vectors are equivalent.”
|
||||
```
|
||||
Swift 为以下自定义类型提等价运算符供合成实现:
|
||||
|
||||
- 只拥有遵循 `Equatable` 协议存储属性的结构体;
|
||||
- 只拥有遵循 `Equatable` 协议关联类型的枚举;
|
||||
- 没有关联类型的枚举。
|
||||
|
||||
在类型原本的声明中声明遵循 `Equatable` 来接收这些默认实现。
|
||||
|
||||
下面为三维位置向量 `(x, y, z)` 定义的 `Vector3D` 结构体,与 `Vector2D` 类似,由于 `x` , `y` 和 `z` 属性都是 `Equatable` 类型, `Vector3D` 就收到默认的等价运算符实现了。
|
||||
|
||||
```swift
|
||||
struct Vector3D: Equatable {
|
||||
var x = 0.0, y = 0.0, z = 0.0
|
||||
}
|
||||
|
||||
let twoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
|
||||
let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
|
||||
if twoThreeFour == anotherTwoThreeFour {
|
||||
print("These two vectors are also equivalent.")
|
||||
}
|
||||
// Prints "These two vectors are also equivalent."
|
||||
```
|
||||
|
||||
<a name="custom_operators"></a>
|
||||
## 自定义运算符
|
||||
@ -482,4 +508,5 @@ let plusMinusVector = firstVector +- secondVector
|
||||
这个运算符把两个向量的 `x` 值相加,同时用第一个向量的 `y` 值减去第二个向量的 `y` 值。因为它本质上是属于“相加型”运算符,所以将它放置 `+` 和 `-` 等默认的中缀“相加型”运算符相同的优先级组中。关于 Swift 标准库提供的运算符,以及完整的运算符优先级组和结合性设置,请参考 [Swift Standard Library Operators Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。而更多关于优先级组以及自定义操作符和优先级组的语法,请参考[运算符声明](#operator_declaration)
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。
|
||||
@ -8,13 +8,16 @@
|
||||
> 2.0
|
||||
> 翻译+校对:[KYawn](https://github.com/KYawn)
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页内容包括:
|
||||
|
||||
- [如何阅读语法](#how_to_read_the_grammar)
|
||||
|
||||
本书的这一节描述了 Swift 编程语言的形式语法。这里描述的语法是为了帮助您更详细地了解该语言,而不是让您直接实现一个解析器或编译器。
|
||||
本书的这一节描述了 Swift 编程语言的形式语法。这里描述的语法是为了帮助您了解该语言的更多细节,而不是让您直接实现一个解析器或编译器。
|
||||
|
||||
Swift 语言相对较小,这是由于 Swift 代码中的几乎所有常见类型、函数以及运算符都已经在 Swift 标准库中定义了。虽然这些类型、函数和运算符并不是 Swift 语言自身的一部分,但是它们被广泛应用于本书的讨论和代码范例中。
|
||||
Swift 语言相对较小,这是由于 Swift 代码中在各种地方出现的常见的类型、函数以及运算符都已经在 Swift 标准库中定义了。虽然这些类型、函数和运算符并不是 Swift 语言自身的一部分,但是它们被广泛应用于本书的讨论和代码范例中。
|
||||
|
||||
<a name="how_to_read_the_grammar"></a>
|
||||
## 如何阅读语法
|
||||
@ -23,18 +26,20 @@ Swift 语言相对较小,这是由于 Swift 代码中的几乎所有常见类
|
||||
|
||||
- 箭头(`→`)用来标记语法产式,可以理解为“可由……构成”。
|
||||
- 斜体文字用来表示句法类型,并出现在一个语法产式规则两侧。
|
||||
- 关键字和标点符号由固定宽度的粗体文本表示,只出现在一个语法产式规则的右侧。
|
||||
- 标记语言和标点符号由固定宽度的粗体文本表示,只出现在一个语法产式规则的右侧。
|
||||
- 可供选择的语法产式由竖线(`|`)分隔。当可选用的语法产式太多时,为了阅读方便,它们将被拆分为多行语法产式规则。
|
||||
- 少数情况下,语法产式规则的右侧会有用于描述的常规字体文字。
|
||||
- 可选的句法类型和字面值用尾标 `opt` 来标记。
|
||||
- 少数情况下,标准字体文本被用来描述一个语法产生规则的右手侧内容。
|
||||
- 可选的句法类型和文本标记用尾标 `opt` 来标记。
|
||||
|
||||
举个例子,getter-setter 的语法块的定义如下:
|
||||
举个例子,getter-setter 方法块的语法定义如下:
|
||||
|
||||
> getter-setter 方法块语法
|
||||
>
|
||||
> *getter-setter 方法块* → { [*getter 子句*](05_Declarations.html#getter-clause) [*setter 子句*](05_Declarations.html#setter-clause)<sub>可选</sub> } | { [*setter 子句*](05_Declarations.html#setter-clause) [*getter 子句*](05_Declarations.html#getter-clause) }
|
||||
|
||||
这个定义表明,一个 getter-setter 方法块可以由一个 getter 子句后跟一个可选的 setter 子句构成,然后用大括号括起来,或者由一个 setter 子句后跟一个 getter 子句构成,然后用大括号括起来。下面的两个语法产式等价于上述的语法产式,并明确指出了如何取舍:
|
||||
这个定义表明,一个 getter-setter 方法块可以由一个 getter 分句后跟一个可选的 setter 分句构成,然后用大括号括起来,或者由一个 setter 分句后跟一个 getter 分句构成,然后用大括号括起来。上述的语法产式等价于下面的两个语法产式, :
|
||||
|
||||
> getter-setter 方法块语法
|
||||
>
|
||||
> getter-setter 方法块 → { [*getter 子句*](05_Declarations.html#getter-clause) [*setter 子句*](05_Declarations.html#setter-clause)<sub>可选</sub> }
|
||||
> getter-setter 方法块 → { [*setter 子句*](05_Declarations.html#setter-clause) [*getter 子句*](05_Declarations.html#getter-clause) }
|
||||
|
||||
@ -12,7 +12,10 @@
|
||||
> 翻译:[mmoaay](https://github.com/mmoaay)
|
||||
|
||||
> 2.2
|
||||
> 翻译+校对:[星夜暮晨](https://github.com/semperidem),2016-04-06
|
||||
> 翻译+校对:[星夜暮晨](https://github.com/semperidem)
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -25,7 +28,7 @@
|
||||
- [字符串字面量](#string_literals)
|
||||
- [运算符](#operators)
|
||||
|
||||
Swift 的*“词法结构 (lexical structure)”* 描述了能构成该语言中合法符号 (token) 的字符序列。这些合法符号组成了语言中最底层的构建基块,并在之后的章节中用于描述语言的其他部分。一个合法符号由一个标识符 (identifier)、关键字 (keyword)、标点符号 (punctuation)、字面量 (literal) 或运算符 (operator) 组成。
|
||||
Swift 的*“词法结构 (lexical structure)”* 描述了能构成该语言中有效符号 (token) 的字符序列。这些合法符号组成了语言中最底层的构建基块,并在之后的章节中用于描述语言的其他部分。一个合法符号由一个标识符 (identifier)、关键字 (keyword)、标点符号 (punctuation)、字面量 (literal) 或运算符 (operator) 组成。
|
||||
|
||||
通常情况下,通过考虑输入文本当中可能的最长子串,并且在随后将介绍的语法约束之下,根据随后将介绍的语法约束生成的,根据 Swift 源文件当中的字符来生成相应的“符号”。这种方法称为*“最长匹配 (longest match)”*,或者*“最大适合(maximal munch)”*。
|
||||
|
||||
@ -36,7 +39,31 @@ Swift 的*“词法结构 (lexical structure)”* 描述了能构成该语言中
|
||||
|
||||
注释被编译器当作空白处理。单行注释由 `//` 开始直至遇到换行符(U+000A)或者回车符(U+000D)。多行注释由 `/*` 开始,以 `*/` 结束。注释允许嵌套,但注释标记必须匹配。
|
||||
|
||||
正如 [*Markup Formatting Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Xcode/Reference/xcode_markup_formatting_ref/index.html#//apple_ref/doc/uid/TP40016497) 所述,注释可以包含附加的格式和标记。
|
||||
> 空白语法
|
||||
|
||||
<a id="whitespace"></a>
|
||||
> *空白* → [*空白项*](#whitespace-item) [*空白*](#whitespace)<sub>可选</sub>
|
||||
> *空白项* → [*断行符*](#line-break)
|
||||
> *空白项* → [*注释*](#comment)
|
||||
> *空白项* → [*多行注释*](#multiline-comment)
|
||||
> *空白项* → U+0000,U+0009,U+000B,U+000C 或者 U+0020
|
||||
|
||||
<a id="line-break"></a>
|
||||
> *断行符* → U+000A
|
||||
> *断行符* → U+000D
|
||||
> *断行符* → U+000D 接着是 U+000A
|
||||
|
||||
<a id="comment"></a>
|
||||
> *注释* → // [*注释内容 断行*](#comment-text line-break)
|
||||
> *多行注释* → `/*` [*多行注释内容*](#multiline-commnet-text) `*/`
|
||||
> *注释内容* → [*注释内容项*](#comment-text-item) [*注释内容*](#comment-text)<sub>可选</sub>
|
||||
> *注释内容项* → 任何Unicode标量值, 除了 U+000A 或者 U+000D
|
||||
> *多行注释内容* → [*多行注释内容项*](#multiline-comment-text-item) [*多行注释内容*](#multiline-comment-text)<sub>可选</sub>
|
||||
> *多行注释内容项* → [*多行注释*](#multiline-comment).
|
||||
> *多行注释内容项* → [*注释内容项*](#comment-text-item)
|
||||
> *多行注释内容项* → 任何Unicode标量值, 除了 `/*` 或者 `*/`
|
||||
|
||||
注释可以包含额外的格式和标记,正如 [*Markup Formatting Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Xcode/Reference/xcode_markup_formatting_ref/index.html#//apple_ref/doc/uid/TP40016497) 所述。
|
||||
|
||||
<a id="identifiers"></a>
|
||||
## 标识符
|
||||
@ -118,6 +145,7 @@ true // 布尔值字面量
|
||||
当为一个字面量值指定了类型标注的时候,这个标注的类型必须能通过这个字面量值实例化。也就是说,这个类型必须符合这些 Swift 标准库协议中的一个:整数字面量的 `IntegerLiteralConvertible` 协议、浮点数字面量的 `FloatingPointLiteralConvertible` 协议、字符串字面量的 `StringLiteralConvertible` 协议以及布尔值字面量的 `BooleanLiteralConvertible` 协议。比如,`Int8` 符合 `IntegerLiteralConvertible` 协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整数字面量 `42` 的类型标注。
|
||||
|
||||
> 字面量语法
|
||||
>
|
||||
> *字面量* → [*数值字面量*](#numeric-literal) | [*字符串字面量*](#string-literal) | [*布尔值字面量*](#boolean-literal) | [*nil 字面量*](#nil-literal)
|
||||
|
||||
<a id="numeric-literal"></a>
|
||||
@ -141,7 +169,7 @@ true // 布尔值字面量
|
||||
除非特别指定,整数字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数](../chapter2/01_The_Basics.html#integers)。
|
||||
|
||||
> 整数字面量语法
|
||||
|
||||
>
|
||||
<a id="integer-literal"></a>
|
||||
> *整数字面量* → [*二进制字面量*](#binary-literal)
|
||||
> *整数字面量* → [*八进制字面量*](#octal-literal)
|
||||
@ -204,7 +232,7 @@ true // 布尔值字面量
|
||||
除非特别指定,浮点数字面量的默认推导类型为 Swift 标准库类型中的 `Double`,表示 64 位浮点数。Swift 标准库也定义了 `Float` 类型,表示 32 位浮点数。
|
||||
|
||||
> 浮点数字面量语法
|
||||
|
||||
>
|
||||
<a id="floating-point-literal"></a>
|
||||
> *浮点数字面量* → [*十进制字面量*](#decimal-literal) [*十进制分数*](#decimal-fraction)<sub>可选</sub> [*十进制指数*](#decimal-exponent)<sub>可选</sub>
|
||||
> *浮点数字面量* → [*十六进制字面量*](#hexadecimal-literal) [*十六进制分数*](#hexadecimal-fraction)<sub>可选</sub> [*十六进制指数*](#hexadecimal-exponent)
|
||||
@ -229,12 +257,27 @@ true // 布尔值字面量
|
||||
<a id="string_literals"></a>
|
||||
### 字符串字面量
|
||||
|
||||
字符串字面量由被包在双引号中的一串字符组成,形式如下:
|
||||
字符串字面量是被引号包括的一串字符组成。 单行字符串字面量被包在双引号中的一串字符组成,形式如下:
|
||||
|
||||
> "`字符`"
|
||||
|
||||
字符串字面量中不能包含未转义的双引号(`"`)、未转义的反斜线(`\`)、回车符、换行符。
|
||||
|
||||
多行字符串字面量被包在三个双引号中的一串字符组成,形式如下:
|
||||
> """
|
||||
> `字符`
|
||||
> """
|
||||
|
||||
与单行字符串字面量不同的是,多行字符串字面量可以包含不转义的双引号( " ),回车以及换行。它不能包含三个非转义的连续双引号。
|
||||
|
||||
""" 之后的回车或者换行开始多行字符串字面量,不是字符串的一部分。 """ 之前回车或者换行结束字面量,也不是字符串的一部分。要让多行字符串字面量的开始或结束带有换行,就在第一行或者最后一行写一个空行。
|
||||
|
||||
多行字符串字面量可以使用任何空格或制表符组合进行缩进;这些缩进不会包含在字符串中。 """ 的结束符号决定了缩进:字面量中的任何一个非空行必须起始于多行字符串字面量结束符号的前面;空格和制表符不会被转换。你可以包在缩进后含额外的空格和制表符;这些空格和制表符会在字符串中出现。
|
||||
|
||||
多行字符串字面量中的一行结束使用规范化的换行符号。尽管你的源代码混用了回车和换行符,字符串中所有的行结束都必须一样.
|
||||
|
||||
在多行字符串字面量里, 在行末用反斜线(`\`)可以省略字符串行间中断。 反斜线之间的空白和行间中断也可以省略。 你可以在你的代码里用这种语法硬包裹多行字符串字面量,不需要改变产生的字符串的值。
|
||||
|
||||
可以在字符串字面量中使用的转义特殊符号如下:
|
||||
|
||||
* 空字符 `\0`
|
||||
@ -246,7 +289,7 @@ true // 布尔值字面量
|
||||
* 单引号 `\'`
|
||||
* Unicode 标量 `\u{`n`}`,n 为一到八位的十六进制数字
|
||||
|
||||
字符串字面量允许在反斜杠 (`\`) 后的括号 `()` 中插入表达式的值。插入表达式可以包含字符串字面量,但不能包含未转义的双引号 (`"`)、未转义的反斜线 (`\`)、回车符以及换行符。
|
||||
字符串字面量允许在反斜杠 (`\`) 后的括号 `()` 中插入表达式的值。插入表达式可以包含字符串字面量,但不能包含未转义的反斜线 (`\`)、回车符以及换行符。
|
||||
|
||||
例如,以下所有字符串字面量的值都是相同的:
|
||||
|
||||
@ -258,7 +301,7 @@ true // 布尔值字面量
|
||||
let x = 3; "1 2 \(x)"
|
||||
```
|
||||
|
||||
字符串字面量的默认推导类型为 `String`。更多有关 `String` 类型的信息请参考 [字符串和字符](../chapter2/03_Strings_and_Characters.html) 以及 [*String Structure Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_String_Structure/index.html#//apple_ref/doc/uid/TP40015181)。
|
||||
字符串字面量的默认推导类型为 `String`。更多有关 `String` 类型的信息请参考 [字符串和字符](../chapter2/03_Strings_and_Characters.html) 以及 [*字符串结构参考*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_String_Structure/index.html#//apple_ref/doc/uid/TP40015181)。
|
||||
|
||||
用 `+` 操作符连接的字符型字面量是在编译时进行连接的。比如下面的 `textA` 和 `textB` 是完全一样的,`textA` 没有任何运行时的连接操作。
|
||||
|
||||
@ -268,7 +311,7 @@ let textB = "Hello world"
|
||||
```
|
||||
|
||||
> 字符串字面量语法
|
||||
|
||||
>
|
||||
<a id="string-literal"></a>
|
||||
> *字符串字面量* → [*静态字符串字面量*](#static-string-literal) | [*插值字符串字面量*](#interpolated-string-literal)
|
||||
|
||||
@ -305,6 +348,7 @@ Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基
|
||||
虽然您可以用问号 `?` 来自定义运算符,但是这个运算符不能只包含单独的一个问号。此外,虽然运算符可以包含一个惊叹号 `!`,但是前缀运算符不能够以问号或者惊叹号开头。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 以下这些标记 `=`、`->`、`//`、`/*`、`*/`、`.`、`<`(前缀运算符)、`&`、`?`、`?`(中缀运算符)、`>`(后缀运算符)、`!` 、`?` 是被系统保留的。这些符号不能被重载,也不能用于自定义运算符。
|
||||
|
||||
运算符两侧的空白被用来区分该运算符是否为前缀运算符、后缀运算符或二元运算符。规则总结如下:
|
||||
@ -323,7 +367,7 @@ Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基
|
||||
要学习如何自定义运算符,请参考 [自定义运算符](../chapter2/25_Advanced_Operators.html#custom_operators) 和 [运算符声明](05_Declarations.html#operator_declaration)。要学习如何重载运算符,请参考 [运算符函数](../chapter2/25_Advanced_Operators.html#operator_functions)。
|
||||
|
||||
> 运算符语法
|
||||
|
||||
>
|
||||
<a id="operator"></a>
|
||||
> *运算符* → [*头部运算符*](#operator-head) [*运算符字符组*](#operator-characters)<sub>可选</sub>
|
||||
> *运算符* → [*头部点运算符*](#dot-operator-head) [*点运算符字符组*](#dot-operator-characters)<sub>可选</sub>
|
||||
|
||||
@ -11,6 +11,9 @@
|
||||
> 2.1
|
||||
> 翻译:[mmoaay](https://github.com/mmoaay)
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [类型注解](#type_annotation)
|
||||
@ -32,11 +35,25 @@ Swift 语言存在两种类型:命名型类型和复合型类型。命名型
|
||||
|
||||
复合型类型是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型 `(Int, (Int, Int))` 包含两个元素:第一个是命名型类型 `Int`,第二个是另一个复合型类型 `(Int, Int)`。
|
||||
|
||||
你可以在命名型类型和复合型类型使用小括号。但是在类型旁加小括号没有任何作用。举个例子,`(Int)`等同于`Int`。
|
||||
|
||||
本节讨论 Swift 语言本身定义的类型,并描述 Swift 中的类型推断行为。
|
||||
|
||||
> 类型语法
|
||||
>
|
||||
<a name="type"></a>
|
||||
> *类型* → [*数组类型*](#array-type) | [*字典类型*](#dictionary-type) | [*函数类型*](#function-type) | [*类型标识*](#type-identifier) | [*元组类型*](#tuple-type) | [*可选类型*](#optional-type) | [*隐式解析可选类型*](#implicitly-unwrapped-optional-type) | [*协议合成类型*](#protocol-composition-type) | [*元型类型*](#metatype-type) | **任意类型** | **自身类型**
|
||||
> *类型* → [*数组类型*](#array-type)
|
||||
> *类型* → [*字典类型*](#dictionary-type)
|
||||
> *类型* → [*函数类型*](#function-type)
|
||||
> *类型* → [*类型标识*](#type-identifier)
|
||||
> *类型* → [*元组类型*](#tuple-type)
|
||||
> *类型* → [*可选类型*](#optional-type)
|
||||
> *类型* → [*隐式解析可选类型*](#implicitly-unwrapped-optional-type)
|
||||
> *类型* → [*协议合成类型*](#protocol-composition-type)
|
||||
> *类型* → [*元型类型*](#metatype-type)
|
||||
> *类型* → **任意类型**
|
||||
> *类型* → **自身类型**
|
||||
> *类型* → [*(类型)*](#type)
|
||||
|
||||
<a name="type_annotation"></a>
|
||||
## 类型注解
|
||||
@ -52,6 +69,7 @@ func someFunction(a: Int) { /* ... */ }
|
||||
类型注解可以在类型之前包含一个类型特性的可选列表。
|
||||
|
||||
> 类型注解语法
|
||||
>
|
||||
<a name="type-annotation"></a>
|
||||
> *类型注解* → **:** [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [*类型*](#type)
|
||||
|
||||
@ -76,6 +94,7 @@ var someValue: ExampleModule.MyType
|
||||
```
|
||||
|
||||
> 类型标识符语法
|
||||
>
|
||||
<a name="type-identifier"></a>
|
||||
> *类型标识符* → [*类型名称*](#type-name) [*泛型参数子句*](08_Generic_Parameters_and_Arguments.html#generic_argument_clause)<sub>可选</sub> | [*类型名称*](#type-name) [*泛型参数子句*](08_Generic_Parameters_and_Arguments.html#generic_argument_clause)<sub>可选</sub> **.** [*类型标识符*](#type-identifier)
|
||||
<a name="type-name"></a>
|
||||
@ -97,9 +116,10 @@ someTuple = (9, 99) // 正确:命名类型被自动推断
|
||||
someTuple = (left: 5, right: 5) // 错误:命名类型不匹配
|
||||
```
|
||||
|
||||
`Void` 是空元组类型 `()` 的别名。如果括号内只有一个元素,那么该类型就是括号内元素的类型。比如,`(Int)` 的类型是 `Int` 而不是 `(Int)`。所以,只有当元组类型包含的元素个数在两个及以上时才可以命名元组元素。
|
||||
所有的元组类型都包含两个及以上元素, 除了`Void`。 `Void` 是空元组类型 `()` 的别名。
|
||||
|
||||
> 元组类型语法
|
||||
>
|
||||
<a name="tuple-type"></a>
|
||||
> *元组类型* → **(** [*元组类型元素列表*](#tuple-type-element-list) <sub>可选</sub> **)**
|
||||
<a name="tuple-type-element-list"></a>
|
||||
@ -118,13 +138,15 @@ someTuple = (left: 5, right: 5) // 错误:命名类型不匹配
|
||||
|
||||
参数类型是由逗号间隔的类型列表。由于参数类型和返回值类型可以是元组类型,所以函数类型支持多参数与多返回值的函数与方法。
|
||||
|
||||
你可以对函数参数使用 `autoclosure` 特性。这会自动将参数表达式转化为闭包,表达式的结果即闭包返回值。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被使用。以自动闭包做为参数的函数类型的例子详见 [自动闭包](../chapter2/07_Closures.html#autoclosures) 。
|
||||
你可以对函数参数`() - > T `(其中 T 是任何类型)使用 `autoclosure` 特性。这会自动将参数表达式转化为闭包,表达式的结果即闭包返回值。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被调用。以自动闭包做为参数的函数类型的例子详见 [自动闭包](../chapter2/07_Closures.html#autoclosures) 。
|
||||
|
||||
函数类型可以拥有一个可变长参数作为参数类型中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变长参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变长参数的例子,请参阅 [可变参数](../chapter2/06_Functions.html#variadic_parameters)。
|
||||
|
||||
为了指定一个 `in-out` 参数,可以在参数类型前加 `inout` 前缀。但是你不可以对可变长参数或返回值类型使用 `inout`。关于这种参数的详细讲解请参阅 [输入输出参数](../chapter2/06_Functions.html#in_out_parameters)。
|
||||
|
||||
函数和方法中的参数名并不是函数类型的一部分。例如:
|
||||
如果一个函数类型只有一个形式参数而且形式参数的类型是元组类型,那么元组类型在写函数类型的时候必须用圆括号括起来。比如说, `((Int, Int)) -> Void` 是接收一个元组 `(Int, Int)` 作为形式参数的函数的类型。与此相反,不加括号的`(Int, Int) -> Void` 是一个接收两个 `Int` 形式参数并且不返回任何值的函数的类型。相似地,因为`Void` 是空元组类型 `()` 的别名, 函数类型`(Void)-> Void`与一个空元组的变量的函数类型`(()) -> ()`是一样的。但这些类型和无变量的函数类型`() -> ()`是不一样的。
|
||||
|
||||
函数和方法中的变量名并不是函数类型的一部分。例如:
|
||||
|
||||
```swift
|
||||
func someFunction(left: Int, right: Int) {}
|
||||
@ -142,11 +164,44 @@ f = functionWithDifferentArgumentTypes // 错误
|
||||
f = functionWithDifferentNumberOfArguments // 错误
|
||||
```
|
||||
|
||||
由于变量标签不是函数类型的一部分,你可以在写函数类型的时候省略它们。
|
||||
|
||||
```
|
||||
var operation: (lhs: Int, rhs: Int) -> Int // 错误
|
||||
var operation: (_ lhs: Int, _ rhs: Int) -> Int // 正确
|
||||
var operation: (Int, Int) -> Int // 正确
|
||||
```
|
||||
|
||||
如果一个函数类型包涵多个箭头(->),那么函数类型将从右向左进行组合。例如,函数类型 `Int -> Int -> Int` 可以理解为 `Int -> (Int -> Int)`,也就是说,该函数类型的参数为 `Int` 类型,其返回类型是一个参数类型为 `Int`,返回类型为 `Int` 的函数类型。
|
||||
|
||||
函数类型若要抛出错误就必须使用 `throws` 关键字来标记,若要重抛错误则必须使用 `rethrows` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](05_Declarations.html#throwing_functions_and_methods) 和 [重抛函数与方法](05_Declarations.html#rethrowing_functions_and_methods)。
|
||||
|
||||
<a name="Restrictions for Nonescaping Closures"></a>
|
||||
### 对非逃逸闭包的限制
|
||||
非逃逸闭包函数不能作为参数传递到另一个非逃逸闭包函数的参数。这样的限制可以让Swift在编译时就完成更多的内存访问冲突检查, 而不是在运行时。举个例子:
|
||||
|
||||
```
|
||||
let external: (Any) -> Void = { _ in () }
|
||||
func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) {
|
||||
first(first) // 错误
|
||||
second(second) // 错误
|
||||
|
||||
first(second) // 错误
|
||||
second(first) // 错误
|
||||
|
||||
first(external) // 正确
|
||||
external(first) // 正确
|
||||
}
|
||||
```
|
||||
在上面代码里,`takesTwoFunctions(first:second:)`的两个参数都是函数。 它们都没有标记为`@escaping`, 因此它们都是非逃逸的。
|
||||
|
||||
上述例子里的被标记为“错误”的四个函数调用会产生编译错误。因为第一个和第二个参数是非逃逸函数,它们不能够被当作变量被传递到另一个非闭包函数参数。与此相反, 标记“正确”的两个函数不回产生编译错误。这些函数调用不会违反限制, 因为`外部(external)`不是`takesTwoFunctions(first:second:)`里的一个参数。
|
||||
|
||||
如果你需要避免这个限制, 标记其中之一的参数为逃逸, 或者使用`withoutActuallyEscaping(_:do:)`函数临时地转换非逃逸函数的其中一个参数为逃逸函数。关于避免内存访问冲突,可以参阅[内存安全](../chapter2/24_Memory_Safety.html)。
|
||||
|
||||
|
||||
> 函数类型语法
|
||||
>
|
||||
<a name="function-type"></a>
|
||||
> *函数类型* → [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> [*函数类型子句*](#function-type-argument-clause) **throws**<sub>可选</sub> **->** [*类型*](#type)
|
||||
> *函数类型* → [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> [*函数类型子句*](#function-type-argument-clause) **rethrows** **->** [*类型*](#type)
|
||||
@ -187,6 +242,7 @@ var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
|
||||
关于 Swift 标准库中 `Array` 类型的详细讨论,请参阅 [数组](../chapter2/04_Collection_Types.html#arrays)。
|
||||
|
||||
> 数组类型语法
|
||||
>
|
||||
<a name="array-type"></a>
|
||||
> *数组类型* → **[** [*类型*](#type) **]**
|
||||
|
||||
@ -213,6 +269,7 @@ let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]
|
||||
关于 Swift 标准库中 `Dictionary` 类型的详细讨论,请参阅 [字典](../chapter2/04_Collection_Types.html#dictionaries)。
|
||||
|
||||
> 字典类型语法
|
||||
>
|
||||
<a name="dictionary-type"></a>
|
||||
> *字典类型* → **[** [*类型*](#type) **:** [*类型*](#type) **]**
|
||||
|
||||
@ -244,6 +301,7 @@ optionalInteger! // 42
|
||||
更多细节以及更多如何使用可选类型的例子,请参阅 [可选类型](../chapter2/01_The_Basics.html#optionals)。
|
||||
|
||||
> 可选类型语法
|
||||
>
|
||||
<a name="optional-type"></a>
|
||||
> *可选类型* → [*类型*](#type) **?**
|
||||
|
||||
@ -278,6 +336,7 @@ let implicitlyUnwrappedArray: [Int]! // 正确
|
||||
关于隐式解析可选类型的更多细节,请参阅 [隐式解析可选类型](../chapter2/01_The_Basics.html#implicityly_unwrapped_optionals)。
|
||||
|
||||
> 隐式解析可选类型语法
|
||||
>
|
||||
<a name="implicitly-unwrapped-optional-type"></a>
|
||||
> *隐式解析可选类型* → [*类型*](#type) **!**
|
||||
|
||||
@ -295,6 +354,7 @@ let implicitlyUnwrappedArray: [Int]! // 正确
|
||||
协议合成列表中的每项必须是协议名或协议合成类型的类型别名。
|
||||
|
||||
> 协议合成类型语法
|
||||
>
|
||||
<a name="protocol-composition-type"></a>
|
||||
> *协议合成类型* → [*协议标识符*](#protocol-identifier) & [*协议合成延续*](#protocol-composition-continuation)
|
||||
<a name="protocol-composition-continuation"></a>
|
||||
@ -329,16 +389,7 @@ type(of: someInstance).printClassName()
|
||||
// 打印 “SomeSubClass”
|
||||
```
|
||||
|
||||
可以使用恒等运算符(`===` 和 `!==`)来测试一个实例的运行时类型和它的编译时类型是否一致。
|
||||
|
||||
```swift
|
||||
if type(of: someInstance) === someInstance.self {
|
||||
print("The dynamic and static type of someInstance are the same")
|
||||
} else {
|
||||
print("The dynamic and static type of someInstance are different")
|
||||
}
|
||||
// 打印 "The dynamic and static type of someInstance are different"
|
||||
```
|
||||
更多信息可以查看Swift标准库里的`type(of:)`。
|
||||
|
||||
可以使用初始化表达式从某个类型的元类型构造出一个该类型的实例。对于类实例,被调用的构造器必须使用 `required` 关键字标记,或者整个类使用 `final` 关键字标记。
|
||||
|
||||
@ -357,6 +408,7 @@ let anotherInstance = metatype.init(string: "some string")
|
||||
```
|
||||
|
||||
> 元类型语法
|
||||
>
|
||||
<a name="metatype-type"></a>
|
||||
> *元类型* → [*类型*](#type) **.** **Type** | [*类型*](#type) **.** **Protocol**
|
||||
|
||||
@ -372,14 +424,13 @@ let anotherInstance = metatype.init(string: "some string")
|
||||
枚举定义中的类型继承子句可以是一系列协议,或是枚举的原始值类型的命名型类型。在枚举定义中使用类型继承子句来指定原始值类型的例子,请参阅 [原始值](../chapter2/08_Enumerations.html#raw_values)。
|
||||
|
||||
> 类型继承子句语法
|
||||
>
|
||||
<a name="type_inheritance_clause"></a>
|
||||
> *类型继承子句* → **:** [*类要求*](#class-requirement) **,** [*类型继承列表*](#type-inheritance-list)
|
||||
> *类型继承子句* → **:** [*类要求*](#class-requirement)
|
||||
> *类型继承子句* → **:** [*类型继承列表*](#type-inheritance-list)
|
||||
<a name="type-inheritance-list"></a>
|
||||
> *类型继承列表* → [*类型标识符*](#type-identifier) | [*类型标识符*](#type-identifier) **,** [*类型继承列表*](#type-inheritance-list)
|
||||
<a name="class-requirement"></a>
|
||||
> *类要求* → **class**
|
||||
|
||||
|
||||
<a name="type_inference"></a>
|
||||
## 类型推断
|
||||
|
||||
@ -17,6 +17,9 @@
|
||||
> 3.0
|
||||
> 翻译+校对:[chenmingjia](https://github.com/chenmingjia)
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [前缀表达式](#prefix_expressions)
|
||||
@ -49,6 +52,7 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表
|
||||
通过前缀表达式和二元表达式可以对简单表达式使用各种运算符。基本表达式从概念上讲是最简单的一种表达式,它是一种访问值的方式。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。
|
||||
|
||||
> 表达式语法
|
||||
>
|
||||
<a name="expression"></a>
|
||||
> *表达式* → [*try运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression) [*二元表达式列表*](#binary-expressions)<sub>可选</sub>
|
||||
<a name="expression-list"></a>
|
||||
@ -66,6 +70,7 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表
|
||||
除了标准库运算符,你也可以对某个变量使用 `&` 运算符,从而将其传递给函数的输入输出参数。更多信息,请参阅 [输入输出参数](../chapter2/06_Functions.html#in_out_parameters)。
|
||||
|
||||
> 前缀表达式语法
|
||||
>
|
||||
<a name="prefix-expression"></a>
|
||||
> *前缀表达式* → [*前缀运算符*](02_Lexical_Structure.md#prefix-operator)<sub>可选</sub> [*后缀表达式*](#postfix-expression)
|
||||
> *前缀表达式* → [*输入输出表达式*](#in-out-expression)
|
||||
@ -73,7 +78,7 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表
|
||||
> *输入输出表达式* → **&** [*标识符*](02_Lexical_Structure.md#identifier)
|
||||
|
||||
<a name="try_operator"></a>
|
||||
### try 运算符
|
||||
### Try 运算符
|
||||
|
||||
try 表达式由 `try` 运算符加上紧随其后的可抛出错误的表达式组成,形式如下:
|
||||
|
||||
@ -102,7 +107,8 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try
|
||||
`try` 表达式不能出现在二进制运算符的的右侧,除非二进制运算符是赋值运算符或者 `try` 表达式是被圆括号括起来的。
|
||||
|
||||
关于 `try`、`try?` 和 `try!` 的更多信息,以及该如何使用的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.html)。
|
||||
> try 表达式语法
|
||||
> Try 表达式语法
|
||||
>
|
||||
<a name="try-operator"></a>
|
||||
> *try 运算符* → **try** | **try?** | **try!**
|
||||
|
||||
@ -118,10 +124,12 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try
|
||||
关于 Swift 标准库提供的运算符的更多信息,请参阅 [*Swift Standard Library Operators Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 在解析时,一个二元表达式将作为一个扁平列表表示,然后根据运算符的优先级,再进一步进行组合。例如,`2 + 3 * 5` 首先被看作具有五个元素的列表,即 `2`、`+`、`3`、`*`、`5`,随后根据运算符优先级组合为 `(2 + (3 * 5))`。
|
||||
|
||||
<a name="binary-expression"></a>
|
||||
> 二元表达式语法
|
||||
>
|
||||
> *二元表达式* → [*二元运算符*](02_Lexical_Structure.md#binary-operator) [*前缀表达式*](#prefix-expression)
|
||||
> *二元表达式* → [*赋值运算符*](#assignment-operator) [*try运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression)
|
||||
> *二元表达式* → [*条件运算符*](#conditional-operator) [*try运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression)
|
||||
@ -146,6 +154,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try
|
||||
赋值运算符不返回任何值。
|
||||
|
||||
> 赋值运算符语法
|
||||
>
|
||||
<a name="assignment-operator"></a>
|
||||
> *赋值运算符* → **=**
|
||||
|
||||
@ -161,6 +170,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try
|
||||
关于使用三元条件运算符的例子,请参阅 [三元条件运算符](../chapter2/02_Basic_Operators.md#ternary_conditional_operator)。
|
||||
|
||||
> 三元条件运算符语法
|
||||
>
|
||||
<a name="conditional-operator"></a>
|
||||
> *三元条件运算符* → **?** [try运算符](#try-operator)<sub>可选</sub> [*表达式*](#expression) **:**
|
||||
|
||||
@ -203,6 +213,7 @@ f(x as Any)
|
||||
|
||||
<a name="type-casting-operator"></a>
|
||||
> 类型转换运算符语法
|
||||
>
|
||||
> *类型转换运算符* → **is** [*类型*](03_Types.md#type)
|
||||
> *类型转换运算符* → **as** [*类型*](03_Types.md#type)
|
||||
> *类型转换运算符* → **as** **?** [*类型*](03_Types.md#type)
|
||||
@ -214,6 +225,7 @@ f(x as Any)
|
||||
基本表达式是最基本的表达式。它们可以单独使用,也可以跟前缀表达式、二元表达式、后缀表达式组合使用。
|
||||
|
||||
> 基本表达式语法
|
||||
>
|
||||
<a name="primary-expression"></a>
|
||||
> *基本表达式* → [*标识符*](02_Lexical_Structure.md#identifier) [*泛型实参子句*](08_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub>
|
||||
> *基本表达式* → [*字面量表达式*](#literal-expression)
|
||||
@ -274,7 +286,7 @@ var emptyDictionary: [String : Double] = [:]
|
||||
```
|
||||
|
||||
> 字面量表达式语法
|
||||
|
||||
>
|
||||
<a name="literal-expression"></a>
|
||||
> *字面量表达式* → [*字面量*](02_Lexical_Structure.md#literal)
|
||||
> *字面量表达式* → [*数组字面量*](#array-literal) | [*字典字面量*](#dictionary-literal)
|
||||
@ -295,7 +307,7 @@ var emptyDictionary: [String : Double] = [:]
|
||||
> *字典字面量项* → [*表达式*](#expression) **:** [*表达式*](#expression)
|
||||
|
||||
<a name="self_expression"></a>
|
||||
### self 表达式
|
||||
### Self 表达式
|
||||
|
||||
`self` 表达式是对当前类型或者当前实例的显式引用,它有如下形式:
|
||||
|
||||
@ -329,7 +341,8 @@ struct Point {
|
||||
}
|
||||
```
|
||||
|
||||
> self 表达式语法
|
||||
> Self 表达式语法
|
||||
>
|
||||
<a name="self-expression"></a>
|
||||
> *self 表达式* → **self** | [*self 方法表达式*](#self-method-expression) | [*self 下标表达式*](#self-subscript-expression) | [*self 构造器表达式*](#self-initializer-expression)
|
||||
>
|
||||
@ -341,9 +354,9 @@ struct Point {
|
||||
> *self 构造器表达式* → **self** **.** **init**
|
||||
|
||||
<a name="superclass_expression"></a>
|
||||
### 超类表达式
|
||||
### 父类表达式
|
||||
|
||||
超类表达式可以使我们在某个类中访问它的超类,它有如下形式:
|
||||
*父类*表达式可以使我们在某个类中访问它的超类,它有如下形式:
|
||||
|
||||
> super.`成员名称`
|
||||
> super[`下标索引`]
|
||||
@ -353,9 +366,11 @@ struct Point {
|
||||
|
||||
子类可以通过超类表达式在它们的成员、下标和构造器中使用超类中的实现。
|
||||
|
||||
> 超类表达式语法
|
||||
> 父类表达式语法
|
||||
>
|
||||
<a name="superclass-expression"></a>
|
||||
> *超类表达式* → [*超类方法表达式*](#superclass-method-expression) | [*超类下标表达式*](#superclass-subscript-expression) | [*超类构造器表达式*](#superclass-initializer-expression)
|
||||
>
|
||||
<a name="superclass-method-expression"></a>
|
||||
> *超类方法表达式* → **super** **.** [*标识符*](02_Lexical_Structure.md#identifier)
|
||||
<a name="superclass-subscript-expression"></a>
|
||||
@ -366,7 +381,7 @@ struct Point {
|
||||
<a name="closure_expression"></a>
|
||||
### 闭包表达式
|
||||
|
||||
闭包表达式会创建一个闭包,在其他语言中也叫 *lambda* 或匿名函数。跟函数一样,闭包包含了待执行的代码,不同的是闭包还会捕获所在环境中的常量和变量。它的形式如下:
|
||||
闭包表达式会创建一个闭包,在其他语言中也叫 *lambda* 或*匿名*函数。跟函数一样,闭包包含了待执行的代码,不同的是闭包还会捕获所在环境中的常量和变量。它的形式如下:
|
||||
|
||||
```swift
|
||||
{ (parameters) -> return type in
|
||||
@ -402,6 +417,8 @@ myFunction { $0 + $1 }
|
||||
|
||||
关于如何将闭包作为参数来传递的内容,请参阅 [函数调用表达式](#function_call_expression)。
|
||||
|
||||
关于逃逸闭包的内容,请参阅[逃逸闭包](./chapter2/07_Closures.md#escaping_closures)
|
||||
|
||||
#### 捕获列表
|
||||
|
||||
默认情况下,闭包会捕获附近作用域中的常量和变量,并使用强引用指向它们。你可以通过一个捕获列表来显式指定它的捕获行为。
|
||||
@ -461,7 +478,7 @@ myFunction { [weak parent = self.parent] in print(parent!.title) }
|
||||
关于闭包表达式的更多信息和例子,请参阅 [闭包表达式](../chapter2/07_Closures.md#closure_expressions)。关于捕获列表的更多信息和例子,请参阅 [解决闭包引起的循环强引用](../chapter2/16_Automatic_Reference_Counting.md#resolving_strong_reference_cycles_for_closures)。
|
||||
|
||||
> 闭包表达式语法
|
||||
|
||||
>
|
||||
<a name="closure-expression"></a>
|
||||
> *闭包表达式* → **{** [*闭包签名*](#closure-signature)<sub>可选</sub> [*语句*](10_Statements.md#statements) **}**
|
||||
|
||||
@ -496,25 +513,37 @@ x = .AnotherValue
|
||||
```
|
||||
|
||||
> 隐式成员表达式语法
|
||||
>
|
||||
<a name="implicit-member-expression"></a>
|
||||
> *隐式成员表达式* → **.** [*标识符*](02_Lexical_Structure.md#identifier)
|
||||
|
||||
<a name="parenthesized_expression"></a>
|
||||
### 圆括号表达式
|
||||
|
||||
圆括号表达式由圆括号和其中多个逗号分隔的子表达式组成。每个子表达式前面可以有一个标识符,用冒号隔开。圆括号表达式形式如下:
|
||||
*圆括号表达式*是由圆括号包围的表达式。你可以用圆括号说明成组的表达式的先后操作。成组的圆括号不会改变表达式的类型 - 例如`(1)`的类型就是简单的`Int`。
|
||||
|
||||
> 圆括号表达式语法
|
||||
>
|
||||
<a name="parenthesized-expression"></a>
|
||||
> *圆括号表达式* → **( [*表达式*](#expression) )**
|
||||
|
||||
<a name="Tuple_Expression"></a>
|
||||
### 元组表达式
|
||||
|
||||
元组表达式由圆括号和其中多个逗号分隔的子表达式组成。每个子表达式前面可以有一个标识符,用冒号隔开。元组表达式形式如下:
|
||||
|
||||
> (`标识符 1` : `表达式 1`, `标识符 2` : `表达式 2`, `...`)
|
||||
|
||||
使用圆括号表达式来创建元组,然后将其作为参数传递给函数。如果某个圆括号表达式中只有一个子表达式,那么它的类型就是子表达式的类型。例如,表达式 `(1)` 的类型是 `Int`,而不是 `(Int)`。
|
||||
元组表达式可以一个表达式都没有,也可以包含两个或是更多的表达式。单个表达式用括号括起来就是括号表达式了。
|
||||
|
||||
> 圆括号表达式语法
|
||||
<a name="parenthesized-expression"></a>
|
||||
> *圆括号表达式* → **(** [*表达式元素列表*](#expression-element-list)<sub>可选</sub> **)**
|
||||
<a name="expression-element-list"></a>
|
||||
> *表达式元素列表* → [*表达式元素*](#expression-element) | [*表达式元素*](#expression-element) **,** [*表达式元素列表*](#expression-element-list)
|
||||
<a name="expression-element"></a>
|
||||
> *表达式元素* → [*表达式*](#expression) | [*标识符*](02_Lexical_Structure.md#identifier) **:** [*表达式*](#expression)
|
||||
> 元组表达式语法
|
||||
>
|
||||
<a name="tuple-expression"></a>
|
||||
> *元组表达式* → **( )** | **(**[*元组元素*](#tuple-element), [*元组元素列表*](#tuple-element-list) **)**
|
||||
<a name="tuple-element-list"></a>
|
||||
> *元组元素列表* → [*元组元素*](#tuple-element) | [*元组元素*](#tuple-element) **,** [*元组元素列表*](#tuple-element-list)
|
||||
<a name="tuple-element"></a>
|
||||
> *元组元素* → [*表达式*](#expression) | [*标识符*](identifier) **:** [*表达式*](#expression)
|
||||
|
||||
<a name="wildcard_expression"></a>
|
||||
### 通配符表达式
|
||||
@ -527,6 +556,7 @@ x = .AnotherValue
|
||||
```
|
||||
|
||||
> 通配符表达式语法
|
||||
>
|
||||
<a name="wildcard-expression"></a>
|
||||
> *通配符表达式* → **_**
|
||||
|
||||
|
||||
@ -15,6 +15,9 @@
|
||||
> 3.0
|
||||
> 翻译:[chenmingjia](https://github.com/chenmingjia)
|
||||
|
||||
> 4.1
|
||||
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [循环语句](#loop_statements)
|
||||
@ -80,14 +83,15 @@
|
||||
`for-in` 语句的形式如下:
|
||||
|
||||
```swift
|
||||
for 项 in 集合 {
|
||||
循环体语句
|
||||
for item in collection {
|
||||
statements
|
||||
}
|
||||
```
|
||||
|
||||
`for-in` 语句在循环开始前会调用集合表达式的 `generate()` 方法来获取一个实现了 `GeneratorType` 协议的类型的值。接下来循环开始,反复调用该值的 `next()` 方法。如果其返回值不是 `None`,它将会被赋给“项”,然后执行循环体语句,执行完毕后回到循环开始处,继续重复这一过程;否则,既不会赋值也不会执行循环体语句,`for-in` 语句至此执行完毕。
|
||||
|
||||
> for-in 语句语法
|
||||
>
|
||||
<a name="for-in-statement"></a>
|
||||
> *for-in 语句* → **for** **case**<sub>可选</sub> [*模式*](07_Patterns.md#pattern) **in** [*表达式*](04_Expressions.md#expression) [*where子句*](#where-clause)<sub>可选</sub> [*代码块*](05_Declarations.md#code-block)
|
||||
|
||||
@ -99,8 +103,8 @@ for 项 in 集合 {
|
||||
`while` 语句的形式如下:
|
||||
|
||||
```swift
|
||||
while 条件 {
|
||||
语句
|
||||
while condition {
|
||||
statements
|
||||
}
|
||||
```
|
||||
|
||||
@ -114,23 +118,16 @@ while 条件 {
|
||||
条件的结果必须是Bool类型或者Bool的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
|
||||
|
||||
> while 语句语法
|
||||
|
||||
>
|
||||
<a name="while-statement"></a>
|
||||
> *while 语句* → **while** [*条件子句*](#condition-clause) [*代码块*](05_Declarations.md#code-block)
|
||||
|
||||
<a name="condition-clause"></a>
|
||||
> *条件子句* → [*表达式*](04_Expressions.md#expression)
|
||||
> *条件子句* → [*表达式*](04_Expressions.md#expression) **,** [*条件列表*](#condition-list)
|
||||
> *条件子句* → [*条件列表*](#condition-list)
|
||||
> *条件子句* → [*可用性条件*](#availability-condition) **,** [*表达式*](04_Expressions.md#expression)
|
||||
|
||||
<a name="condition-list"></a>
|
||||
> *条件列表* → [*条件*](#condition) | [*条件*](#condition) **,** [*条件列表*](#condition-list)
|
||||
> *条件子句* → [*表达式*](04_Expressions.md#expression) | [*表达式*](04_Expressions.md#expression) **,** [*条件列表*](#condition-list)
|
||||
<a name="condition"></a>
|
||||
> *条件* → [*表达式*](04_Expressions.md#expression) |[*可用性条件*](#availability-condition) | [*case条件*](#case-condition) | [*可选绑定条件*](#optional-binding-condition)
|
||||
<a name="case-condition"></a>
|
||||
> *case 条件* → **case** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer)
|
||||
|
||||
<a name="optional-binding-condition"></a>
|
||||
> *可选绑定条件* → **let** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer) | **var** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer)
|
||||
|
||||
@ -143,8 +140,8 @@ while 条件 {
|
||||
|
||||
```swift
|
||||
repeat {
|
||||
语句
|
||||
} while 条件
|
||||
statements
|
||||
} while condition
|
||||
```
|
||||
|
||||
`repeat-while` 语句的执行流程如下:
|
||||
@ -157,6 +154,7 @@ repeat {
|
||||
条件的结果必须是Bool类型或者Bool的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
|
||||
|
||||
> repeat-while 语句语法
|
||||
>
|
||||
<a name="repeat-while-statement"></a>
|
||||
> *repeat-while 语句* → **repeat** [*代码块*](05_Declarations.md#code-block) **while** [*表达式*](04_Expressions.md#expression)
|
||||
|
||||
@ -168,6 +166,7 @@ repeat {
|
||||
`if` 语句和 `switch` 语句中的控制流可以用 `break` 语句改变,请参阅 [Break 语句](#break_statement)。
|
||||
|
||||
> 分支语句语法
|
||||
>
|
||||
<a name="branch-statement"></a>
|
||||
> *分支语句* → [*if 语句*](#if-statement)
|
||||
> *分支语句* → [*guard 语句*](#guard-statement)
|
||||
@ -183,36 +182,37 @@ repeat {
|
||||
第一种形式是当且仅当条件为真时执行代码,像下面这样:
|
||||
|
||||
```swift
|
||||
if 条件 {
|
||||
语句
|
||||
if condition {
|
||||
statements
|
||||
}
|
||||
```
|
||||
|
||||
第二种形式是在第一种形式的基础上添加 `else` 语句,当只有一个 `else` 语句时,像下面这样:
|
||||
|
||||
```swift
|
||||
if 条件 {
|
||||
若条件为真则执行这部分语句
|
||||
if ondition {
|
||||
statements to execute if condition is true
|
||||
} else {
|
||||
若条件为假则执行这部分语句
|
||||
statements to execute if condition is false
|
||||
}
|
||||
```
|
||||
|
||||
`else` 语句也可包含 `if` 语句,从而形成一条链来测试更多的条件,像下面这样:
|
||||
|
||||
```swift
|
||||
if 条件1 {
|
||||
若条件1为真则执行这部分语句
|
||||
} else if 条件2 {
|
||||
若条件2为真则执行这部分语句
|
||||
if condition 1 {
|
||||
statements to execute if condition 1 is true
|
||||
} else if condition 2 {
|
||||
statements to execute if condition 2 is true
|
||||
} else {
|
||||
若前两个条件均为假则执行这部分语句
|
||||
statements to execute if both conditions are false
|
||||
}
|
||||
```
|
||||
|
||||
`if` 语句中条件的结果必须是Bool类型或者Bool的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
|
||||
|
||||
> if 语句语法
|
||||
>
|
||||
<a name="if-statement"></a>
|
||||
> *if 语句* → **if** [*条件子句*](#condition-clause) [*代码块*](05_Declarations.md#code-block) [*else子句*](#else-clause)<sub>可选</sub>
|
||||
<a name="else-clause"></a>
|
||||
@ -226,8 +226,8 @@ if 条件1 {
|
||||
`guard` 语句的格式如下:
|
||||
|
||||
```swift
|
||||
guard 条件 else {
|
||||
语句
|
||||
guard condition else {
|
||||
statements
|
||||
}
|
||||
```
|
||||
|
||||
@ -245,6 +245,7 @@ guard 条件 else {
|
||||
关于控制转移语句,请参阅 [控制转移语句](#control_transfer_statements)。关于`Never`返回类型的函数,请参阅 [永不返回的函数](05_Declarations.md#rethrowing_functions_and_methods)。
|
||||
|
||||
> guard 语句语法
|
||||
>
|
||||
<a name="guard-statement"></a>
|
||||
> *guard 语句* → **guard** [*条件子句*](#condition-clause) **else** [*代码块*](05_Declarations.html#code-block)
|
||||
|
||||
@ -256,19 +257,20 @@ guard 条件 else {
|
||||
`switch` 语句的形式如下:
|
||||
|
||||
```swift
|
||||
switch 控制表达式 {
|
||||
case 模式1:
|
||||
语句
|
||||
case 模式2 where 条件:
|
||||
语句
|
||||
case 模式3 where 条件, 模式4 where 条件:
|
||||
语句
|
||||
switch control expression {
|
||||
case pattern 1:
|
||||
statements
|
||||
case pattern 2 where condition:
|
||||
statements
|
||||
case pattern 3 where condition,
|
||||
pattern 4 where condition:
|
||||
statements
|
||||
default:
|
||||
语句
|
||||
statements
|
||||
}
|
||||
```
|
||||
|
||||
`switch` 语句会先计算控制表达式的值,然后与每一个 `case` 的模式进行匹配。如果匹配成功,程序将会执行对应的 `case` 中的语句。另外,每一个 `case` 都不能为空,也就是说在每一个 `case` 中必须至少有一条语句。如果你不想在匹配到的 `case` 中执行代码,只需在该 `case` 中写一条 `break` 语句即可。
|
||||
`switch` 语句会先计算*控制表达式*的值,然后与每一个 `case` 的模式进行匹配。如果匹配成功,程序将会执行对应的 `case` 中的语句。另外,每一个 `case` 都不能为空,也就是说在每一个 `case` 中必须至少有一条语句。如果你不想在匹配到的 `case` 中执行代码,只需在该 `case` 中写一条 `break` 语句即可。
|
||||
|
||||
可以用作控制表达式的值是十分灵活的。除了标量类型外,如 `Int`、`Character`,你可以使用任何类型的值,包括浮点数、字符串、元组、自定义类型的实例和可选类型。控制表达式的值还可以用来匹配枚举类型中的成员值或是检查该值是否包含在指定的 `Range` 中。关于如何在 `switch` 语句中使用这些类型,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章中的 [Switch](../chapter2/05_Control_Flow.html#switch)。
|
||||
|
||||
@ -293,7 +295,7 @@ case let (x, y) where x == y:
|
||||
当匹配到的 `case` 中的代码执行完毕后,`switch` 语句会直接退出,而不会继续执行下一个 `case` 。这就意味着,如果你想执行下一个 `case`,需要显式地在当前 `case` 中使用 `fallthrough` 语句。关于 `fallthrough` 语句的更多信息,请参阅 [Fallthrough 语句](#fallthrough_statements)。
|
||||
|
||||
> switch 语句语法
|
||||
|
||||
>
|
||||
<a name="switch-statement"></a>
|
||||
> *switch 语句* → **switch** [*表达式*](04_Expressions.md#expression) **{** [*switch-case列表*](#switch-cases)<sub>可选</sub> **}**
|
||||
<a name="switch-cases"></a>
|
||||
@ -323,6 +325,7 @@ case let (x, y) where x == y:
|
||||
关于使用带标签的语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章中的 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。
|
||||
|
||||
> 带标签的语句语法
|
||||
>
|
||||
<a name="labeled-statement"></a>
|
||||
> *带标签的语句* → [*语句标签*](#statement-label) [*循环语句*](#loop-statement) | [*语句标签*](#statement-label) [*if语句*](#if-statement) | [*语句标签*](#statement-label) [*switch语句*](#switch-statement)
|
||||
<a name="statement-label"></a>
|
||||
@ -336,6 +339,7 @@ case let (x, y) where x == y:
|
||||
控制转移语句能够无条件地把控制权从一片代码转移到另一片代码,从而改变代码执行的顺序。Swift 提供五种类型的控制转移语句:`break` 语句、`continue` 语句、`fallthrough` 语句、`return` 语句和 `throw` 语句。
|
||||
|
||||
> 控制转移语句语法
|
||||
>
|
||||
<a name="control-transfer-statement"></a>
|
||||
> *控制转移语句* → [*break 语句*](#break-statement)
|
||||
> *控制转移语句* → [*continue 语句*](#continue-statement)
|
||||
@ -349,7 +353,7 @@ case let (x, y) where x == y:
|
||||
`break` 语句用于终止循环语句、`if` 语句或 `switch` 语句的执行。使用 `break` 语句时,可以只写 `break` 这个关键词,也可以在 `break` 后面跟上标签名,像下面这样:
|
||||
|
||||
> break
|
||||
> break `标签名`
|
||||
> break `label name`
|
||||
|
||||
当 `break` 语句后面带标签名时,可用于终止由这个标签标记的循环语句、`if` 语句或 `switch` 语句的执行。
|
||||
|
||||
@ -360,6 +364,7 @@ case let (x, y) where x == y:
|
||||
关于使用 `break` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [Break](../chapter2/05_Control_Flow.md#break) 和 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。
|
||||
|
||||
> break 语句语法
|
||||
>
|
||||
<a name="break-statement"></a>
|
||||
> *break 语句* → **break** [*标签名称*](#label-name)<sub>可选</sub>
|
||||
|
||||
@ -369,7 +374,7 @@ case let (x, y) where x == y:
|
||||
`continue` 语句用于终止循环中当前迭代的执行,但不会终止该循环的执行。使用 `continue` 语句时,可以只写 `continue` 这个关键词,也可以在 `continue` 后面跟上标签名,像下面这样:
|
||||
|
||||
> continue
|
||||
> continue `标签名`
|
||||
> continue `label name`
|
||||
|
||||
当 `continue` 语句后面带标签名时,可用于终止由这个标签标记的循环中当前迭代的执行。
|
||||
|
||||
@ -382,6 +387,7 @@ case let (x, y) where x == y:
|
||||
关于使用 `continue` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [Continue](../chapter2/05_Control_Flow.md#continue) 和 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。
|
||||
|
||||
> continue 语句语法
|
||||
>
|
||||
<a name="continue-statement"></a>
|
||||
> *continue 语句* → **continue** [*标签名称*](#label-name)<sub>可选</sub>
|
||||
|
||||
@ -395,6 +401,7 @@ case let (x, y) where x == y:
|
||||
关于在 `switch` 语句中使用 `fallthrough` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [控制转移语句](../chapter2/05_Control_Flow.md#control_transfer_statements)。
|
||||
|
||||
> fallthrough 语句语法
|
||||
>
|
||||
<a name="fallthrough-statement"></a>
|
||||
> *fallthrough 语句* → **fallthrough**
|
||||
|
||||
@ -406,16 +413,18 @@ case let (x, y) where x == y:
|
||||
使用 `return` 语句时,可以只写 `return` 这个关键词,也可以在 `return` 后面跟上表达式,像下面这样:
|
||||
|
||||
> return
|
||||
> return `表达式`
|
||||
> return `expression`
|
||||
|
||||
当 `return` 语句后面带表达式时,表达式的值将会返回给调用函数或方法。如果表达式的值的类型与函数或者方法声明的返回类型不匹配,Swift 则会在返回表达式的值之前将表达式的值的类型转换为返回类型。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 正如 [可失败构造器](05_Declarations.md#failable_initializers) 中所描述的,`return nil` 在可失败构造器中用于表明构造失败。
|
||||
|
||||
而只写 `return` 时,仅仅是从该函数或方法中返回,而不返回任何值(也就是说,函数或方法的返回类型为 `Void` 或者说 `()`)。
|
||||
|
||||
> return 语句语法
|
||||
>
|
||||
<a name="return-statement"></a>
|
||||
> *return 语句* → **return** [*表达式*](04_Expressions.html#expression)<sub>可选</sub>
|
||||
|
||||
@ -428,13 +437,14 @@ case let (x, y) where x == y:
|
||||
|
||||
`throw` 语句由 `throw` 关键字紧跟一个表达式组成,如下所示:
|
||||
|
||||
> throw `表达式`
|
||||
> throw `expression`
|
||||
|
||||
表达式的结果必须符合 `ErrorType` 协议。
|
||||
|
||||
关于如何使用 `throw` 语句的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.md) 一章的 [用 throwing 函数传递错误](../chapter2/18_Error_Handling.md#propagating_errors_using_throwing_functions)。
|
||||
|
||||
> throw 语句语法
|
||||
>
|
||||
<a name="throw-statement"></a>
|
||||
> *throw 语句* → **throw** [*表达式*](04_Expressions.md#expression)
|
||||
|
||||
@ -447,7 +457,7 @@ case let (x, y) where x == y:
|
||||
|
||||
```swift
|
||||
defer {
|
||||
语句
|
||||
statements
|
||||
}
|
||||
```
|
||||
|
||||
@ -470,6 +480,7 @@ f()
|
||||
`defer` 语句中的语句无法将控制权转移到 `defer` 语句外部。
|
||||
|
||||
> defer 语句语法
|
||||
>
|
||||
<a name="defer-statement"></a>
|
||||
> *延迟语句* → **defer** [*代码块*](05_Declarations.md#code-block)
|
||||
|
||||
@ -484,12 +495,12 @@ Swift 中的 `do` 语句与 C 中限定代码块界限的大括号(`{}`)很
|
||||
|
||||
```swift
|
||||
do {
|
||||
try 表达式
|
||||
语句
|
||||
} catch 模式1 {
|
||||
语句
|
||||
} catch 模式2 where 条件 {
|
||||
语句
|
||||
try expression
|
||||
statements
|
||||
} catch pattern 1 {
|
||||
statements
|
||||
} catch pattern 2 where condition {
|
||||
statements
|
||||
}
|
||||
```
|
||||
|
||||
@ -500,6 +511,7 @@ do {
|
||||
关于如何在 `do` 语句中使用一系列 `catch` 子句的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.md#handling_errors)。
|
||||
|
||||
> do 语句语法
|
||||
>
|
||||
<a name="do-statement"></a>
|
||||
> *do 语句* → **do** [*代码块*](05_Declarations.md#code-block) [*多条 catch子句*](#catch-clauses)<sub>可选</sub>
|
||||
<a name="catch-clauses"></a>
|
||||
@ -513,6 +525,7 @@ do {
|
||||
编译器控制语句允许程序改变编译器的行为。Swift 有两种编译器控制语句:编译配置语句和线路控制语句。
|
||||
|
||||
> 编译器控制语句语法
|
||||
>
|
||||
<a name="compiler-control-statement"></a>
|
||||
> *编译器控制语句* → [*编译配置语句*](#build-config-statement)
|
||||
> *编译器控制语句* → [*线路控制语句*](#line-control-statement)
|
||||
@ -525,8 +538,8 @@ do {
|
||||
每一个编译配置语句都以 `#if` 开始,`#endif` 结束。如下是一个简单的编译配置语句:
|
||||
|
||||
```swift
|
||||
#if 编译配置项
|
||||
语句
|
||||
#if compilation condition
|
||||
statements
|
||||
#endif
|
||||
```
|
||||
|
||||
@ -543,6 +556,7 @@ do {
|
||||
`swift()`(语言版本检测函数)的版本号参数主要由主版本号和次版本号组成并且使用点号(`.`)分隔开,`>=` 和版本号之间不能有空格。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> `arch(arm)` 平台检测函数在 ARM 64 位设备上不会返回 `true`。如果代码在 32 位的 iOS 模拟器上编译,`arch(i386)` 平台检测函数会返回 `true`。
|
||||
|
||||
你可以使用逻辑操作符 `&&`、`||` 和 `!` 来组合多个编译配置,还可以使用圆括号来进行分组。
|
||||
@ -550,21 +564,22 @@ do {
|
||||
就像 `if` 语句一样,你可以使用 `#elseif` 子句来添加任意多个条件分支来测试不同的编译配置。你也可以使用 `#else` 子句来添加最终的条件分支。包含多个分支的编译配置语句例子如下:
|
||||
|
||||
```swift
|
||||
#if 编译配置1
|
||||
如果编译配置1成立则执行这部分代码
|
||||
#elseif 编译配置2
|
||||
如果编译配置2成立则执行这部分代码
|
||||
#if compilation condition 1
|
||||
statements to compile if compilation condition 1 is true
|
||||
#elseif compilation condition 2
|
||||
statements to compile if compilation condition 2 is true
|
||||
#else
|
||||
如果编译配置均不成立则执行这部分代码
|
||||
statements to compile if both compilation conditions are false
|
||||
#endif
|
||||
```
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 即使没有被编译,编译配置中的语句仍然会被解析。然而,唯一的例外是编译配置语句中包含语言版本检测函数:仅当 `Swift` 编译器版本和语言版本检测函数中指定的版本号匹配时,语句才会被解析。这种设定能确保旧的编译器不会尝试去解析新 Swift 版本的语法。
|
||||
|
||||
<a name="build-config-statement"></a>
|
||||
> 编译配置语句语法
|
||||
|
||||
>
|
||||
<a name="build-configuration-statement"></a>
|
||||
> *单个编译配置语句* → **#if** [*编译配置*](#build-configuration) [*语句*](#statements)<sub>可选</sub> [*多个编译配置elseif子句*](#build-configuration-elseif-clauses)<sub>可选</sub> **-** [*单个编译配置else子句*](#build-configuration-else-clause)<sub>可选</sub> **#endif**
|
||||
<a name="build-configuration-elseif-clauses"></a>
|
||||
@ -603,7 +618,7 @@ do {
|
||||
|
||||
行控制语句形式如下:
|
||||
|
||||
> \#sourceLocation(file: `文件名` , line:`行号`)
|
||||
> \#sourceLocation(file: `filename` , line:`line number`)
|
||||
|
||||
> \#sourceLocation()
|
||||
|
||||
@ -613,7 +628,7 @@ do {
|
||||
|
||||
<a name="line-control-statement"></a>
|
||||
> 行控制语句语法
|
||||
|
||||
>
|
||||
> *行控制语句* → **#sourceLocation(file:[*文件名*](#file-name),line:[*行号*](#line-number))**
|
||||
> *行控制语句* → **#sourceLocation()**
|
||||
<a name="line-number"></a>
|
||||
@ -629,10 +644,10 @@ do {
|
||||
可用性条件的形式如下:
|
||||
|
||||
```swift
|
||||
if #available(平台名称 版本, ..., *) {
|
||||
如果 API 可用,则执行这部分语句
|
||||
if #available(platform name version, ..., *) {
|
||||
statements to execute if the APIs are available
|
||||
} else {
|
||||
如果 API 不可用,则执行这部分语句
|
||||
fallback statements to execute if the APIs are unavailable
|
||||
}
|
||||
```
|
||||
|
||||
@ -643,7 +658,7 @@ if #available(平台名称 版本, ..., *) {
|
||||
与布尔类型的条件不同,不能用逻辑运算符 `&&` 和 `||` 组合可用性条件。
|
||||
|
||||
> 可用性条件语法
|
||||
|
||||
>
|
||||
<a name="availability-condition"></a>
|
||||
> *可用性条件* → **#available** **(** [*可用性参数列表*](#availability-arguments) **)**
|
||||
<a name="availability-arguments"></a>
|
||||
Reference in New Issue
Block a user