1 Commits

Author SHA1 Message Date
a064d9f4d6 Update 25_Access_Control.md 2019-07-23 23:35:13 -05:00
67 changed files with 4387 additions and 6564 deletions

View File

@ -7,19 +7,12 @@
# 在线阅读
使用 GitBook 制作,可以 [GitBook](https://swiftgg.gitbook.io/swift/) 网站阅读
使用 GitBook 制作,可以直接 [在线阅读](https://swiftgg.gitbook.io/swift/)。
# 当前阶段
- 更新到 Swift 5.72022-06-06
- 更新到 Swift 5.62022-03-14
- 更新到 Swift 5.52021-06-07
- 更新到 Swift 5.42021-04-26
- 更新到 Swift 5.32020-09-16
- 更新到 Swift 5.22020-02-15
- 更新到 Swift 5.12019-11-11
- 更新到 Swift 5.12019-07-10
- 更新到 Swift 5.02019-04-05
- 更新到 Swift 4.52019-03-16
- 更新到 Swift 4.22019-01-29
- 更新到 Swift 4.12018-04-12感谢 [@Mylittleswift](https://github.com/Mylittleswift)
- 更新到 Swift 3.02016-09-23
@ -64,13 +57,8 @@ diff 操作如下:
| 术语 | 备选翻译 |
| --- | --- |
| result builder | 结果构造器 |
| property wrapper | 属性包装器([翻译相关讨论](https://github.com/SwiftGGTeam/the-swift-programming-language-in-chinese/issues/982#issuecomment-536244784) |
| projected value | 被呈现值 |
| wrapped value | 被包装值 |
| argument | 实参 |
| parameter | 形参 |
| variadic parameters| 可变参数 |
| associated type | 关联类型 |
| range | 区间 |
| type property | 类型属性 |

View File

@ -1,13 +0,0 @@
# 版本兼容性
本书描述的是在 Xcode 14 中默认包含的 Swift 5.7 版本。你可以使用 Xcode 14 来构建 Swift 5.7、Swift 4.2 或 Swift 4 写的项目。
使用 Xcode 14 构建 Swift 4 和 Swift 4.2 代码时Swift 5.7 的大多数功能都适用。但以下功能仅支持 Swift 5.7 或更高版本:
* 返回值是不透明类型的函数依赖 Swift 5.1 运行时。
* **try?** 表达式不会为已返回可选类型的代码引入额外的可选类型层级。
* 大数字的整型字面量初始化代码的类型将会被正确推导,例如 **UInt64(0xffff_ffff_ffff_ffff)** 将会被推导为整型类型而非溢出。
并发特性需要 Swift 5.7 及以上版本,以及一个提供了并发相关类型的 Swift 标准库版本。要应用于苹果平台,请至少将部署版本设置为 iOS 15、macOS 12、tvOS 15 或 watchOS 8.0。
用 Swift 5.7 写的项目可以依赖用 Swift 4.2 或 Swift 4 写的项目反之亦然。这意味着如果你将一个大的项目分解成多个框架framework你可以逐个地将框架从 Swift 4 代码迁移到 Swift 5.7。

View File

@ -1,298 +0,0 @@
# 并发
Swift 对于结构化的编写异步和并行代码有着原生的支持。异步代码可以被挂起并在之后继续执行,同一时间只能有一段代码被执行。代码支持挂起和继续执行,就可以在执行耗时很长的任务时抽空执行一些快速的操作,比如在下载文件、解析文件的过程中更新 UI。*并行代码*指的是多段代码同时执行;比如一个拥有四核处理器的电脑可以同时运行四段代码,每个核心执行其中一项任务。一个使用并行和异步代码的程序可以同时执行多个运算;它可以在某个运算等待外部系统的时候挂起这个运算,从而让编写内存安全的代码更加容易。
并发和异步代码在带来时序灵活性的同时不免会增加复杂度。一些异步代码会自动包含编译时检查——比如,你可以使用 actor 来安全的访问可变的状态。然而,给一段运行缓慢并且有错误的代码添加并发能力并不能让它更快或者更正确的运行。事实上,给代码增加并发能力还有可能导致代码问题更难排查。但如果在需要并发的代码中使用 Swift 原生支持的并发能力会让你在编译阶段就发现问题。
本章剩余的部分将使用*并发*指代异步和并行。
> 注意
>
> 如果你曾经写过并发的代码的话那可能使用过线程。Swift 中的并发模型是基于线程的,但你不会直接和线程打交道。在 Swift 中一个异步函数可以交出它在某个线程上的运行权这样另一个异步函数在这个函数被阻塞时就能获得此线程的运行权。但是Swift并不能确定当异步函数恢复运行时其将在哪条线程上运行。
你当然也可以不用 Swift 原生支持去写并发的代码,只不过代码的可读性会下降。比如,下面的这段代码会拉取一系列图片名称的列表,下载列表中的图片然后展示给用户:
```Swift
listPhotos(inGallery: "Summer Vacation") { photoNames in
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
downloadPhoto(named: name) { photo in
show(photo)
}
}
```
在这个简单的案例中,由于代码中有一系列的 completion handler最终你必须得使用嵌套闭包。更加复杂的代码会产生更深的嵌套从而使代码迅速变得臃肿起来。
## 定义和调用异步函数 {#Defining-and-Calling-Asynchronous-Functions}
*异步函数*或*异步方法*是一种能在运行中被挂起的特殊函数或方法。对于普通的同步函数或方法来说,它们只能运行到完成闭包、抛出错误或者永远不返回。异步函数或方法也能做到这三件事,但同时也可以在等待其他资源的时候挂起。在异步函数或者方法的函数体中,你可以标记其中的任意位置是可以被挂起的。
为了标记某个函数或者方法是异步的,你可以在它的声明中的参数列表后边加上 `async` 关键字,和使用 `throws` 关键字来标记 throwing 函数是类似的。如果一个函数或方法有返回值,可以在返回箭头(->)前添加 `async` 关键字。 比如,下面是从图库中拉取图片名称的方法:
```Swift
func listPhotos(inGallery name: String) async -> [String] {
let result = // 省略一些异步网络请求代码
return result
}
```
对于那些既是异步又是 throwing 的函数,需要把 `async` 写在`throws` 关键字前边。
调用一个异步方法时,执行会被挂起直到这个异步方法返回。你需要在调用前增加 `await` 关键字来标记此处为可能的悬点Suspension point。这就像调用 throwing 函数需要添加 `try` 关键字来标记在发生错误的时候会改变程序流程一样。在一个异步方法中,执行只会在调用另一个异步方法的时候会被挂起;挂起永远都不会是隐式或者优先的,这也意味着所有的悬点都需要被标记为 `await`
比如,下面的这段代码可以拉取图库中所有图片的名称,然后展示第一张图片:
```Swift
let photoNames = await listPhotos(inGallery: "Summer Vacation")
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
let photo = await downloadPhoto(named: name)
show(photo)
```
因为 `listPhotos(inGallery:)``downloadPhoto(named:)` 都需要发起网络请求,需要花费较长的时间完成。给这两个函数在返回箭头前加上 `async` 可以将它们定义为异步函数,从而使得这部分代码在等待图片的时候让程序的其他部分继续运行。
为了更好理解上面这段代码的并发本质,下面列举出这段程序可能的一个执行顺序:
1. 代码从第一行开始执行到第一个 `await`,调用 `listPhotos(inGallery:)` 函数并且挂起这段代码的执行,等待这个函数的返回。
2. 当这段代码的执行被挂起时,程序的其他并行代码会继续执行。比如,后台有一个耗时长的任务更新其他一些图库。那段代码会执行到被 `await` 的标记的悬点,或者执行完成。
3.`listPhotos(inGallery:)` 函数返回之后,上面这段代码会从上次的悬点开始继续执行。它会把函数的返回赋值给 `photoNames` 变量。
4. 定义 `sortedNames``name` 的那行代码是普通的同步代码,因为并没有被 `await` 标记,也不会有任何可能的悬点。
5. 接下来的 `await` 标记是在调用 `downloadPhoto(named:)` 的地方。这里会再次暂停这段代码的执行直到函数返回,从而给了其他并行代码执行的机会。
6.`downloadPhoto(named:)` 返回后,它的返回值会被赋值到 `photo` 变量中,然后被作为参数传递给 `show(_:)`
代码中被 `await` 标记的悬点表明当前这段代码可能会暂停等待异步方法或函数的返回。这也被称为*让出线程yielding the thread*,因为在幕后 Swift 会挂起你这段代码在当前线程的执行,转而让其他代码在当前线程执行。因为有 `await` 标记的代码可以被挂起,所以在程序中只有特定的地方才能调用异步方法或函数:
* 异步函数,方法或变量内部的代码
* 静态函数 `main()` 中被打上 `@main` 标记的结构体、类或者枚举中的代码
* 非结构化的子任务中的代码,之后会在 [非结构化并行](#Unstructured-Concurrency) 中说明
在可能的悬点之间的代码将按顺序运行,并不可能被其它并发代码中断。例如,以下代码将一张图片从一个图库移动到另一个图库:
```swift
let firstPhoto = await listPhotos(inGallery: "Summer Vacation")[0]
add(firstPhoto, toGallery: "Road Trip")
//此时firstPhoto暂时地同时存在于两个画廊中
remove(firstPhoto, fromGallery: "Summer Vacation")
```
其它代码不能在 `add(_:toGallery:)``remove(_:fromGallery:)` 两个方法之间运行。在此期间,第一张图片同时存在于两个图库,暂时打破了应用程序的一个不变量。为了更明确地表示这段代码不能加入 `await` 标记,你可以将这段代码重构为一个同步函数:
```swift
func move(_photoName: String, from source: String, to destination: String) {
add(photoName, to: destination)
remove(photoName, from: source)
}
//...
let firstPhoto = await listPhotos(inGallery: "Summer Vacation")[0]
move(firstPhoto, from: "Summer Vacation", to: "Road Trip")
```
在上例中,由于 `move(_:from:to:)` 函数为同步函数你将能够保证它将不会包含潜在的悬点。在未来试图在该函数中写入并发代码将引发编译错误而非产生bug。
> 注意
>
> 学习并行的过程中,[Task.sleep(_:)](https://developer.apple.com/documentation/swift/task/3814836-sleep) 方法非常有用。这个方法什么都没有做,只是等待不少于指定的时间(单位纳秒)后返回。下面是使用 `sleep(until:clock:)` 方法模拟网络请求实现 `listPhotos(inGallery:)` 的一个版本:
>
```swift
func listPhotos(inGallery name: String) async throws -> [String] {
try await Task.sleep(until: .now + .seconds(2), clock: .continuous)
return ["IMG001", "IMG99", "IMG0404"]
}
```
## 异步序列 {#Asynchronous-Sequences}
上一节中的 `listPhotos(inGallery:)` 方法会在拿到数组中的所有元素后,以异步的方式一次性返回整个数组。另一种方式是使用*异步序列asynchronous sequence*,每次收到一个元素后对其进行处理。下面这段代码展示了如何遍历一个异步序列:
```Swift
import Foundation
let handle = FileHandle.standardInput
for try await line in handle.bytes.lines {
print(line)
}
```
与普通的 `for-in` 循环相比,上面的例子在 `for` 之后添加了 `await` 关键字。就像在调用异步函数或方法时一样,`await` 表明代码中有一个可能的悬点。`for-await-in` 循环会在每次循环开始的时候因为有可能需要等待下一个元素而挂起当前代码的执行。
想让自己创建的类型使用 `for-in` 循环需要遵循 [Sequence](https://developer.apple.com/documentation/swift/sequence) 协议,这里也同理,如果想让自己创建的类型使用 `for-await-in` 循环,就需要遵循 [AsyncSequence](https://developer.apple.com/documentation/swift/asyncsequence) 协议。
## 并行的调用异步方法 {#Calling-Asynchronous-Functions-in-Parallel}
调用有 `await` 标记的异步函数在同一时间只能执行一段代码。在异步代码执行的过程中,调用方需要等待异步代码执行完后才能继续执行下一行代码。比如,当你想从图库中拉取前三张图片,可以像下面这样,等三次调用完后再执行 `downloadPhoto(named:)` 函数:
```Swift
let firstPhoto = await downloadPhoto(named: photoNames[0])
let secondPhoto = await downloadPhoto(named: photoNames[1])
let thirdPhoto = await downloadPhoto(named: photoNames[2])
let photos = [firstPhoto, secondPhoto, thirdPhoto]
show(photos)
```
这种方式有一个非常严重的缺陷:虽然下载过程是异步的,并且在等待过程中可以执行其他任务,但每次只能执行一个 `downloadPhoto(named:)`。每一张图片只能在上一张图片下载结束了才开始下载。然而,并没有必要让这些操作等待,每张图片可以独立甚至同时下载。
为了在调用异步函数的时候让它附近的代码并发执行,定义一个常量时,在 `let` 前添加 `async` 关键字,然后在每次使用这个常量时添加 `await` 标记。
```Swift
async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])
let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)
```
在上面的例子中,三次调用 `downloadPhoto(named:)` 都不需要等待前一次调用结束。如果系统有足够的资源,这三次调用甚至都可以同时执行。这三次调用都没有没标记为 `await`,因为代码不需要被挂起等待函数的结果。程序会继续执行直到 `photos` 被定义,与上面不同的是,在这个时间点由于程序需要上面几次异步调用的结果,所以你需要添加 `await` 关键字来挂起当前代码的执行直到所有图片下载完成。
下面是关于两种不同方法的一些说法:
* 代码中接下来的几行需要依赖异步函数的结果时,需要使用 `await` 来调用异步函数。这样产生的结果是有序的。
* 短时间内并不需要异步函数的结果时,需要使用 `async-let` 来调用异步函数。这样产生的任务是并发的。
* `await` 和 `async-let` 都允许其他任务在他们被挂起的时候执行。
* 在两种情况下,都需要用 `await` 标记可能的悬点,以表明代码在这些点在需要的情况下会被挂起,直到异步函数执行结束。
你也可以在同一段代码中混用两种方式。
## 任务和任务组 {#Tasks-and-Task-Groups}
*任务task)* 是一项工作,可以作为程序的一部分并发执行。所有的异步代码都属于某个任务。上一部分介绍的 `async-let` 语法就会产生一个子任务。你也可以创建一个任务组并且给其中添加子任务,这可以让你对优先级和任务取消有了更多的掌控力,并且可以控制任务的数量。
任务是按层级结构排列的。同一个任务组中的任务拥有相同的父任务,并且每个任务都可以添加子任务。由于任务和任务组之间明确的关系,这种方式又被称为*结构化并发structured concurrency*。虽然你需要确保代码的正确性,但任务间明确的父子关系让 Swift 能替你处理一些如扩散取消propagating cancellation之类的行为并且能让 Swift 在编译阶段发现一些错误。
```Swift
await withTaskGroup(of: Data.self) { taskGroup in
let photoNames = await listPhotos(inGallery: "Summer Vacation")
for name in photoNames {
taskGroup.addTask { await downloadPhoto(named: name) }
}
}
```
如果想更多的了解任务组,可以参考 [TaskGroup](https://developer.apple.com/documentation/swift/taskgroup)。
### 非结构化并发 {#Unstructured-Concurrency}
对于并发来说除了上一部分讲到的结构化的方式Swift 还支持非结构化并发。与任务组中的任务不同的是,*非结构化任务unstructured task*并没有父任务。你能以任何方式来处理非结构化任务以满足你程序的需要,但与此同时,你需要对于他们的正确性付全责。如果想创建一个在当前 actor 上运行的非结构化任务,需要调用构造器 [Task.init(priority:operation:)](https://developer.apple.com/documentation/swift/task/3856790-init)。如果想要创建一个不在当前 actor 上运行的非结构化任务(更具体地说就是*游离任务detached task*),需要调用类方法 [Task.detached(priority:operation:)](https://developer.apple.com/documentation/swift/task/3856786-detached)。以上两种方法都能返回一个能让你与任务交互(继续等待结果或取消任务)的任务句柄,如下:
```swift
let newPhoto = // ... 图片数据 ...
let handle = Task {
return await add(newPhoto, toGalleryNamed: "Spring Adventures")
}
let result = await handle.value
```
如果你想更多的了解游离任务,可以参考 [Task](https://developer.apple.com/documentation/swift/task)。
### 任务取消 {#Task-Cancellation}
Swift 中的并发使用合作取消模型。每个任务都会在执行中合适的时间点检查自己是否被取消了,并且会用任何合适的方式来响应取消操作。这些方式会根据你所执行的工作分为以下几种:
* 抛出如 `CancellationError` 这样的错误
* 返回 nil 或者空的集合
* 返回完成一半的工作
如果想检查任务是否被取消,既可以使用 [Task.checkCancellation()](https://developer.apple.com/documentation/swift/task/3814826-checkcancellation)(如果任务取消会返回 `CancellationError`),也可以使用 [Task.isCancelled](https://developer.apple.com/documentation/swift/task/3814832-iscancelled) 来判断,继而在代码中对取消进行相应的处理。比如,一个从图库中下载图片的任务需要删除下载到一半的文件并且关闭连接。
如果想手动执行扩散取消,调用 [Task.cancel()](https://developer.apple.com/documentation/swift/task/3851218-cancel)。
## Actors {#Actors}
你可以使用任务来将自己的程序分割为孤立、并发的部分。任务间相互孤立这也使得它们能够安全地同时运行。但有时你需要在任务间共享信息。Actors便能够帮助你安全地在并发代码间分享信息。
跟类一样actor 也是一个引用类型,所以 [类是引用类型](https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html#ID89) 中关于值类型和引用类型的比较同样适用于 actor 和类。不同于类的是actor 在同一时间只允许一个任务访问它的可变状态,这使得多个任务中的代码与一个 actor 交互时更加安全。比如,下面是一个记录温度的 actor
```Swift
actor TemperatureLogger {
let label: String
var measurements: [Int]
private(set) var max: Int
init(label: String, measurement: Int) {
self.label = label
self.measurements = [measurement]
self.max = measurement
}
}
```
你可以用 `actor` 关键字引入一个 actor后边的花括号中是它的定义。`TemperatureLogger` 中有外部能访问到的属性,并且限制 `max` 变量,所以只能在 actor 内部修改最大值。
你可以使用与结构体和类初始化相同的语法创建一个 actor。当你访问 actor 中的属性或方法时,需要使用 `await` 来标记潜在的悬点,比如:
```Swift
let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
print(await logger.max)
// 输出 "25"
```
在这个例子中,访问 `logger.max` 是一个可能的悬点。因为 actor 在同一时间只允许一个任务访问它的可变状态,如果别的任务正在与 logger 交互,上面这段代码将会在等待访问属性的时候被挂起。
相比之下actor 内部的代码在访问其属性的时候不需要添加 `await` 关键字。比如,下面的方法是更新 `TemperatureLogger` 中的温度:
```Swift
extension TemperatureLogger {
func update(with measurement: Int) {
measurements.append(measurement)
if measurement > max {
max = measurement
}
}
}
```
`update(with:)` 方法本来就在 actor 中运行,所以没必要在访问如 `max` 等属性的时候加 `await` 关键字。这个方法也展示了为什么要在同一时间只允许一个任务访问其可变状态的其中一个理由:一些对于 actor 状态的改变暂时打破了不可变性。 `TemperatureLogger` 记录了一个温度的列表和最高温度,并且会在你更新了一个新测量值之后更新最大温度。在更新的过程中,在增加了新测量值但没有更新 `max` 前,`TemperatureLogger` 正处于一个暂时不一致的状态。阻止不同的任务和同一个 actor 实例交互可以防止以下事件序列的发生:
1. 你的代码调用 `update(with:)` 方法,并且先更新了 `measurements` 数组。
2. 在你的代码更新 `max` 前,其他地方的代码读取了最大值和温度列表的值。
3. 你的代码更新了 `max` 完成调用。
在这种情况下,其他的代码读取到了错误的值,因为 actor 的读取操作被夹在 `update(with:)` 方法中间,而此时数据暂时是无效的。你可以用 Swift 中的 actor 以防止这种问题的发生,因为 actor 在同一时刻只允许有一个任务能访问它的状态,而且只有在被 `await` 标记为悬点的地方代码才会被打断。因为 `update(with:)` 方法没有任何悬点,没有其他任何代码可以在更新的过程中访问到数据。
如果你想在 actor 外部像访问类属性一样访问 actor 的属性,会得到一个编译时错误;比如:
```Swift
print(logger.max) // 报错
```
不添加 `await` 关键字的情况下访问 `logger.max` 会失败,因为 actor 的属性是它隔离的本地状态的一部分。Swift 可以保证只有 actor 内部的代码可以访问 actor 的内部状态。这个保证也被称为 *actor isolation*。
## 可发送类型 {#Sendable-Types}
任务和Actor能够帮助你将程序分割为能够安全地并发运行的小块。在一个任务中或是在一个Actor实例中程序包含可变状态的部分如变量和属性被称为*并发域Concurrency domain*。部分类型的数据不能在并发域间共享,因为它们包含了可变状态,但它不能阻止重叠访问。
能够在并发域间共享的类型被称为*可发送类型(Sendable Type)*。例如在调用Actor方法时被作为实参传递或是作为任务的结果返回。本章之前的例子并未讨论可发送性因为这些例子均使用了简单值类型对于在并发域间传递的数据而言简单值类型总是安全的。而与之相反另一些类型并不能安全地在并发域间传递。例如当你在不同的任务间传递该类的实例时包含可变属性且并未序列化对这些属性的访问的类可能产生不可预测和不正确的结果。
你可以通过声明其符合 `Sendable` 协议来将某个类型标记为可发送类型。该协议并不包含任何代码要求但Swift对其做出了强制的语义要求。总之有三种方法将一个类型声明为可发送类型
- 该类型为值类型,且其可变状态由其它可发送数据构成——例如具有存储属性的结构体或是具有关联值的枚举。
- 该类型不包含任何可变状态,且其不可变状态由其它可发送数据构成——例如只包含只读属性的结构体或类
- 该类型包含能确保其可变状态安全的代码——例如标记了 `@MainActor` 的类或序列化了对特定线程/队列上其属性的访问的类。
如需了解Swift对Sendable协议的语义要求的详细信息请访问 [Sendable](https://developer.apple.com/documentation/swift/sendable) 协议参考。
部分类型总是可发送类型,如只有可发送属性的结构体和只有可发送关联值的枚举。例如:
```swift
struct TemperatureReading: Sendable {
var measurement: Int
}
extension TemperatureLogger {
func addReading(from reading: TemperatureReading) {
measurements.append(reading.measurement)
}
}
let logger = TemperatureLogger(label: "Tea kettle", measurement: 85)
let reading = TemperatureReading(measurement: 45)
await logger.addReading(from: reading)
```
由于 `TemperatureReading` 是只有可发送属性的结构体,且该结构体并未被标记为 `public` 或 `@usableFromInline`,因此它是隐式可发送的。下文给出了该结构体的一个符合 `Sendable` 协议的版本:
```swift
struct TemperatureReading {
var measurement: Int
}
```

View File

@ -1,714 +0,0 @@
# 词法结构Lexical Structure
Swift 的*“词法结构lexical structure”* 描述了能构成该语言中有效符号token的字符序列。这些合法符号组成了语言中最底层的构建基块并在之后的章节中用于描述语言的其他部分。一个合法符号由一个标识符identifier、关键字keyword、标点符号punctuation、字面量literal或运算符operator组成。
通常情况下,符号是考虑了输入文本中最长可能的子字符串,并被随后将介绍的语法约束,根据 Swift 源文件的字符生成的。这种方法称为*“最长匹配longest match”*,或者*“最大适合maximal munch”*。
## 空白与注释 {#whitespace}
空白whitespace有两个用途分隔源文件中的符号和区分前缀、后缀和中缀运算符参见 [运算符](#operators)在其他情况下空白则会被忽略。以下的字符会被当作空白空格U+0020、换行符U+000A、回车符U+000D、水平制表符U+0009、垂直制表符U+000B、换页符U+000C以及空字符U+0000
注释被编译器当作空白处理。单行注释由 `//` 开始直至遇到换行符U+000A或者回车符U+000D。多行注释由 `/*` 开始,以 `*/` 结束。多行注释允许嵌套,但注释标记必须成对出现。
注释可以包含其他的格式和标记,如 [标记格式参考](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/index.html)中所述。
> 空白语法
>
> *空白* → [空白项](#whitespace-item) [空白](#whitespace)<sub>可选</sub>
#### whitespace-item {#whitespace-item}
>
> *空白项* → [断行符](#line-break)
>
> *空白项* → [行内空白](#inline-space)
>
> *空白项* → [注释](#comment)
>
> *空白项* → [多行注释](#multiline-comment)
>
> *空白项* → U+0000U+0009U+000BU+000C 或者 U+0020
>
>
#### line-break {#line-break}
>
> *断行符* → U+000A
>
> *断行符* → U+000D
>
> *断行符* → U+000D 接着是 U+000A
>
#### inline-spaces {#inline-spaces}
>
> *行内空白组* → [行内空白](#inline-space) [行内空白组](#inline-spaces)<sub>可选</sub>
#### inline-space {#inline-space}
> *行内空白* → U+0009 或 U+0020
#### comment {#comment}
>
> *注释* → // [注释内容](#comment-text) [断行符](#line-break)
>
>
#### multiline-comment {#multiline-comment}
>
> *多行注释* → `/*` [多行注释内容](#multiline-commnet-text) `*/`
>
>
#### comment-text {#comment-text}
>
> *注释内容* → [注释内容项](#comment-text-item) [注释内容](#comment-text)<sub>可选</sub>
>
>
#### comment-text-item {#comment-text-item}
>
> *注释内容项* → 任何 Unicode 标量值,除了 U+000A 或者 U+000D
>
>
#### multiline-commnet-text {#multiline-commnet-text}
>
> *多行注释内容* → [多行注释内容项](#multiline-comment-text-item) [多行注释内容](#multiline-comment-text)<sub>可选</sub>
>
> *多行注释内容项* → [多行注释](#multiline-comment).
>
> *多行注释内容项* → [注释内容项](#comment-text-item)
>
> *多行注释内容项* → 任何 Unicode 标量值,除了 `/*` 或者 `*/`
## 标识符 {#identifiers}
*标识符identifier* 可以由以下的字符开始:大写或小写的字母 `A``Z`、下划线(`_`、基本多文种平面Basic Multilingual Plane中非字符数字组合的 Unicode 字符以及基本多文种平面以外的非个人专用区字符。在首字符之后,允许使用数字和组合 Unicode 字符。
以下划线开头的标识视为内部标识符,即使声明了 `public` 访问级别。这个约定允许框架作者标记一部分不能被使用方调用或依赖的 API即便因为某些限制原因导致它的声明必须是公开的。另外双下划线开头的标识符是为 Swift 编译器和标准库预留的。
使用保留字作为标识符,需要在其前后增加反引号(`` ` ``)。例如,`class` 不是合法的标识符,但可以使用 `` `class` ``。反引号不属于标识符的一部分,`` `x` `` 和 `x` 表示同一标识符。
闭包中如果没有明确指定参数名称,参数将被隐式命名为 `$0`、`$1`、`$2` 等等。这些命名在闭包作用域范围内是合法的标识符。
编译器给含有属性包装器呈现值的属性自动合成以美元符号(*$*)开头的标识符。你的代码可以与这些标识符进行交互,,但是不能使用该前缀声明标识符。更详细的介绍,请查看 [特性](./07_Attributes.md) 章节中的 [属性包装器](./07_Attributes.md#propertywrapper) 部分。
> 标识符语法
>
> #### identifier {#identifier}
>
> *标识符* → [头部标识符](#identifier-head) [标识符字符组](#identifier-characters)<sub>可选</sub>
>
> *标识符* → \`[头部标识符](#identifier-head) [标识符字符组](#identifier-characters)<sub>可选</sub>\`
>
> *标识符* → [隐式参数名](#implicit-parameter-name)
>
> *标识符* → [属性包装器呈现值](#property-wrapper-projection)
>
> #### identifier-list {#identifier-list}
>
> *标识符列表* → [标识符](#identifier) | [标识符](#identifier) **,** [标识符列表](#identifier)
>
>
#### identifier-head {#identifier-head}
>
> *头部标识符* → 大写或小写字母 A - Z
>
> *头部标识符* → **_**
>
> *头部标识符* → U+00A8U+00AAU+00ADU+00AFU+00B2U+00B5或者 U+00B7U+00BA
>
> *头部标识符* → U+00BCU+00BEU+00C0U+00D6U+00D8U+00F6或者 U+00F8U+00FF
>
> *头部标识符* → U+0100U+02FFU+0370U+167FU+1681U+180D或者 U+180FU+1DBF
>
> *头部标识符* → U+1E00U+1FFF
>
> *头部标识符* → U+200BU+200DU+202AU+202EU+203FU+2040U+2054或者 U+2060U+206F
>
> *头部标识符* → U+2070U+20CFU+2100U+218FU+2460U+24FF或者 U+2776U+2793
>
> *头部标识符* → U+2C00U+2DFF 或者 U+2E80U+2FFF
>
> *头部标识符* → U+3004U+3007U+3021U+302FU+3031U+303F或者 U+3040U+D7FF
>
> *头部标识符* → U+F900U+FD3DU+FD40U+FDCFU+FDF0U+FE1F或者 U+FE30U+FE44
>
> *头部标识符* → U+FE47U+FFFD
>
> *头部标识符* → U+10000U+1FFFDU+20000U+2FFFDU+30000U+3FFFD或者 U+40000U+4FFFD
>
> *头部标识符* → U+50000U+5FFFDU+60000U+6FFFDU+70000U+7FFFD或者 U+80000U+8FFFD
>
> *头部标识符* → U+90000U+9FFFDU+A0000U+AFFFDU+B0000U+BFFFD或者 U+C0000U+CFFFD
>
> *头部标识符* → U+D0000U+DFFFD 或者 U+E0000U+EFFFD
#### identifier-character {#identifier-character}
>
> *标识符字符* → 数值 0 - 9
>
> *标识符字符* → U+0300U+036FU+1DC0U+1DFFU+20D0U+20FF或者 U+FE20U+FE2F
>
> *标识符字符* → [头部标识符](#identifier-head)
>
>
#### identifier-characters {#identifier-characters}
>
> *标识符字符组* → [标识符字符](#identifier-character) [标识符字符组](#identifier-characters)<sub>可选</sub>
>
>
#### implicit-parameter-name {#implicit-parameter-name}
>
> *隐式参数名* → **$** [十进制数字列表](#decimal-digit)
#### property-wrapper-projection {#property-wrapper-projection}
>
> *属性包装器呈现值* → **$** [标识符字符组](#identifier-characters)
## 关键字和标点符号 {#keywords-and-punctuation}
下面这些被保留的关键字不允许用作标识符,除非使用反引号转义,具体描述请参考 [标识符](#identifiers)。除了 `inout``var` 以及 `let` 之外的关键字可以用作某个函数声明或者函数调用当中的外部参数名,无需添加反引号转义。当一个成员与一个关键字具有相同的名称时,不需要使用反引号来转义对该成员的引用,除非在引用该成员和使用该关键字之间存在歧义 - 例如,`self``Type``Protocol` 在显式的成员表达式中具有特殊的含义,因此它们必须在该上下文中使用反引号进行转义。
* 用在声明中的关键字:`associatedtype``class``deinit``enum``extension``fileprivate ``func``import``init``inout``internal``let``open``operator``private``precedencegroup``protocol``public``rethrows``static``struct``subscript``typealias` 以及 `var`
* 用在语句中的关键字:`break``case``catch``continue``default``defer``do``else``fallthrough``for``guard``if``in``repeat``return``throw``switch``where` 以及 `while`
* 用在表达式和类型中的关键字:`Any``as``catch``false``is``nil``rethrows``self``Self``super``throw``throws``true` 以及 `try `
* 用在模式中的关键字:`_`
* 以井字号(`#`)开头的关键字:`#available``#colorLiteral``#column``#dsohandle``#elseif``#else``#endif``#error``#fileID``#fileLiteral``#filePath``#file``#function``#if``#imageLiteral``#keyPath``#line``#selector``#sourceLocation` 以及 `#warning`
* 特定上下文中被保留的关键字:`associativity``convenience``didSet``dynamic``final``get``indirect``infix``lazy``left``mutating``none``nonmutating``optional``override``postfix``precedence``prefix``Protocol``required``right``set``some``Type``unowned``weak` 以及 `willSet`。这些关键字在特定上下文之外可以被用做标识符。
以下符号被保留为标点符号,不能用于自定义运算符:`(``)``{``}``[``]``.``,``:``;``=``@``#``&`(作为前缀运算符)、`->`、`` ` ``、`?`、以及 `!`(作为后缀运算符)。
## 字面量 {#literal}
*字面量literal* 用来表示源码中某种特定类型的值,比如一个数字或字符串。
下面是字面量的一些示例:
```swift
42 // 整数字面量
3.14159 // 浮点数字面量
"Hello, world!" // 字符串字面量
/Hello, .*/ // 正则表达式字面量
true // 布尔值字面量
```
字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中Swift 使用了显式类型注解(`: Int8`)来推导出 `42` 这个整数字面量的类型是 `Int8`。如果没有可用的类型信息Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整数字面量的默认类型是 `Int`,浮点数字面量的默认类型是 `Double`,字符串字面量的默认类型是 `String`,布尔值字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"` 的默认推导类型就是 `String`。
当为一个字面量值指定了类型注解的时候,这个注解类型必须能通过这个字面量值实例化。也就是说,这个类型必须符合这些 Swift 标准库协议中的一个:整数字面量的 `ExpressibleByIntegerLiteral` 协议、浮点数字面量的 `ExpressibleByFloatLiteral` 协议、字符串字面量的 `ExpressibleByStringLiteral` 协议、布尔值字面量的 `ExpressibleByBooleanLiteral` 协议、只包含单个 Unicode 标量字符串文本的 `ExpressibleByUnicodeScalarLiteral` 协议以及只包含单个扩展字形簇grapheme cluster字符串文字的 `ExpressibleByExtendedGraphemeClusterLiteral` 协议。比如,`Int8` 符合 `ExpressibleByIntegerLiteral` 协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整数字面量 `42` 的类型注解。
> 字面量语法
>
> *字面量* → [数值字面量](#integer-literal) | [字符串字面量](#string-literal) | [布尔值字面量](#integer-literal) | [nil 字面量](#integer-literal)
>
> *数值字面量* → **-**<sub>可选</sub> [整数字面量](#integer-literal) | **-**<sub>可选</sub> [浮点数字面量](#floating-point-literal)
>
> *布尔值字面量* → **true** | **false**
>
> *nil 字面量* → **nil**
### 整数字面量{#integer-literal}
*整数字面量Integer Literals* 表示未指定精度的整数值。整数字面量默认用十进制表示;可以加前缀来指定其他的进制。二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。
十进制字面量包含数字 `0` 至 `9`。二进制字面量包含 `0` 和 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F`(大小写均可)。
负整数字面量的表示方式为在整数字面量前加负号 `-`,比如 `-42`。
整型字面面可以使用下划线(`_`)来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,这同样也会被系统所忽略,并不会影响字面量的值。
除非特别指定,整数字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数](../02_language_guide/01_The_Basics.md#integers)。
> 整数字面量语法
>
>
#### integer-literal {#integer-literal}
>
> *整数字面量* → [二进制字面量](#binary-literal)
>
> *整数字面量* → [八进制字面量](#octal-literal)
>
> *整数字面量* → [十进制字面量](#decimal-literal)
>
> *整数字面量* → [十六进制字面量](#hexadecimal-literal)
>
>
#### binary-literal {#binary-literal}
>
> *二进制字面量* → **0b** [二进制数字](#binary-digit) [二进制字面量字符组](#binary-literal-characters)<sub>可选</sub>
>
>
#### binary-digit {#binary-digit}
>
> *二进制数字* → 数值 0 或 1
>
> *二进制字面量字符* → [二进制数字](#binary-digit) | **_**
>
>
#### binary-literal-characters {#binary-literal-characters}
>
> *二进制字面量字符组* → [二进制字面量字符](#binary-literal-character) [二进制字面量字符组](#binary-literal-characters)<sub>可选</sub>
>
>
#### octal-literal {#octal-literal}
>
> *八进制字面量* → **0o** [八进字数字](#octal-digit) [八进制字符组](#octal-literal-characters)<sub>可选</sub>
>
>
#### octal-digit {#octal-digit}
>
> *八进字数字* → 数值 0 到 7
>
> *八进制字符* → [八进字数字](#octal-digit) | **_**
>
>
#### octal-literal-characters {#octal-literal-characters}
>
> *八进制字符组* → [八进制字符](#octal-literal-character) [八进制字符组](#octal-literal-characters)<sub>可选</sub>
>
>
#### decimal-literal {#decimal-literal}
>
> *十进制字面量* → [十进制数字](#decimal-digit) [十进制字符组](#decimal-literal-characters)<sub>可选</sub>
>
>
#### decimal-digit {#decimal-digit}
>
> *十进制数字* → 数值 0 到 9
>
>
#### decimal-literal-characters {#decimal-literal-characters}
>
> *十进制数字组* → [十进制数字](#decimal-digit) [十进制数字组](#decimal-literal-characters)<sub>可选</sub>
>
> *十进制字符* → [十进制数字](#decimal-digit) | **_**
>
> *十进制字符组* → [十进制字符](#decimal-literal-characters) [十进制字符组](#decimal-literal-characters)<sub>可选</sub>
>
>
#### hexadecimal-literal {#hexadecimal-literal}
>
> *十六进制字面量* → **0x** [十六进制数字](#hexadecimal-digit) [十六进制字面量字符组](#hexadecimal-literal-characters)<sub>可选</sub>
>
>
#### hexadecimal-digit {#hexadecimal-digit}
>
> *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F
>
> *十六进制字符* → [十六进制数字](#hexadecimal-digit) | **-**
>
>
#### hexadecimal-literal-characters {#hexadecimal-literal-characters}
>
> *十六进制字面量字符组* → [十六进制字符](#hexadecimal-literal-characters) [十六进制字面量字符组](#hexadecimal-literal-characters)<sub>可选</sub>
### 浮点数字面量{#floating-point-literal}
*浮点数字面量Floating-point literals*表示未指定精度浮点数的值。
浮点数字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。
十进制浮点数字面量由十进制数字串后跟十进制小数部分或十进制指数部分(或两者皆有)组成。十进制小数部分由小数点(`.`)后跟十进制数字串组成。指数部分由大写或小写字母 `e` 为前缀后跟十进制数字串组成,这串数字表示 `e` 之前的数值乘以 10 的几次方。例如:`1.25e2` 表示 1.25 x 10²也就是 `125.0`。同样,`1.25e2` 表示 1.25 x 10¯²也就是 `0.0125`。
十六进制浮点数字面量由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 为前缀后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 15 x 2²也就是 `60`。同样,`0xFp-2` 表示 15 x 2¯²也就是 `3.75`。
负数的浮点数字面量由负号(`-`)和浮点数字面量组成,例如 `-42.5`。
浮点数字面量允许使用下划线(`_`)来增强数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。
除非特别指定,浮点数字面量的默认推导类型为 Swift 标准库类型中的 `Double`,表示 64 位浮点数。Swift 标准库也定义了 `Float` 类型,表示 32 位浮点数。
> 浮点数字面量语法
>
>
#### floating-point-literal {#floating-point-literal}
>
> *浮点数字面量* → [十进制字面量](#decimal-literal) [十进制分数](#decimal-fraction)<sub>可选</sub> [十进制指数](#decimal-exponent)<sub>可选</sub>
>
> *浮点数字面量* → [十六进制字面量](#hexadecimal-literal) [十六进制分数](#hexadecimal-fraction)<sub>可选</sub> [十六进制指数](#hexadecimal-exponent)
>
>
#### decimal-fraction {#decimal-fraction}
>
> *十进制分数* → **.** [十进制字面量](#decimal-literal)
>
>
#### decimal-exponent {#decimal-exponent}
>
> *十进制指数* → [十进制指数 e](#floating-point-e) [正负号](#sign)<sub>可选</sub> [十进制字面量](#decimal-literal)
>
>
#### hexadecimal-fraction {#hexadecimal-fraction}
>
> *十六进制分数* → **.** [十六进制数字](#hexadecimal-digit) [十六进制字面量字符组](#hexadecimal-literal-characters)<sub>可选</sub>
>
>
#### hexadecimal-exponent {#hexadecimal-exponent}
>
> *十六进制指数* → [十六进制指数 p](#floating-point-p) [正负号](#sign)<sub>可选</sub> [十进制字面量](#decimal-literal)
>
>
#### floating-point-e {#floating-point-e}
>
> *十进制指数 e* → **e** | **E**
>
>
#### floating-point-p {#floating-point-p}
>
> *十六进制指数 p* → **p** | **P**
>
>
#### sign {#sign}
>
> *正负号* → **+** | **-**
### 字符串字面量 {#string-literal}
字符串字面量是被引号包括的一串字符组成。单行字符串字面量是被包在双引号中的一串字符组成,形式如下:
> "`字符`"
字符串字面量中不能包含未转义的双引号(`"`)、未转义的反斜线(`\`)、回车符、换行符。
多行字符串字面量被包在三个双引号中的一串字符组成,形式如下:
> """
> `字符`
> """
与单行字符串字面量不同的是,多行字符串字面量可以包含不转义的双引号("),回车以及换行。它不能包含三个未转义的连续双引号。
`"""` 之后的回车或者换行开始多行字符串字面量,它们不是字符串的一部分。结束部分的 `"""` 以及它之前的回车或者换行,也不是字符串的一部分。要让多行字符串字面量的开始或结束带有换行,就在第一行或者最后一行写一个空行。
多行字符串字面量可以使用任何空格或制表符组合进行缩进;这些缩进不会包含在字符串中。`"""` 的结束符号决定了缩进:字面量中的每一个非空行开头都必须与结束符 `"""` 之前出现的缩进完全一致;空格和制表符不会被转换。你可以在缩进后包含额外的空格和制表符;这些空格和制表符会在字符串中出现。
多行字符串字面量中的一行结束使用规范化的换行符号。尽管你的源代码混用了回车和换行符,字符串中所有的行结束都必须一样.
在多行字符串字面量里,在行末用反斜线(`\`)可以省略字符串行间中断。反斜线和换行符之间的空白也将被忽略。你可以在你的代码里用这种语法硬包裹多行字符串字面量,而不改变结果字符串的值。
可以在字符串字面量中使用的转义特殊符号如下:
* 空字符 `\0`
* 反斜线 `\\`
* 水平制表符 `\t`
* 换行符 `\n`
* 回车符 `\r`
* 双引号 `\"`
* 单引号 `\'`
* Unicode 标量 `\u{`n`}`n 为一到八位的十六进制数字
字符串字面量允许在反斜杠(`\`)后的括号 `()` 中插入表达式的值。插入表达式可以包含字符串字面量,但不能包含未转义的反斜线(`\`)、回车符以及换行符。
例如,以下所有字符串字面量的值都是相同的:
```swift
"1 2 3"
"1 2 \("3")"
"1 2 \(3)"
"1 2 \(1 + 2)"
let x = 3; "1 2 \(x)"
```
由扩展分隔符包裹的字符串,它是由引号以及成对出现的数字符号(`#`)包裹的字符串序列。由扩展分隔符包裹的字符串形式如下所示:
> \#"`characters`"#
>
> \#"""
>
> `characters`
>
> """#
特殊字符在被扩展分隔符分隔的结果字符串中会展示为普通字符,而不是特殊字符。你可以使用扩展分隔符来创建一些通常情况下具有特殊效果的字符串。例如,生成字符串插值,启动转义序列或终止字符串。
以下所示,由字符串字面量和扩展分隔符所创建的字符串是等价的:
```swift
let string = #"\(x) \ " \u{2603}"#
let escaped = "\\(x) \\ \" \\u{2603}"
print(string)
// Prints "\(x) \ " \u{2603}"
print(string == escaped)
// Prints "true"
```
如果在一个字符串中使用多对扩展分隔符,请不要在分隔符之间使用空格。
```swift
print(###"Line 1\###nLine 2"###) // OK
print(# # #"Line 1\# # #nLine 2"# # #) // Error
```
使用扩展分隔符创建的多行字符串字面量与普通多行字符串字面量具有相同的缩进要求。
字符串字面量的默认推导类型为 `String`。更多有关 `String` 类型的信息请参考 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 以及 [*字符串结构参考*](https://developer.apple.com/documentation/swift/string)。
`` 操作符连接的字符型字面量是在编译时进行连接的。比如下面的 `textA``textB` 是完全一样的,`textA` 没有任何运行时的连接操作。
```swift
let textA = "Hello " + "world"
let textB = "Hello world"
```
> 字符串字面量语法
>
> *字符串字面量* → [静态字符串字面量](#static-string-literal) | [插值字符串字面量](#interpolated-string-literal)
>
> *字符串开分隔定界符* → [字符串扩展分隔符](#extended-string-literal-delimiter) **"**
>
> *字符串闭分隔定界符* → **"** [字符串扩展分隔符](#extended-string-literal-delimiter)<sub>可选</sub>
>
>
#### static-string-literal {#static-string-literal}
>
> *静态字符串字面量* → [字符串开分隔定界符](#extended-string-literal-delimiter) [引用文本](#quoted-text)<sub>可选</sub> [字符串闭分隔定界符](#extended-string-literal-delimiter)
>
> *静态字符串字面量* → [多行字符串开分隔定界符](#extended-string-literal-delimiter) [多行引用文本](#multiline-quoted-text)<sub>可选</sub> [多行字符串闭分隔定界符](#extended-string-literal-delimiter)
>
> *多行字符串开分隔定界符* → [字符串扩展分隔符](#extended-string-literal-delimiter) **"""**
>
> *多行字符串闭分隔定界符* → **"""** [字符串扩展分隔符](#extended-string-literal-delimiter)
>
>
#### extended-string-literal-delimiter {#extended-string-literal-delimiter}
>
> *字符串扩展分隔符* → **#** [字符串扩展分隔符](#extended-string-literal-delimiter)<sub>可选</sub>
>
>
#### quoted-text {#quoted-text}
>
> *引用文本* → [引用文本项](#quoted-text-item) [引用文本](#quoted-text)<sub>可选</sub>
>
>
#### quoted-text-item {#quoted-text-item}
>
> *引用文本项* → [转义字符](#escaped-character)
>
> *引用文本项* → 除了 **"**、**\\**、U+000A、U+000D 以外的所有 Unicode 字符
>
>
#### multiline-quoted-text {#multiline-quoted-text}
>
> *多行引用文本* → [多行引用文本项](#multiline-quoted-text-item) [多行引用文本](#multiline-quoted-text)<sub>可选</sub>
>
>
#### multiline-quoted-text-item {#multiline-quoted-text-item}
>
> *多行引用文本项* → [转义字符](#escaped-character)<sub>可选</sub>
>
#### multiline-quoted-text {#multiline-quoted-text}
>
> *多行引用文本* → 除了 **\\** 以外的任何 Unicode 标量值
>
> *多行引用文本* → [转义换行](#escaped-newline)
>
>
#### interpolated-string-literal {#interpolated-string-literal}
>
> *插值字符串字面量* → [字符串开分隔定界符](#extended-string-literal-delimiter) [插值文本](#interpolated-text)<sub>可选</sub> [字符串闭分隔定界符](#extended-string-literal-delimiter)
>
> *插值字符串字面量* → [多行字符串开分隔定界符](#extended-string-literal-delimiter) [插值文本](#interpolated-text)<sub>可选</sub> [多行字符串闭分隔定界符](#extended-string-literal-delimiter)
>
>
#### interpolated-text {#interpolated-text}
>
> *插值文本* → [插值文本项](#interpolated-text-item) [插值文本](#interpolated-text)<sub>可选</sub>
>
>
#### interpolated-text-item {#interpolated-text-item}
>
> *插值文本项* → **\\(**[ 表达式 ](./04_Expressions.md)**)** | [引用文本项](#quoted-text-item)
>
> *多行插值文本* → [多行插值文本项](#multiline-quoted-text-item) [多行插值文本](#multiline-quoted-text)<sub>可选</sub>
>
> *多行插值文本项* → **\\(** [表达式](./04_Expressions.md) **)** | [多行引用文本项](#multiline-quoted-text-item)
>
>
#### escape-sequence {#escape-sequence}
>
> *转义序列* → **\\** [字符串扩展分隔符](#extended-string-literal-delimiter)
>
>
#### escaped-character {#escaped-character}
>
> *转义字符* → [转义序列](#escape-sequence) **0** | [转义序列](#escape-sequence) **\\** | [转义序列](#escape-sequence) **t** | [转义序列](#escape-sequence) **n** | [转义序列](#escape-sequence) **r** | [转义序列](#escape-sequence) **\"** | [转义序列](#escape-sequence) **'**
>
> *转义字符* → [转义序列](#escape-sequence) **u {** [unicode 标量数字](#unicode-scalar-digits) **}**
>
>
#### unicode-scalar-digits {#unicode-scalar-digits}
>
> *unicode 标量数字* → 一到八位的十六进制数字
>
>
#### escaped-newline {#escaped-newline}
>
> *转义换行符* → [转义序列](#escape-sequence) [空白](#whitespace)<sub>可选</sub> [断行符](#line-break)
### 正则表达式字面量 {#regular-expression-literals}
正则表达式字面量是被符号 `/` 以如下形式包围的一串字符:
```swift
/ 正则表达式 /
```
正则表达式字面量不能以未转义的制表符或空格开头,且正则表达式字面量内不能包含未转义的 `/` 符号、回车或是换行符。
在正则表达式字面量内部,反斜杠符号 (`/`) 被视作正则表达式的一部分,而非如字符串表达式中一样被视为一个转义符。反斜杠符号提示其后的特殊字符应当按照其字面义进行解读,其后的非特殊字符则应按照特殊方式进行解读。例如,`/\(/` 匹配了一个左括号 (`(`) 而 `/\d/` 匹配了一个单个数字。
被一对反斜杠 `/` 和一对称数字符 `#` 包围的一串字符被称为由扩展分隔符分割的正则表达式字面量,它有以下两种形式:
```swift
`#/ 正则表达式 /#`
```
```swift
`#/
正则表达式
/#`
```
由扩展分隔符分割的正则表达式字面量可以以未转义的制表符或是空格开头,也可以包含未转义的反斜杠符号 (`/`) 并占据多行代码。若需要占用多行,正则表达式字面量开头的扩展分隔符 `#` 应位于行末,末尾的扩展分隔符 `#` 应独占一行。在多行正则表达式字面量内默认启用了扩展正则表达式的语法规则——尤其是关于忽略空格并允许注释的部分。
如果你使用多个数字符号(`#`)来形成扩展分隔符分隔的正则表达式,数字符号不应使用空格分隔。
```swift
let regex1 = ##/abc/## //正常运行
let regex2 = # #/abc/# # //报错
```
若需要创建一个空白的正则表达式字面量,必须使用扩展分隔符语法。
> 正则表达式字面量语法
>
> 正则表达式字面量 → [正则表达式字面量开分隔定界符](#regular-expression-literal-opening-delimiter) [正则表达式字面量](#regular-expression) [正则表达式字面量闭分隔定界符](#regular-expression-literal-closing-delimiter)
>
#### 正则表达式字面量 {#regular-expression}
>
> 正则表达式字面量 → 任何正则表达式
>
#### 正则表达式字面量开分隔定界符 {#regular-expression-literal-opening-delimiter}
>
> 正则表达式字面量开分隔定界符 → [扩展正则表达式分隔符](#extended-regular-expression-literal-delimiter)<sub>可选</sub> **/**
>
#### 正则表达式字面量闭分隔定界符 {#regular-expression-literal-closing-delimiter}
>
> 正则表达式字面量闭分隔定界符 → **/** [扩展正则表达式分隔符](#extended-regular-expression-literal-delimiter)<sub>可选</sub>
>
#### 扩展正则表达式分隔符 {#extended-regular-expression-literal-delimiter}
>
> 扩展正则表达式分隔符 → **#** [扩展正则表达式分隔定界符](#extended-regular-expression-literal-delimiter)<sub>可选</sub>
>
## 运算符 {#operator}
Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](../02_language_guide/02_Basic_Operators.md) 和 [高级运算符](../02_language_guide/27_Advanced_Operators.md) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。
自定义运算符可以由以下其中之一的 ASCII 字符 `/`、`=`、`-`、`+`、`!`、`*`、`%`、`<`、`>`、`&`、`|`、`^`、`?` 以及 `~`,或者后面语法中规定的任一个 Unicode 字符(其中包含了*数学运算符*、*零散符号Miscellaneous Symbols* 以及*印刷符号Dingbats*之类的 Unicode 块)开始。在第一个字符之后,允许使用组合型 Unicode 字符。
您也可以以点号(`.`)开头来定义自定义运算符。这些运算符可以包含额外的点。例如 `.+.` 会被看作一个单独的运算符。如果某个运算符不是以点号开头的,那么它就无法再包含另外的点号了。例如,`+.+` 就会被看作为一个 `+` 运算符后面跟着一个 `.+` 运算符。
虽然您可以用问号 `?` 来自定义运算符,但是这个运算符不能只包含单独的一个问号。此外,虽然运算符可以包含一个惊叹号 `!`,但是前缀运算符不能够以问号或者惊叹号开头。
> 注意
>
> 以下这些标记 `=`、`->`、`//`、`/*`、`*/`、`.`,前缀运算符 `<`、`&` 和 `?`,中缀运算符 `?`,后缀运算符 `>`、`!` 和 `?` 是被系统保留的。这些符号不能被重载,也不能用作自定义运算符。
运算符两侧的空白被用来区分该运算符是否为前缀运算符、后缀运算符或二元运算符。规则总结如下:
* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+++b` 和 `a +++ b` 当中的 `+++` 运算符会被看作二元运算符。
* 如果运算符只有左侧空白,将被看作一元前缀运算符。例如 `a +++b` 中的 `+++` 运算符会被看做是一元前缀运算符。
* 如果运算符只有右侧空白,将被看作一元后缀运算符。例如 `a+++ b` 中的 `+++` 运算符会被看作是一元后缀运算符。
* 如果运算符左侧没有空白并紧跟 `.`,将被看作一元后缀运算符。例如 `a+++.b` 中的 `+++` 运算符会被视为一元后缀运算符(即上式被视为 `a+++ .b` 而不是 `a +++ .b`)。
鉴于这些规则,`(`、`[` 和 `{` 是在运算符前面,`)`、`]` 和 `}` 是在运算符后面,以及字符 `,`、`;` 和 `:` 都被视为空白。
以上规则需注意一点。如果预定义运算符 `!` 或 `?` 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将 `?` 用作可选链式调用运算符,左侧必须无空白。如果用于条件运算符 `? :`,必须两侧都有空白。
如果中缀操作符的参数之一是正则表达式字面量,则操作符两侧都必须有空格。
在某些特定的设计中,以 `<` 或 `>` 开头的运算符会被分离成两个或多个符号。剩余部分可能会以同样的方式被再次分离。因此,在 `Dictionary<String, Array<Int>>` 中没有必要添加空白来消除闭合字符 `>` 的歧义。在这个例子中,闭合字符 `>` 不会被视为单独的符号,因而不会被错误解析为 `>>` 运算符。
要学习如何自定义运算符,请参考 [自定义运算符](../02_language_guide/27_Advanced_Operators.md#custom-operators) 和 [运算符声明](./06_Declarations.md#operator-declaration)。要学习如何重载运算符,请参考 [运算符函数](../02_language_guide/27_Advanced_Operators.md#operator-functions)。
> 运算符语法
>
> *运算符* → [头部运算符](#operator-head) [运算符字符组](#operator-characters)<sub>可选</sub>
>
> *运算符* → [头部点运算符](#dot-operator-head) [点运算符字符组](#dot-operator-characters)
>
>
#### operator-head {#operator-head}
>
> *头部运算符* → **/** | **=** | **-** | **+** | **!** | __*__ | **%** | **<** | **>** | **&** | **|** | **^** | **~** | **?**
>
> *头部运算符* → U+00A1U+00A7
>
> *头部运算符* → U+00A9 或 U+00AB
>
> *头部运算符* → U+00AC 或 U+00AE
>
> *头部运算符* → U+00B0U+00B1
>
> *头部运算符* → U+00B6U+00BBU+00BFU+00D7或 U+00F7
>
> *头部运算符* → U+2016U+2017
>
> *头部运算符* → U+2020U+2027
>
> *头部运算符* → U+2030U+203E
>
> *头部运算符* → U+2041U+2053
>
> *头部运算符* → U+2055U+205E
>
> *头部运算符* → U+2190U+23FF
>
> *头部运算符* → U+2500U+2775
>
> *头部运算符* → U+2794U+2BFF
>
> *头部运算符* → U+2E00U+2E7F
>
> *头部运算符* → U+3001U+3003
>
> *头部运算符* → U+3008U+3020
>
> *头部运算符* → U+3030
>
>
#### operator-character {#operator-character}
>
> *运算符字符* → [头部运算符](#operator-head)
>
> *运算符字符* → U+0300U+036F
>
> *运算符字符* → U+1DC0U+1DFF
>
> *运算符字符* → U+20D0U+20FF
>
> *运算符字符* → U+FE00U+FE0F
>
> *运算符字符* → U+FE20U+FE2F
>
> *运算符字符* → U+E0100U+E01EF
>
>
#### operator-characters {#operator-characters}
>
> *运算符字符组* → [运算符字符](#operator-character) [运算符字符组](#operator-characters)<sub>可选</sub>
>
>
#### dot-operator-head {#dot-operator-head}
>
> *头部点运算符* → **.**
>
>
#### dot-operator-character {#dot-operator-character}
>
> *点运算符字符* → **.** | [运算符字符](#operator-character)
>
>
#### dot-operator-characters {#dot-operator-characters}
>
> *点运算符字符组* → [点运算符字符](#dot-operator-character) [点运算符字符组](#dot-operator-characters)<sub>可选</sub>
>
> *二元运算符* → [运算符](#operator)
>
> *前缀运算符* → [运算符](#operator)
>
> *后缀运算符* → [运算符](#operator)

View File

@ -1,799 +0,0 @@
# 特性Attributes
在 Swift 中有两种特性,分别用于修饰声明和类型。特性提供了有关声明和类型的更多信息。例如,使用 `discardableResult` 特性声明的函数,表明该函数虽然有返回值,但如果没有使用该返回值,编译器不会产生警告。
您可以通过以下方式指定一个特性,通过符号 `@` 后跟特性的名称和特性接收的任何参数:
@`特性名`
@`特性名`(`特性参数`)
有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰某个特定的声明的。这些_特性的参数_写在圆括号内它们的格式由它们所属的特性来定义。
## 声明特性 {#declaration-attributes}
声明特性只能应用于声明。
### `available` {#available}
`available` 特性用于声明时,表示该声明的生命周期是相对于特定的平台和操作系统版本。
`available` 特性经常与参数列表一同出现,该参数列表至少有两个特性参数,参数之间由逗号分隔。这些参数由以下这些平台名字中的一个起头:
- `iOS`
- `iOSApplicationExtension`
- `macOS`
- `macOSApplicationExtension`
- `macCatalyst`
- `macCatalystApplicationExtension`
- `watchOS`
- `watchOSApplicationExtension`
- `tvOS`
- `tvOSApplicationExtension`
- `swift`
当然,你也可以用一个星号(`*`)来表示上面提到的所有平台。指定 Swift 版本的 `available` 特性参数,不能使用星号表示。
其余的参数,可以按照任何顺序出现,并且可以添加关于声明生命周期的附加信息,包括重要事件。
- `unavailable` 参数表示该声明在指定的平台上是无效的。当指定 Swift 版本可用性时不可使用该参数。
- `introduced` 参数表示指定平台从哪一版本开始引入该声明。格式如下:
`introduced`: `版本号`
*版本号*由一至三个正整数构成,由句点分隔的。
- `deprecated` 参数表示指定平台从哪一版本开始弃用该声明。格式如下:
`deprecated`: `版本号`
可选的*版本号*由一个或多个正整数构成,由句点分隔的。省略版本号表示该声明目前已弃用,而不提供有关弃用发生时间的任何信息。如果你省略了版本号,冒号(`:`)也可省略。
- `obsoleted` 参数表示指定平台或语言从哪一版本开始废弃该声明。当一个声明被废弃后,它就从平台或语言中移除,不能再被使用。格式如下:
`obsoleted`: `版本号`
*版本号*由一至三个正整数构成,由句点分隔的。
- `message` 参数用来提供文本信息。当使用被弃用或者被废弃的声明时,编译器会抛出该信息作为警告或错误。格式如下:
`message`: `信息内容`
_信息内容_由一个字符串构成。
- `renamed` 参数用来提供文本信息,用以表示被重命名声明的新名字。当使用声明的旧名字时,编译器会报错并提示新名字。格式如下:
`renamed`: `新名字`
_新名字_由一个字符串构成。
你可以将带有 `renamed``unavailable` 参数的 `available` 特性应用于类型别名声明,如下所示,来表明框架和库发行版本之间的声明名称已经被更改。这个组合会导致声明已重命名的编译时错误。
```swift
// 首发版本
protocol MyProtocol {
// 这里是协议定义
}
```
```swift
// 后续版本重命名了 MyProtocol
protocol MyRenamedProtocol {
// 这里是协议定义
}
@available(*, unavailable, renamed:"MyRenamedProtocol")
typealias MyProtocol = MyRenamedProtocol
```
你可以在某个声明上使用多个 `available` 特性,以指定该声明在不同平台和 Swift 版本上的可用性。如果当前目标与 `available` 特性中指定的平台或语言版本不匹配时,该声明将会被忽略。如果你使用了多个 `available` 特性,则最终效果是平台和 Swift 可用性的组合。
如果 `available` 特性除了平台名称或者语言版本参数之外,只指定了一个 `introduced` 参数,那么可以使用以下简写语法代替:
@available(`平台名称` `版本号`, *)
@available(swift `版本号`)
`available` 特性的简写语法简洁的表示了多个平台的可用性。尽管这两种形式在功能上是相同的,但请尽可能地使用简写语法形式。
```swift
@available(iOS 10.0, macOS 10.12, *)
class MyClass {
// 这里是类定义
}
```
当需要同时指定 Swift 版本和平台可用性,需要使用一个单独的 `available` 特性来指明 Swift 版本,以及一个或者多个 `available` 特性来声明平台可用性。
```swift
@available(swift 3.0.2)
@available(macOS 10.12, *)
struct MyStruct {
// 这里是结构体定义
}
```
### `discardableResult` {#discardableresult}
该特性用于的函数或方法声明,以抑制编译器中函数或方法被调用而其返回值没有被使用时的警告。
### `dynamicCallable` {#dynamiccallable}
该特性用于类、结构体、枚举或协议,以将该类型的实例视为可调用的函数。该类型必须实现 `dynamicallyCall(withArguments:)``dynamicallyCall(withKeywordArguments:)` 方法之一,或两者同时实现。
你可以调用 `dynamicCallable` 特性的实例,就像是调用一个任意数量参数的函数。
```swift
@dynamicCallable
struct TelephoneExchange {
func dynamicallyCall(withArguments phoneNumber: [Int]) {
if phoneNumber == [4, 1, 1] {
print("Get Swift help on forums.swift.org")
} else {
print("Unrecognized number")
}
}
}
let dial = TelephoneExchange()
// 使用动态方法调用
dial(4, 1, 1)
// 打印“Get Swift help on forums.swift.org”
dial(8, 6, 7, 5, 3, 0, 9)
// 打印“Unrecognized number”
// 直接调用底层方法
dial.dynamicallyCall(withArguments: [4, 1, 1])
```
`dynamicallyCall(withArguments:)` 方法的声明必须至少有一个参数遵循 [`ExpressibleByArrayLiteral`](https://developer.apple.com/documentation/swift/expressiblebyarrayliteral) 协议,如 `[Int]`,而返回值类型可以是任意类型。
如果实现 `dynamicallyCall(withKeywordArguments:)` 方法,则可以在动态方法调用中包含标签。
```swift
@dynamicCallable
struct Repeater {
func dynamicallyCall(withKeywordArguments pairs: KeyValuePairs<String, Int>) -> String {
return pairs
.map { label, count in
repeatElement(label, count: count).joined(separator: " ")
}
.joined(separator: "\n")
}
}
let repeatLabels = Repeater()
print(repeatLabels(a: 1, b: 2, c: 3, b: 2, a: 1))
// a
// b b
// c c c
// b b
// a
```
`dynamicallyCall(withKeywordArguments:)` 方法声明必须只有一个遵循 [`ExpressibleByDictionaryLiteral`](https://developer.apple.com/documentation/swift/expressiblebydictionaryliteral) 协议的参数,返回值可以任意类型。参数的 [`Key`](https://developer.apple.com/documentation/swift/expressiblebydictionaryliteral/2294108-key) 必须遵循 [`ExpressibleByStringLiteral`](https://developer.apple.com/documentation/swift/expressiblebystringliteral) 协议。上述的示例使用 [`KeyValuePairs`](https://developer.apple.com/documentation/swift/keyvaluepairs) 作为参数类型,以便调用者可以传入重复的参数标签,`a``b` 在调用 `repeat`中多次使用。
如果你同时实现两种 `dynamicallyCall` 方法,则当在方法调用中包含关键字参数时,会调用 `dynamicallyCall(withKeywordArguments:)` 方法,否则调用 `dynamicallyCall(withArguments:)` 方法。
你只能调用参数和返回值与 `dynamicallyCall` 方法实现匹配的动态调用实例。在下面示例的调用无法编译,因为其 `dynamicallyCall(withArguments:)` 实现不接受 `KeyValuePairs<String, String>` 参数。
```swift
repeatLabels(a: "four") // Error
```
### `dynamicMemberLookup` {#dynamicmemberlookup}
该特性用于类、结构体、枚举或协议,让其能在运行时查找成员。该类型必须实现 `subscript(dynamicMember:)` 下标。
在显式成员表达式中,如果指定成员没有相应的声明,则该表达式被理解为对该类型的 `subscript(dynamicMember:)` 下标调用,将有关该成员的信息作为参数传递。下标接收参数既可以是键路径,也可以是成员名称字符串;如果你同时实现这两种方式的下标调用,那么以键路径参数方式为准。
`subscript(dynamicMember:)` 实现允许接收 [`KeyPath`](https://developer.apple.com/documentation/swift/keypath)[`WritableKeyPath`](https://developer.apple.com/documentation/swift/writablekeypath) 或 [`ReferenceWritableKeyPath`](https://developer.apple.com/documentation/swift/referencewritablekeypath) 类型的键路径参数。它可以使用遵循 [`ExpressibleByStringLiteral`](https://developer.apple.com/documentation/swift/expressiblebystringliteral) 协议的类型作为参数来接受成员名 -- 通常情况下是 `String`。下标返回值类型可以为任意类型。
按成员名进行的动态成员查找可用于围绕编译时无法进行类型检查的数据创建包装类型,例如在将其他语言的数据桥接到 `Swift` 时。例如:
```swift
@dynamicMemberLookup
struct DynamicStruct {
let dictionary = ["someDynamicMember": 325,
"someOtherMember": 787]
subscript(dynamicMember member: String) -> Int {
return dictionary[member] ?? 1054
}
}
let s = DynamicStruct()
// 使用动态成员查找
let dynamic = s.someDynamicMember
print(dynamic)
// 打印“325”
// 直接调用底层下标
let equivalent = s[dynamicMember: "someDynamicMember"]
print(dynamic == equivalent)
// 打印“true”
```
根据键路径来动态地查找成员,可用于创建一个包裹数据的包装类型,该类型支持在编译时期进行类型检查。例如:
```swift
struct Point { var x, y: Int }
@dynamicMemberLookup
struct PassthroughWrapper<Value> {
var value: Value
subscript<T>(dynamicMember member: KeyPath<Value, T>) -> T {
get { return value[keyPath: member] }
}
}
let point = Point(x: 381, y: 431)
let wrapper = PassthroughWrapper(value: point)
print(wrapper.x)
```
### `frozen` {#frozen}
针对枚举或者结构体的声明使用该特性,可以限制你对该类型的修改。它只有在编译迭代库时被允许使用。未来版本的库不能通过添加、删除或重新排序枚举的 case 或结构的存储实例属性来更改声明。在未冻结的类型上,这些操作都是允许的,但是他们破坏了冻结类型的 ABI 兼容性。
> 注意
> 当编译器不处于迭代库的模式,所有的结构体和枚举都是隐性冻结,并且该特性会被忽视。
在迭代库的模式中,与未冻结结构体和枚举的成员进行交互的代码在被编译时,允许它在不重新编译的情况下继续工作,即使在新版本的库中添加、删除或重新排序该类型的成员。编译器用类似运行时查找信息和添加间接层的技术使之可能。将一个枚举或者结构体标记为冻结将以放弃这种灵活性为代价来获取性能上的提升:未来版本的库只能对类型进行有限的更改,但编译器可以对与类型成员交互的代码进行额外的优化。
使用冻结类型,结构体存储属性的类型以及枚举 case 的关联值必须是 `public` 或使用 `usablefrominline` 特性标记。冻结结构体的属性不能有属性观察器,为存储实例属性提供初始值的表达式必须遵循与 `inlinable` 函数相同的限制,如 [`inlinable`](#inlinable) 中所述。
要在命令行上启用迭代库模式,请将 `-enable-library-evolution` 选项传递给 Swift 编译器。要在 Xcode 中支持它,则将生成设置 “Build Libraries for Distribution”`BUILD_LIBRARY_FOR_DISTRIBUTION`)设置为 Yes详情查看 [`Xcode 帮助文档`](https://help.apple.com/xcode/mac/current/#/dev04b3a04ba)。
针对冻结枚举的 switch 语法,不需要 `default` case就像 [`对未来枚举的 case 进行 switch`](./05_Statements.md#future-case)。在针对冻结枚举使用 switch 语法时包含 `default``@unknown default` case 将生成警告,因为该代码永远不会执行。
### `GKInspectable` {#gkinspectable}
应用此特性可将自定义 GameplayKit 组件属性公开到 SpriteKit 编辑器 UI。应用此特性同时表示应用了 `objc` 特性。
### `inlinable` {#inlinable}
该特性用于函数、方法、计算属性、下标、便利构造器或析构器的声明,以将该声明的实现公开为模块公开接口的一部分。编译器允许在调用处把 `inlinable` 标记的符号替换为符号实现的副本。
内联代码可以与任意模块中 `public` 访问级别的符号进行交互,同时可以与在相同模块中标记 `usableFromInline` 特性的 `internal` 访问级别的符号进行交互。内联代码不能与 `private``fileprivate` 级别的符号进行交互。
该特性不能用于嵌套在函数内的声明,也不能用于 `fileprivate``private` 访问级别的声明。在内联函数内定义的函数和闭包都是隐式内联的,即使他们不能标记该特性。
### `main` {#main}
将该特性用于结构体、类,或枚举的声明,表示它包含了程序流的顶级入口。类型必须提供一个不接收任何参数,且返回值为 `Void``main` 类型函数。如下所示:
```swift
@main
struct MyTopLevel {
static func main() {
// 顶级代码从这里开始
}
}
```
换一个说法,应用了 `main` 特性的类型所必须满足的条件,与遵循下面这个假想的协议一样:
```swift
protocol ProvidesMain {
static func main() throws
}
```
编译生成可执行程序的 Swift 代码最多只能拥有一个顶级代码入口,请参阅[顶级代码](../03_language_reference/06_Declarations.md#top-level-code)。
### `nonobjc` {#nonobjc}
针对方法、属性、下标、或构造器的声明使用该特性将覆盖隐式的 `objc` 特性。`nonobjc` 特性告诉编译器该声明不能在 Objective-C 代码中使用,即便它能在 Objective-C 中表示。
该特性用在扩展中,与在没有明确标记为 `objc` 特性的扩展中给每个成员添加该特性具有相同效果。
可以使用 `nonobjc` 特性解决标有 `objc` 的类中桥接方法的循环问题,该特性还允许对标有 `objc` 的类中的构造器和方法进行重载。
标有 `nonobjc` 特性的方法不能重写标有 `objc` 特性的方法。然而,标有 `objc` 特性的方法可以重写标有 `nonobjc` 特性的方法。同样,标有 `nonobjc` 特性的方法不能满足标有 `@objc` 特性的协议中的方法要求。
### `NSApplicationMain` {#nsapplicationmain}
在类上使用该特性表示该类是应用程序委托类。使用该特性与调用 `NSApplicationMain(_:_:)` 函数的效果相同。
如果你不想使用这个特性,可以提供一个 `main.swift` 文件,并在代码顶层调用 `NSApplicationMain(_:_:)` 函数,如下所示:
```swift
import AppKit
NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
```
编译生成可执行程序的 Swift 代码最多只能拥有一个顶级代码入口,请参阅[顶级代码](../03_language_reference/06_Declarations.md#top-level-code)。
### `NSCopying` {#nscopying}
该特性用于修饰一个类的存储型变量属性。该特性将使属性的设值方法使用传入值的`副本`进行赋值,这个值由传入值的 `copyWithZone(_:)` 方法返回,而不是传入值本身。该属性的类型必需符合 `NSCopying` 协议。
`NSCopying` 特性的行为与 Objective-C 中的 `copy` 属性特性相似。
### `NSManaged` {#nsmanaged}
该特性用于修饰 `NSManagedObject` 子类中的实例方法或存储型变量属性,表明它们的实现由 `Core Data` 在运行时基于相关实体描述动态提供。对于标记了 `NSManaged` 特性的属性,`Core Data` 也会在运行时为其提供存储。应用这个特性也意味着 `objc` 特性。
### `objc` {#objc}
该特性用于修饰任何可以在 Objective-C 中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限原始值为整型的枚举)、类的属性和方法(包括存取方法)、协议以及协议中的可选成员、构造器以及下标运算符。`objc` 特性告诉编译器这个声明可以在 Objective-C 代码中使用。
该特性用在扩展中,与在没有明确标记为 `nonobjc` 特性的扩展中给每个成员添加该特性具有相同效果。
编译器隐式地将 `objc` 特性添加到 Objective-C 中定义的任何类的子类。但是,子类不能是泛型的,并且不能继承于任何泛型类。你可以将 `objc` 特性显式添加到满足这些条件的子类,来指定其 Objective-C 名称,如下所述。添加了 `objc` 的协议不能继承于没有此特性的协议。
在以下情况中同样会隐式的添加 `objc` 特性:
- 父类有 `objc` 特性,且重写为子类的声明。
- 遵循带有 `objc` 特性协议的声明。
- 带有 `IBAction``IBSegueAction``IBOutlet``IBDesignable``IBInspectable``NSManaged``GKInspectable` 特性的声明。
如果你将 `objc` 特性应用于枚举,每一个枚举 case 都会以枚举名称和 case 名称组合的方式暴露在 Objective-C 代码中。实例名称的首字母大写。例如,在 Swift 枚举类型 `Planet` 中有一个名为 `Venus` 的 case该 case 暴露在 Objective-C 代码中时叫做 `PlanetVenus`
`objc` 特性可以接受一个可选的特性参数,由标识符构成。当你想把 `objc` 所修饰的实体以一个不同的名字暴露给 Objective-C 时,你就可以使用这个特性参数。你可以使用这个参数来命名类、枚举类型、枚举 case、协议、方法、存取方法以及构造器。如果你要指定类、协议或枚举在 Objective-C 下的名称,请在名称上包含三个字母的前缀,就像 [Objective-C 编程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html#//apple-ref/doc/uid/TP40011210) 中的 [约定](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Conventions/Conventions.html#//apple-ref/doc/uid/TP40011210-CH10-SW1)描述的一样。下面的例子把 `ExampleClass` 中的 `enabled` 属性的取值方法暴露给 Objective-C名字是 `isEnabled`,而不是它原来的属性名。
```swift
class ExampleClass: NSObject {
@objc var enabled: Bool {
@objc(isEnabled) get {
// 返回适当的值
}
}
}
```
更多相关信息,请参考 [把 Swift 导入 Objective-C](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_swift_into_objective-c)。
> 注意
> 具有 `objc` 特性的实参也会改变那个声明的运行时名称。在调用与 Objective-C 运行时交互的函数时,比如 [NSClassFromString](https://developer.apple.com/documentation/foundation/1395135-nsclassfromstring),以及在应用程序的 info.plist 文件中指定类名时,你会用到运行时名称。如果你通过传递实参的方式来指定名称,这个名称会作为 Objective-C 代码中的名称和运行时名称。如果你不使用这个实参,在 Objective-C 代码中使用的名称会与 Swift 代码中的名称匹配,并且运行时名称会遵循标准 Swift 编译器名称管理的惯例。
### `objcMembers` {#objcmembers}
该特性用于类声明,以将 `objc` 特性应用于该类、扩展、子类以及子类的扩展的所有 Objective-C 兼容成员。
大多数代码应该使用 `objc` 特性,以暴露所需的声明。如果需要暴露多个声明,可以将其分组到添加 `objc` 特性的扩展中。`objcMembers` 特性为大量使用 Objective-C 运行时的内省工具的库提供了便利。添加不必要的 `objc` 特性会增加二进制体积并影响性能。
### `propertyWrapper` {#propertywrapper}
在类、结构体或者枚举的声明时使用该特性,可以让其成为一个属性包装器。如果将该特性应用在一个类型上,将会创建一个与该类型同名的自定义特性。将这个新的特性用于类、结构体、枚举的属性,则可以通过包装器的实例封装对该属性的访问;本地存储变量声明也能利用这个特性完成对它的访问封装。局部和全局变量不能使用属性包装器。
包装器必须定义一个 `wrappedValue` 实例属性。该属性 _wrapped value_ 是该属性存取方法暴露的值。大多数时候,`wrappedValue` 是一个计算属性,但它可以是一个存储属性。包装器负责定义和管理其包装值所需的任何底层存储。编译器通过在包装属性的名称前加下划线(`_`)来为包装器的实例提供同步存储。例如,`someProperty` 的包装器存储为 `_someProperty`。包装器的同步存储具有 `private` 的访问控制级别。
拥有属性包装器的属性可以包含 `willSet``didSet` 闭包,但是不能重写编译器合成的 `get``set` 闭包。
Swift 为属性包装器的构造函数提供了两种形式的语法糖。可以在包装值的定义中使用赋值语法,将赋值语句右侧的表达式作为值传递给属性包装器构造函数中的 `wrappedValue` 参数。同样的,你也可以为包装器提供一些参数,这些参数将会传递给包装器的构造函数。就像下面的例子,`SomeStruct` 中,定义 `SomeWrapper` 的地方各自调用了对应的构造函数。
```swift
@propertyWrapper
struct SomeWrapper {
var wrappedValue: Int
var someValue: Double
init() {
self.wrappedValue = 100
self.someValue = 12.3
}
init(wrappedValue: Int) {
self.wrappedValue = wrappedValue
self.someValue = 45.6
}
init(wrappedValue value: Int, custom: Double) {
self.wrappedValue = value
self.someValue = custom
}
}
struct SomeStruct {
// 使用 init()
@SomeWrapper var a: Int
// 使用 init(wrappedValue:)
@SomeWrapper var b = 10
// 两个都是使用 init(wrappedValue:custom:)
@SomeWrapper(custom: 98.7) var c = 30
@SomeWrapper(wrappedValue: 30, custom: 98.7) var d
}
```
属性包装器中 _projected value_ 是它可以用来暴露额外功能的第二个值。属性包装器的作者负责确认其映射值的含义并定义公开映射值的接口。若要通过属性包装器来映射值,请在包装器的类型上定义 `projectedValue` 实例属性。编译器通过在包装属性的名称前面加上美元符号(`$`)来合成映射值的标识符。例如,`someProperty` 的映射值是 `$someProperty`。映射值具有与原始包装属性相同的访问控制级别。
```swift
@propertyWrapper
struct WrapperWithProjection {
var wrappedValue: Int
var projectedValue: SomeProjection {
return SomeProjection(wrapper: self)
}
}
struct SomeProjection {
var wrapper: WrapperWithProjection
}
struct SomeStruct {
@WrapperWithProjection var x = 123
}
let s = SomeStruct()
s.x // Int value
s.$x // SomeProjection value
s.$x.wrapper // WrapperWithProjection value
```
### `resultBuilder` {#result-builder}
将该特性应用于类、结构体或者枚举可以把它作为结果构造器使用。结果构造器能按顺序构造一组嵌套的数据结构。利用它可以以一种自然的声明式语法为嵌套数据结构实现一套领域专门语言DSL。想了解如何使用 `resultBuild`,请参阅 [结果构造器](../02_language_guide/27_Advanced_Operators.md#result-builders)。
#### 结果构造方法 {#result-building-methods}
结果构造器实现了下面的静态方法。它所有的功能已经通过静态方法暴露了,因此不需要初始化一个实例。`buildBlock(_:)` 方法是必须实现的而那些能让领域专门语言DSL拥有额外能力的方法则是可选的。事实上结果构造器类型的声明不需要遵循任何协议。
这些静态方法的描述中用到了三种类型。`Expression` 为构造器的输入类型,`Component` 为中间结果类型,`FinalResult` 为构造器最终生成结果的类型。将它们替换成你构造器所使用的类型。如果结果构造方法中没有明确 `Expression``FinalResult`,默认会使用 `Component` 的类型。
结果构造方法如下:
- `static func buildBlock(_ components: Component...) -> Component`
将可变数量的中间结果合并成一个中间结果。必须实现这个方法。
- `static func buildOptional(_ component: Component?) -> Component`
将可选的中间结果构造成新的中间结果。用于支持不包含 `else` 闭包的 `if` 表达式。
- `static func buildEither(first: Component) -> Component`
构造一个由条件约束的中间结果。同时实现它与 `buildEither(second:)` 来支持 `switch` 与包含 `else` 闭包的 `if` 表达式。
- `static func buildEither(second: Component) -> Component`
构造一个由条件约束的中间结果。同时实现它与 `buildEither(first:)` 来支持 `switch` 与包含 `else` 闭包的 `if` 表达式。
- `static func buildArray(_ components: [Component]) -> Component`
将中间结果数组构造成中间结果。用于支持 `for` 循环。
- `static func buildExpression(_ expression: Expression) -> Component`
将表达式构造成中间结果。利用它来执行预处理,比如,将入参转换为内部类型,或为使用方提供额外的类型推断信息。
- `static func buildFinalResult(_ component: Component) -> FinalResult`
将中间结果构造成最终结果。可以在中间结果与最终结果类型不一致的结果构造器中实现这个方法,或是在最终结果返回前对它做处理。
- `static func buildLimitedAvailability(_ component: Component) -> Component`
构建中间结果用于在编译器控制compiler-control语句进行可用性检查之外传递或擦除类型信息。可以在不同的条件分支上擦除类型信息。译者注这里的编译器控制语句主要指 `if #available`,参考 https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md#result-building-methods
下面的代码定义了一个简易的结果构造器,用于构造整型数组。类型别名明确了 `Compontent``Expression`,以一种简单的方式让下面的例子满足上面的方法。
```swift
@resultBuilder
struct ArrayBuilder {
typealias Component = [Int]
typealias Expression = Int
static func buildExpression(_ element: Expression) -> Component {
return [element]
}
static func buildOptional(_ component: Component?) -> Component {
guard let component = component else { return [] }
return component
}
static func buildEither(first component: Component) -> Component {
return component
}
static func buildEither(second component: Component) -> Component {
return component
}
static func buildArray(_ components: [Component]) -> Component {
return Array(components.joined())
}
static func buildBlock(_ components: Component...) -> Component {
return Array(components.joined())
}
}
```
#### 结果转换 {#result-transformations}
使用了结果构造器语法的代码会被递归地转换,成为对结果构造器类型静态方法的调用:
- 如果结果构造器实现了 `buildExpression(_:)` 方法,每个表达式都会转换成一次方法调用。这是转换的第一步。下面的声明方式是等价的:
```swift
@ArrayBuilder var builderNumber: [Int] { 10 }
var manualNumber = ArrayBuilder.buildExpression(10)
```
- 赋值语句也会像表达式一样被转换,但它的类型会被当作 `()`。重载 `buildExpression(_:)` 方法,接收实参类型为 `()` 来特殊处理赋值。(译者注:重载方法参考 https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md#assignments
- 分支语句中对可用性的检查会转换成 `buildLimitedAvailability(_:)` 调用。这个转换早于 `buildEither(first:)`、`buildEither(second:)` 或 `buildOptional(_:)`。类型信息会因进入的条件分支不同发生改变,使用 `buildLimitedAvailability(_:)` 方法可以将它擦除。下面的实例中 `buildEither(first:)` 和 `buildEither(second:)` 使用泛型捕获了不同条件分支的具体类型信息。
```swift
protocol Drawable {
func draw() -> String
}
struct Text: Drawable {
var content: String
init(_ content: String) { self.content = content }
func draw() -> String { return content }
}
struct Line<D: Drawable>: Drawable {
var elements: [D]
func draw() -> String {
return elements.map { $0.draw() }.joined(separator: "")
}
}
struct DrawEither<First: Drawable, Second: Drawable>: Drawable {
var content: Drawable
func draw() -> String { return content.draw() }
}
@resultBuilder
struct DrawingBuilder {
static func buildBlock<D: Drawable>(_ components: D...) -> Line<D> {
return Line(elements: components)
}
static func buildEither<First, Second>(first: First)
-> DrawEither<First, Second> {
return DrawEither(content: first)
}
static func buildEither<First, Second>(second: Second)
-> DrawEither<First, Second> {
return DrawEither(content: second)
}
}
```
然而,在面对有可用性检测的代码时会出现问题:
```swift
@available(macOS 99, *)
struct FutureText: Drawable {
var content: String
init(_ content: String) { self.content = content }
func draw() -> String { return content }
}
@DrawingBuilder var brokenDrawing: Drawable {
if #available(macOS 99, *) {
FutureText("Inside.future") // 问题所在
} else {
Text("Inside.present")
}
}
// brokenDrawing 的类型是 Line<DrawEither<Line<FutureText>, Line<Text>>>
```
上面的代码中,由于 `FutureText` 是 `DrawEither` 泛型的一个类型,它成为了 `brokenDrawing` 类型的组成部分。`FutureText` 如果在运行时不可用会引起程序的崩溃,即使它没有被显式使用。
为了解决这个问题,实现 `buildLimitedAvailability(_:)` 方法擦除类型信息。举例来说,下面的代码构造了一个 `AnyDrawable` 用于可用性检查。
```swift
struct AnyDrawable: Drawable {
var content: Drawable
func draw() -> String { return content.draw() }
}
extension DrawingBuilder {
static func buildLimitedAvailability(_ content: Drawable) -> AnyDrawable {
return AnyDrawable(content: content)
}
}
@DrawingBuilder var typeErasedDrawing: Drawable {
if #available(macOS 99, *) {
FutureText("Inside.future")
} else {
Text("Inside.present")
}
}
// typeErasedDrawing 的类型是 Line<DrawEither<AnyDrawable, Line<Text>>>
```
- 分支语句会被转换成一连串 `buildEither(first:)` 与 `buildEither(second:)` 的方法调用。语句的不同条件分支会被映射到一颗二叉树的叶子结点上,语句则变成了从根节点到叶子结点路径的嵌套 `buildEither` 方法调用。
举例来说,如果你的 `switch` 语句有三个条件分支,编译器会生成一颗拥有 3 个叶子结点的二叉树。同样地,从根节点到第二个分支相当于走了“第二个子节点”再到“第一个子节点”这样的路径,这种情况会转化成 `buildEither(first: buildEither(second: ... ))` 这样的嵌套调用。下面的声明方式是等价的:
```swift
let someNumber = 19
@ArrayBuilder var builderConditional: [Int] {
if someNumber < 12 {
31
} else if someNumber == 19 {
32
} else {
33
}
}
var manualConditional: [Int]
if someNumber < 12 {
let partialResult = ArrayBuilder.buildExpression(31)
let outerPartialResult = ArrayBuilder.buildEither(first: partialResult)
manualConditional = ArrayBuilder.buildEither(first: outerPartialResult)
} else if someNumber == 19 {
let partialResult = ArrayBuilder.buildExpression(32)
let outerPartialResult = ArrayBuilder.buildEither(second: partialResult)
manualConditional = ArrayBuilder.buildEither(first: outerPartialResult)
} else {
let partialResult = ArrayBuilder.buildExpression(33)
manualConditional = ArrayBuilder.buildEither(second: partialResult)
}
```
- 分支语句不一定会产生值,就像没有 `else` 闭包的 `if` 语句,会转换成 `buildOptional(_:)` 方法调用。如果 `if` 语句满足条件,它的代码块会被转换,作为实参进行传递;否则,`buildOptional(_:)` 会被调用,并用 `nil` 作为它的实参。下面的声明方式是等价的:
```swift
@ArrayBuilder var builderOptional: [Int] {
if (someNumber % 2) == 1 { 20 }
}
var partialResult: [Int]? = nil
if (someNumber % 2) == 1 {
partialResult = ArrayBuilder.buildExpression(20)
}
var manualOptional = ArrayBuilder.buildOptional(partialResult)
```
- 代码块或 `do` 语句会转换成 `buildBlock(_:)` 调用。闭包中的每一条语句都会被转换,完成后作为实参传入 `buildBlock(_:)`。下面的声明方式是等价的:
```swift
@ArrayBuilder var builderBlock: [Int] {
100
200
300
}
var manualBlock = ArrayBuilder.buildBlock(
ArrayBuilder.buildExpression(100),
ArrayBuilder.buildExpression(200),
ArrayBuilder.buildExpression(300)
)
```
- `for` 循环的转换分为三个部分:一个临时数组,一个 `for` 循环,以及一次 `buildArray(_:)` 方法调用。新的 `for` 循环会遍历整个序列,并把每一个中间结果存入临时数组。临时数组会作为实参传递给 `buildArray(_:)` 调用。下面的声明方式是等价的:
```swift
@ArrayBuilder var builderArray: [Int] {
for i in 5...7 {
100 + i
}
}
var temporary: [[Int]] = []
for i in 5...7 {
let partialResult = ArrayBuilder.buildExpression(100 + i)
temporary.append(partialResult)
}
let manualArray = ArrayBuilder.buildArray(temporary)
```
- 如果结果构造器实现了 `buildFinalResult(_:)` 方法,最终结果会转换成对于这个方法的调用。它永远最后执行。
虽然转换行为描述中会出现临时变量,但结果构造器不会创建任何对代码可见的临时变量。
不能在结果构造器的转换中使用 `break`、`continue`、`defer`、`guard`,或是 `return` 语句、`while` 语句、`do-catch` 语句。
转换过程不会改变代码中的声明,这样就可以使用临时常量或变量一步一步构造表达式。它也不会改变 `throw` 语句、编译时诊断语句,或包含 `return` 语句的闭包。
在可能的情况下转换会被合并。例如,表达式 `4 + 5 * 6` 会被直接转换成 `buildExpression(4 + 5 * 6)` 而不是多个函数调用。同样地,嵌套分支语句也只会变成一颗调用 `buildEither` 方法的二叉树。
#### 自定义结果构造器特性 {#custom-result-builder-attributes}
创建一个结果构造器类型的同时也创建了一个同名的自定义特性。你可以将它应用于如下场景:
- 用于函数声明时,会构造函数体
- 用于变量或包含 getter 方法的下标声明时,会构造 getter 的函数体
- 用于函数声明中的形参时,会构造传递相应实参的闭包
应用结果构造器特性并不会影响 ABI 稳定。在形参上应用结果构造器,会把这个特性变成函数接口的一部分,从而影响源码稳定性。
### `requires_stored_property_inits` {#requires-stored-property-inits}
该特性用于类声明,以要求类中所有存储属性提供默认值作为其定义的一部分。对于从中继承的任何类都推断出 `NSManagedObject` 特性。
### `testable` {#testable}
将此特性应用于 `import` 声明以导入该模块,并更改其访问控制以简化对该模块代码的测试。这样就能访问被导入模块中的任何标有 `internal` 访问级别修饰符的实体,犹如它们被标记了 `public` 访问级别修饰符。测试也可以访问使用 `internal` 或者 `public` 访问级别修饰符标记的类和类成员,就像它们是 `open` 访问修饰符声明的。被导入的模块必须以允许测试的方式编译。
### `UIApplicationMain` {#uiapplicationmain}
在类上使用该特性表示该类是应用程序委托类。使用该特性与调用 `UIApplicationMain` 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个特性,可以提供一个 `main.swift` 文件,并在代码顶层调用 `UIApplicationMain(_:_:_:_:)` 函数。比如,如果你的应用程序使用一个继承于 `UIApplication` 的自定义子类作为主要类,你可以调用 `UIApplicationMain(_:_:_:_:)` 函数而不是使用该特性。
编译生成可执行程序的 Swift 代码最多只能拥有一个顶级代码入口,请参阅[顶级代码](../03_language_reference/06_Declarations.md#top-level-code)。
### `unchecked` {#unchecked}
该特性用于协议类型,将其作为已采用协议的类型声明列表的一部分,以关闭该协议要求的强制执行。
仅支持 [Sendable](https://developer.apple.com/documentation/swift/sendable) 协议。
### `usableFromInline` {#usablefrominline}
该特性用于函数、方法、计算属性、下标、构造器或析构器的声明,以在同一模块中允许该符号用于内联代码的声明。声明必须具有 `internal` 访问级别修饰符。被标记为 `usableFromInline` 的结构体或类它们属性的类型只能是被标记为 public 或者 `usableFromInline` 的类型。被标记为 `usableFromInline` 的枚举,它 case 的真实值或者关联类型只能是被标记为 public 或者 `usableFromInline` 的类型。
与 `public` 访问修饰符相同的是,该特性将声明公开为模块公共接口的一部分。区别于 `public`,编译器不允许在模块外部的代码通过名称引用 `usableFromInline` 标记的声明,即使导出了声明符号也无法引用。但是,模块外的代码仍然可以通过运行时与声明符号进行交互。
标记为 `inlinable` 特性的声明,在内联代码中可以隐式使用。虽然 `inlinable` 或 `usableFromInline` 可以用于 `internal` 声明,但这两者不能同时使用。
### `warn-unqualified-access` {#warn-unqualified-access}
该特性应用于顶级函数、实例方法、类方法或静态方法,以在没有前置限定符(例如模块名称、类型名称、实例变量或常量)的情况下使用该函数或方法时触发警告。使用该特性可以减少在同一作用域里访问的同名函数之间的歧义。
例如Swift 标准库包含 [`min(_:_:)`](https://developer.apple.com/documentation/swift/1538339-min/) 顶级函数和用于序列比较元素的 [`min()`](https://developer.apple.com/documentation/swift/sequence/1641174-min) 方法。序列方法声明使用了 `warn_unqualified_access`,以减少在 `Sequence` 扩展中使用它们的歧义。
### Interface Builder 使用的声明特性 {#declaration-attributes-used-by-interface-builder}
Interface Builder 特性是 Interface Builder 用来与 Xcode 同步的声明特性。Swift 提供了以下的 Interface Builder 特性:`IBAction``IBSegueAction``IBOutlet``IBDesignable`,以及 `IBInspectable`。这些特性与 Objective-C 中对应的特性在概念上是相同的。
`IBOutlet` 和 `IBInspectable` 用于修饰一个类的属性声明。`IBAction` 和 `IBSegueAction` 特性用于修饰一个类的方法声明,`IBDesignable` 用于修饰类的声明。
应用 `IBAction`、`IBSegueAction`、`IBOutlet`、`IBDesignable` 或者 `IBInspectable` 特性都意味着同时应用 `objc` 特性。
## 类型特性 {#type-attributes}
类型特性只能用于修饰类型。
### `autoclosure` {#autoclosure}
这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以修饰类型为返回表达式结果类型的无参数函数类型的函数参数。关于如何使用 `autoclosure` 特性的例子,请参阅 [自动闭包](../02_language_guide/07_Closures.md#autoclosures) 和 [函数类型](./03_Types.md#function-type)。
### `convention` {#convention}
该特性用于修饰函数类型,它指出了函数调用的约定。
`convention` 特性总是与下面的参数之一一起出现。
- `swift` 参数用于表示一个 Swift 函数引用。这是 Swift 中函数值的标准调用约定。
- `block` 参数用于表示一个 Objective-C 兼容的块引用。函数值会作为一个块对象的引用,块是一种 `id` 兼容的 Objective-C 对象,其中嵌入了调用函数。调用函数使用 C 的调用约定。
- `c` 参数用于表示一个 C 函数引用。函数值没有上下文,不具备捕获功能,并且使用 C 的调用约定。
除了少数例外,当函数需要任何其他调用约定时,可以转换成任意调用约定的函数。非范型全局函数、不捕获任何局部变量的局部函数或不捕获任何局部变量的闭包可以转换为 C 调用约定。其余的 Swift 函数不能转换成 C 调用约定。一个 Objective-C 块调用约定的函数不能转换成 C 调用约定。
### `escaping` {#escaping}
在函数或者方法声明上使用该特性,它表示参数将不会被存储以供延迟执行。这将确保参数不会超出函数调用的生命周期。在使用 `escaping` 特性声明的函数类型中访问属性和方法时需要显式地使用 `self.`。关于如何使用 `escaping` 特性的例子,请参阅 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures)。
### `Sendable` {#Sendable}
在函数类型上使用该特性以指示该函数是闭包或可发送的。在函数类型上使用该特性与使非函数类型符合 `Sendable` 协议具有相同的意义。
当一个函数或闭包被用在需要可发送值的情况,且该函数或闭包满足可发送的要求,则可以在该函数或闭包上推断此特性。
可发送函数类型是对应的不可发送函数类型的一个子类型。
## Switch Case 特性 {#switch-case-attributes}
你只能在 switch cases 语句中使用 switch case 特性。
### `unknown` {#unknown}
该特性用于 switch case用于没有匹配上代码编译时已知 case 的情况。有关如何使用 `unknown` 特性的示例,可参阅 [对未来枚举的 `case` 进行 `switch`](./05_Statements.md#future-case)。
> 特性语法
>
>
>
#### attribute {#attribute}
>
> *特性* → @ [特性名](#attribute-name) [特性参数子句](#atribute-argument-clause)<sub>可选</sub>
>
>
#### attribute-name {#attribute-name}
>
> *特性名* → [标识符](./02_Lexical_Structure.md#identifier)
>
>
#### atribute-argument-clause {#atribute-argument-clause}
>
> *特性参数子句* → **(** [均衡令牌列表](#balanced-tokens)<sub>可选</sub> **)**
>
>
#### attributes {#attributes}
>
> *特性列表* → [特性](#attribute) [特性列表](#attributes)<sub>可选</sub>
>
>
>
#### balanced-tokens {#balanced-tokens}
>
> *均衡令牌列表* → [均衡令牌](#balanced-token) [均衡令牌列表](#balanced-tokens)<sub>可选</sub>
>
>
#### balanced-token {#balanced-token}
>
> *均衡令牌* → **(** [均衡令牌列表](#balanced-tokens)<sub>可选</sub> **)**
>
> *均衡令牌* → **\[** [均衡令牌列表](#balanced-tokens)<sub>可选</sub> **\]**
>
> *均衡令牌* → **{** [均衡令牌列表](#balanced-tokens)<sub>可选</sub> **}**
>
> *均衡令牌* → 任意标识符,关键字,字面量或运算符
>
> *均衡令牌* → 任意标点除了 **(****)****[****]****{**,或 **}**
>

View File

@ -1,327 +0,0 @@
# Swift 文档修订历史
### 2022-06-06
* 更新至 Swift 5.7。
* 新增 [可发送类型](../02_language_guide/28_Concurrency.md#Sendable-Types) 章节,其中包含在 actors 和任务间发送数据的内容。在 [可发送](03_language_reference/07_Attributes.md#Sendable) 章节和 [unchecked](03_language_reference/07_Attributes.md#unchecked) 章节新增了有关特性 `@Sendable``@unchecked` 的内容。
* 新增了 [正则表达式字面量](03_language_reference/02_Lexical_Structure.md#regular-expression-literals) 章节,其中包含新建正则表达式的内容。
* 在 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding) 章节新增了 `if-let` 结构更简短的一种形式。
* 在 [检查 API 可用性](../02_language_guide/05_Control_Flow.md#checking-api-availability) 章节新增了与不可用性检查 `#unavaliable` 有关的内容。
### 2022-03-14
* 更新至 Swift 5.6
* 更新了 [显式成员表达式](../03_language_reference/04_Expressions.md#explicit-member-expression) 章节,现在可以在链式调用方法和其它后缀表达式周围使用 `#if` 了。
### 2021-07-14
* 更新 [并发](../02_language_guide/28_Concurrency.md) 篇章里的示例,使用当前的并发 API。
### 2021-06-07
* 更新至 Swift 5.5。
* 在 [并发](../02_language_guide/28_Concurrency.md) 篇章、[Actor 声明](03_language_reference/06_Declarations.md#actor-declaration)、[异步函数和方法](03_language_reference/06_Declarations.md#asynchronous-functions-and-methods) 和 [Await 运算符](03_language_reference/04_Expressions.md#await-operators) 中新增了有关异步函数、任务和 actor 的内容。
### 2021-04-26
* 更新至 Swift 5.4。
* 新增 [结果构造器](../02_language_guide/27_Advanced_Operators.md#result-builders) 和 [resultBuilder](03_language_reference/07_Attributes.md#resultbuilder) 章节,其中包含结果构造器的内容。
* 新增 [指针类型的隐式转换](03_language_reference/04_Expressions.md#implicit-conversion-to-a-pointer) 章节,其中包含在函数调用时输入输出形参可以隐式转换到不安全指针的内容。
* 更新 [可变参数](../02_language_guide/06_Functions.md#variadic-parameters) 和 [函数声明](03_language_reference/06_Declarations.md#function-declaration) 章节,现在函数可以有多个可变参数了。
* 更新 [隐式成员表达式](03_language_reference/04_Expressions.md#implicit-member-expression) 章节,现在可以链式调用隐式成员表达式了。
### 2020-09-16
* 更新至 Swift 5.3。
* 在 [尾随闭包](../02_language_guide/07_Closures.md#trailing-closures) 章节中新增了有关多个尾随闭包的内容,在 [函数调用表达式](../03_language_reference/04_Expressions.md#function-call-expression) 章节中新增了有关尾随闭包如何匹配形参的内容。
* 在 [使用合成实现来采纳协议](../02_language_guide/21_Protocols.md#adopting-a-protocol-using-a-synthesized-implementation) 章节中新增了枚举合成实现 `Comparable` 的内容。
* 新增 [包含上下文关系的 where 分句](../02_language_guide/22_Generics.md#contextual-where-clauses) 章节,现在可以在更多位置编写范型 `where` 分句。
* 新增 [无主可选引用](../02_language_guide/24_Automatic_Reference_Counting.md#unowned-optional-references) 章节,其中包含可选值使用无主引用的内容。
* 在 [main](../03_language_reference/07_Attributes.md#main) 章节中新增有关 `@main` 特性的内容。
* 在 [字面量表达式](../03_language_reference/04_Expressions.md#literal-expression) 章节中新增 `#filePath`,并更新了 `#file` 的讨论。
* 更新 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures) 章节,现在闭包可以在更多场景隐式引用 `self`
* 更新 [用 Do-Catch 处理错误](../02_language_guide/17_Error_Handling.md#handling-errors-using-do-Catch) 和 [Do 语句](../03_language_reference/05_Statements.md#do-statements) 章节,现在 `catch` 子句可以匹配多个错误。
* 新增更多有关 `Any` 的内容并移动到新增的 [Any 类型](../03_language_reference/03_Types.md#any-type) 章节。
* 更新 [属性观察器](../02_language_guide/10_Properties.md#property-observers) 章节,现在 lazy 属性可以有观察器。
* 更新 [协议声明](../03_language_reference/06_Declarations.md#protocol-declaration) 章节,现在枚举的成员可以用于遵循协议的要求。
* 更新 [存储型变量和属性的观察器](../03_language_reference/06_Declarations.md#stored-variable-observers-and-property-observers) 章节,描述观察器之前 getter 是何时被调用的。
* 更新 [内存安全](../02_language_guide/25_Memory_Safety.md) 篇章并提及原子操作。
### 2020-03-24
* 更新至 Swift 5.2。
* 在 [Key-Path 表达式](../03_language_reference/04_Expressions.md#key-path-expression) 章节中新增了有关传递 key path 代替闭包的内容。
* 在 [特殊名称方法](../03_language_reference/06_Declarations.md#methods-with-special-names) 章节中新增了有关让类、结构体和枚举的实例作为函数调用语法糖的内容。
* 更新 [下标选项](../02_language_guide/12_Subscripts.md#subscript-options) 章节,现在下标支持形参默认值。
* 更新 [自身类型](../03_language_reference/03_Types.md#self-type-h) 章节,现在 `Self` 可以在更多上下文中使用。
* 更新 [隐式解析可选类型](../02_language_guide/01_The_Basics.md#implicityly-unwrapped-optionals) 章节,明确了隐式解析可选值可以作为可选值或者非可选值使用。
### 2019-09-10
* 更新至 Swift 5.1。
* 在 [不透明类型](../02_language_guide/23_Opaque_Types.md) 篇章中新增了有关函数返回值遵循指定协议,而不需要提供指定返回类型的内容。
* 在 [属性包装器](../02_language_guide/10_Properties.md#property-wrappers) 章节中新增了有关属性包装器的内容。
* 在 [冻结](../03_language_reference/07_Attributes.md#frozen) 章节中新增了有关因库演变而需要的枚举和结构体冻结。
* 新增 [隐式返回的函数](../02_language_guide/06_Functions.md#functions-with-an-implicit-return) 和 [简化 Getter 声明](../02_language_guide/10_Properties.md#shorthand-getter-declaration) 章节,其中包含函数省略 `return` 的内容。
* 在 [类型下标](../02_language_guide/12_Subscripts.md#type-subscripts) 章节中新增了有关在类型中使用下标的内容。
* 更新 [枚举 Case 模式匹配](../03_language_reference/08_Patterns.md#enumeration-case-pattern) 章节,现在枚举 case 模式匹配支持匹配可选值。
* 更新 [结构体的逐一成员构造器](../02_language_guide/14_Initialization.md#memberwise-initializers-for-structure-types) 章节,现在逐一成员构造器支持在属性有默认值时省略形参。
* 在 [动态查找成员](../03_language_reference/07_Attributes.md#dynamicmemberlookup) 章节中新增了有关在运行时用 key path 查找动态成员的内容。
* 在 [条件编译代码块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 中的目标环境里添加了 `macCatalyst`
* 更新 [自身类型](../03_language_reference/03_Types.md#self-type-h) 章节,现在 `Self` 可以指向当前类,结构体或者枚举声明时的类型。
### 2019-03-25
* 更新至 Swift 5。
* 新增 [拓展字符串分隔符](../02_language_guide/03_Strings_and_Characters.md#extended-string-delimiters) 章节。更新 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节,拓展有关字符串分隔符的内容。
* 新增 [动态调用](../03_language_reference/07_Attributes.md#dynamiccallable) 章节,其中包含使用 `dynamicCallable` 属性动态调用实例作为函数的内容。
* 新增 [unknown](../03_language_reference/07_Attributes.md#unknown) 和 [未来枚举匹配](../03_language_reference/05_Statements.md#future-case) 章节,其中包含了使用 `unknown` 来处理未来枚举可能发生改变的情形。
* 在 [Key-Path 表达式](../03_language_reference/04_Expressions.md#key-path-expression) 章节新增了有关标示 key path (\\.self) 的内容。
* 在 [可选编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节新增了有关小于比较符 `<` 的内容。
### 2018-09-17
* 更新至 Swift 4.2。
* 在 [遍历枚举情形](../02_language_guide/08_Enumerations.md#iterating-over-enumeration-cases) 章节新增了有关访问所有枚举情形的内容。
* 在 [编译诊断](../03_language_reference/05_Statements.md#compile-time-diagnostic-statement) 章节新增了有关 `#error``#warning` 的内容。
* 在 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `inlinable``usableFromInline` 属性的内容。
* 在 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `requires-stored-property-inits``warn-unqualified-access` 属性的内容。
* 在 [可选编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节新增了有关如何根据 Swift 编译器版本对代码进行对应编译处理的内容。
* 在 [字面量语法](../03_language_reference/04_Expressions.md#literal-expression) 章节新增了有关 `#dsohandle` 的内容。
### 2018-03-29
* 更新至 Swift 4.1。
* 在 [等价运算符](../02_language_guide/27_Advanced_Operators.md#equivalence-operators) 章节新增了有关等价运算符的合成实现的内容。
* 在 [声明](../03_language_reference/06_Declarations.md) 篇章中 [声明拓展](../03_language_reference/06_Declarations.md#extension-declaration) 章节和 [协议](../02_language_guide/21_Protocols.md) 篇章中 [有条件地遵循协议](../02_language_guide/21_Protocols.md#Conditionally-Conforming-to-a-Protocol) 章节新增了有关协议有条件遵循的内容。
* 在 [关联类型约束中使用协议](../02_language_guide/22_Generics.md#using-a-protocol-in-its-associated-types-constraints) 章节中新增了有关递归协议约束的内容。
* 在 [条件编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节中新增了有关 `canImport()``targetEnvironment()` 平台条件的内容。
### 2017-12-04
* 更新至 Swift 4.0.3。
* 更新 [Key-Path 表达式](../03_language_reference/04_Expressions.md#key-path-expression) 章节,现在 key path 支持下标子路径。
### 2017-09-19
* 更新至 Swift 4.0。
* 在 [内存安全](../02_language_guide/25_Memory_Safety.md) 章节新增了有关内存互斥访问的内容。
* 新增 [带有泛型 Where 子句联类型](../02_language_guide/22_Generics.md#associated-types-with-a-generic-where-clause) 章节,现在可以使用泛型 `where` 子句约束关联类型。
* 在 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 篇章中 [字面量](../02_language_guide/03_Strings_and_Characters.md#string-literals) 章节以及 [词法结构](../03_language_reference/02_Lexical_Structure.md) 篇章的 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节中新增了有关多行字符串字面量的内容。
* 更新 [声明属性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 中 `objc` 属性的讨论,现在该属性会在更少的位置被推断出来。
* 新增 [范型下标](../02_language_guide/22_Generics.md#generic-subscripts) 章节,现在下标也支持范型特性了。
* 更新 [协议](../02_language_guide/21_Protocols.md) 篇章中 [协议组合](../02_language_guide/21_Protocols.md#protocol-composition) 章节和 [类型](../03_language_reference/03_Types.md) 篇章中 [协议组合类型](../03_language_reference/03_Types.md#protocol-composition-type-h) 章节的讨论,现在协议组合类型支持进行父类约束了。
* 更新 [拓展声明](../03_language_reference/06_Declarations.md#extension-declaration) 中有关协议扩展的讨论,现在它们不支持 `final` 特性了。
* 在 [断言和前置条件](../02_language_guide/01_The_Basics.md#assertions-and-preconditions) 章节中新增了部分前置条件和致命错误的内容。
### 2017-03-27
* 更新至 Swift 3.1。
* 新增 [范型 Where 子句扩展](../02_language_guide/22_Generics.md#extensions-with-a-generic-where-clause) 章节,包含需要的扩展内容。
* 在 [For-In 循环](../02_language_guide/05_Control_Flow.md#for-in-loops) 章节中新增了区间迭代的例子。
* 在 [可失败构造器](../02_language_guide/14_Initialization.md#failable-initializers) 章节中新增了可失败数值转换的例子。
* 在 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关使用 Swift 语言版本的 `available` 特性的内容 。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节中的讨论,注意在写函数类型时不允许使用参数标签。
* 更新 [条件编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节中的 Swift 语言版本号的讨论,现在可以使用可选的补丁版本号。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节的讨论,现在 Swift 区分了采用多参数的函数和采用元组类型的单个参数的函数。
* 在 [表达式](../03_language_reference/04_Expressions.md) 篇章中删除了动态表达式的章节,现在 `type(of:)` 是 Swift 标准库函数。
### 2016-10-27
* 更新至 Swift 3.0.1。
* 更新 [自动引用计数](../02_language_guide/24_Automatic_Reference_Counting.md) 章节中有关 weak 和 unowned 引用的讨论。
* 在 [声明标识符](../03_language_reference/06_Declarations.md#declaration-modifiers) 章节中新增了有关新的标识符 `unowned``unowend(safe)``unowned(unsafe)` 的内容。
* 在 [Any 和 AnyObject 的类型转换](../02_language_guide/18_Type_Casting.md#type-casting-for-any-and-anyobject) 章节中新增了一处说明,有关使用类型 `Any` 作为可选值。
* 更新 [表达式](../03_language_reference/04_Expressions.md) 章节,把括号表达式和元组表达式的描述分开。
### 2016-09-13
* 更新至 Swift 3.0。
* 更新 [函数](../02_language_guide/06_Functions.md) 篇章和 [函数声明](../03_language_reference/06_Declarations.md#function-declaration) 章节中有关函数的讨论,所有函数参数默认都有函数标签。
* 更新 [高级操作符](../02_language_guide/27_Advanced_Operators.md) 篇章中有关操作符的讨论,现在你可以作为类型函数来实现,替代之前的全局函数实现方式。
* 在 [访问控制](../02_language_guide/26_Access_Control.md) 章节中新增有关对新的访问级别描述符 `open``fileprivate` 的内容。
* 更新 [函数声明](../03_language_reference/06_Declarations.md#function-declaration) 章节中有关 `inout` 的讨论,注意它现在出现在参数类型的前面,而不是在参数名称的前面。
* 更新 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures) 和 [自动闭包](../02_language_guide/07_Closures.md#autoclosures) 章节还有 [属性](../03_language_reference/07_Attributes.md) 篇章中有关 `@noescape``@autoclosure` 的讨论,现在他们是类型属性,而不是定义属性。
* 在 [高级操作符](../02_language_guide/27_Advanced_Operators.md) 篇章中 [自定义中缀操作符的优先级](./02_language_guide/27_Advanced_Operators.md#precedence-and-associativity-for-custom-infix-operators) 章节和 [定义](../03_language_reference/06_Declarations.md) 篇章中 [优先级组声明](../03_language_reference/06_Declarations.md#precedence-group-declaration-modifiers) 章节中新增了有关操作符优先级组的内容。
* 更新一些讨论,使用 macOS 替换掉 OS X Error 替换掉 ErrorProtocol。更新一些协议名称比如使用 ExpressibleByStringLiteral 替换掉 StringLiteralConvertible。
* 更新 [泛型](../02_language_guide/22_Generics.md) 篇章中 [泛型 Where 语句](../02_language_guide/22_Generics.md#extensions-with-a-generic-where-clause) 章节和 [泛型形参和实参](../03_language_reference/09_Generic_Parameters_and_Arguments.md) 篇章的讨论,现在泛型的 where 语句写在一个声明的最后。
* 更新 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures) 章节中的讨论,现在闭包默认为非逃逸的。
* 更新 [基础部分](../02_language_guide/01_The_Basics.md) 篇章中 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章中 [While 语句](../03_language_reference/05_Statements.md#while-statement) 章节中的讨论,现在 if`while``guard` 语句使用逗号分隔条件列表,不需要使用 `where` 语句。
* 在 [控制流](../02_language_guide/05_Control_Flow.md) 篇章中 [Switch](../02_language_guide/05_Control_Flow.md#switch) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章中 [Switch 语句](../03_language_reference/05_Statements.md#switch-statement) 章节中新增了 switch cases 可以使用多模式的内容。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节有关现在函数参数标签不包含在函数类型中的讨论。
* 更新 [协议](../02_language_guide/21_Protocols.md) 篇章中 [协议组合](../02_language_guide/21_Protocols.md#protocol-composition) 章节和 [类型](../03_language_reference/03_Types.md) 篇章中 [协议组合类型](../03_language_reference/03_Types.md#protocol-composition-type-h) 章节中有关使用新的 Protocol1 & Protocol2 语法的内容。
* 更新动态类型表达式章节中使用新的 `type(of:)` 表达式的讨论。
* 更新 [行控制表达式](../03_language_reference/05_Statements.md#line-control-statement) 章节中使用 `#sourceLocation(file:line:)` 表达式的讨论。
* 更新 [永不返回函数](../03_language_reference/06_Declarations.md#functions-that-never-return) 章节中使用 新的 `Never` 类型的讨论。
* 在 [字面量表达式](../03_language_reference/04_Expressions.md#literal-expression) 章节中新增了有关 `playground` 字面量的内容。
* 更新 [In-Out 参数](../03_language_reference/06_Declarations.md#in-out-parameters) 章节,标明只有非逃逸闭包能捕获 `in-out` 参数。
* 更新 [默认参数值](../02_language_guide/06_Functions.md#default-parameter-values) 章节,现在默认参数不能在调用时候重新排序。
* 更新 [属性](../03_language_reference/07_Attributes.md) 篇章中有关属性参数使用分号的说明。
* 在 [重新抛出函数和方法](../03_language_reference/06_Declarations.md#rethrowing-functions-and-methods) 章节中新增了有关在 catch 代码块中抛出错误的重新抛出函数的内容。
* 在 [Selector 表达式](../03_language_reference/04_Expressions.md#selector-expression7) 章节中新增了中有关访问 Objective-C 中 Selector 的 getter 和 setter 的内容。
* 在 [类型别名声明](../03_language_reference/06_Declarations.md#type-alias-declaration) 章节中中新增了有关泛型类型别名和在协议内使用类型别名的内容。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节中有关函数类型的讨论,标明函数类型作为参数类型必须使用括号包裹。
* 更新 [属性](../03_language_reference/07_Attributes.md) 篇章,标明 `@IBAction``@IBOutlet``@NSManaged` 隐式含有 `@objc` 属性。
* 在 [声明属性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `@GKInspectable` 的内容。
* 更新 [可选协议要求](../02_language_guide/21_Protocols.md#optional-protocol-requirements) 章节中有关只能在与 `Objective-C` 交互的代码中才能使用可选协议要求的内容。
* 删除 [函数声明](../03_language_reference/06_Declarations.md#function-declaration) 章节中有关显式使用 `let` 关键字作为函数参数的内容。
* 删除 [语句](../03_language_reference/05_Statements.md) 章节中有关 `Boolean` 协议的内容, 现在这个协议已经被 Swift 标准库删除。
* 更正 [声明属性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@NSApplicationMain` 协议的内容。
### 2016-03-21
* 更新至 Swift 2.2。
* 在 [编译配置语句](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节新增了中有关如何根据 Swift 版本进行条件编译。
* 在 [显示成员表达式](../03_language_reference/04_Expressions.md#explicit-member-expression) 章节中新增了有关如何区分只有参数名不同的方法和构造器的内容。
* 在 [选择器表达式](../03_language_reference/04_Expressions.md#selector-expression7) 章节中新增了了针对 Objective-C 选择器的 `#selector` 语法。
* 更新 [关联类型](../02_language_guide/22_Generics.md#associated-types) 和 [协议关联类型声明](../03_language_reference/06_Declarations.md#protocol-associated-type-declaration) 章节中有关使用 `associatedtype` 关键词修饰关联类型的讨论。
* 更新 [可失败构造器](../02_language_guide/14_Initialization.md#failable-initializers) 章节中有关当构造器在实例完全初始化之前返回 `nil` 的相关内容。
* 在 [比较运算符](../02_language_guide/02_Basic_Operators.md#comparison-operators) 章节中新增了比较元组的内容。
* 在 [关键字和标点符号](../03_language_reference/02_Lexical_Structure.md#keywords-and-punctuation) 章节中新增了使用关键字作为外部参数名的内容。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@objc` 特性的讨论,并指出枚举和枚举用例。
* 更新 [操作符](../03_language_reference/02_Lexical_Structure.md#operator) 章节中对于自定义运算符的包含了 `.` 的讨论。
* 在 [重新抛出错误的函数和方法](../03_language_reference/06_Declarations.md#rethrowing-functions-and-methods) 章节中新增了一处说明,重新抛出错误函数不能直接抛出错误。
* 在 [属性观察器](../02_language_guide/10_Properties.md#property-observers) 章节中新增了一处说明,当作为 in-out 参数传递属性时,属性观察器的调用行为。
* 在 [Swift 初见](../01_welcome_to_swift/03_a_swift_tour.md) 篇章中新增了错误处理的章节。
* 更新 [弱引用](../02_language_guide/24_Automatic_Reference_Counting.md#weak-references) 章节中的图片用以更清楚的展示重新分配过程。
* 删除 C 语言风格的 `for` 循环,`++` 前缀和后缀运算符,以及 `--` 前缀和后缀运算符。
* 删除对变量函数参数和柯里化函数的特殊语法的讨论。
### 2015-10-20
* 更新至 Swift 2.1。
* 更新 [字符串插值](../02_language_guide/03_Strings_and_Characters.md#string-interpolation) 和 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节,现在字符串插值可包含字符串字面量。
* 在 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures) 章节中新增了有关 `@noescape` 属性的相关内容。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 和 [编译配置语句](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节中与 tvOS 相关的内容。
* 在 [In-Out 参数](../03_language_reference/06_Declarations.md#in-out-parameters) 章节中新增了与 in-out 参数行为相关的内容。
* 在 [捕获列表](../03_language_reference/04_Expressions.md#capture-lists) 章节新增了有关指定闭包捕获列表被捕获时捕获值的相关内容。
* 更新 [可选链式调用访问属性](../02_language_guide/16_Optional_Chaining.md#accessing-properties-through-optional-chaining) 章节,阐明了如何通过可选链式调用进行赋值。
* 改进 [自动闭包](../02_language_guide/07_Closures.md#autoclosures) 章节中对自闭包的讨论。
* 在 [Swift 初见](../01_welcome_to_swift/03_a_swift_tour.md) 篇章中新增了一个使用 `??` 操作符的例子。
### 2015-09-16
* 更新至 Swift 2.0。
* 在 [错误处理](../02_language_guide/17_Error_Handling.md) 篇章中新增了有关错误处理的相关内容,包括 [Do 语句](../03_language_reference/05_Statements.md#do-statement)、 [Throw 语句](../03_language_reference/05_Statements.md#throw-statement)、 [Defer 语句](../03_language_reference/05_Statements.md##defer-statements) 以及 [try 运算符](../03_language_reference/04_Expressions.md#try-operator)。
* 更新 [错误表示和抛出](../02_language_guide/17_Error_Handling.md#representing-and-throwing-errors) 章节,现在所有类型都可以遵循 `ErrorType` 协议了。
* 在 [将错误装换成可选值](../02_language_guide/17_Error_Handling.md#converting-errors-to-optional-values) 篇章增加了 `try?` 关键字相关内容。
* 在 [枚举](../02_language_guide/08_Enumerations.md) 篇章的 [递归枚举](../02_language_guide/08_Enumerations.md#recursive-enumerations) 章节以及以及 [声明](../03_language_reference/06_Declarations.md) 篇章的 [任意类型用例的枚举](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-any-type) 章节中新增了递归枚举相关内容。
* 在 [控制流](../02_language_guide/05_Control_Flow.md) 篇章的 [API 可用性检查](../02_language_guide/05_Control_Flow.md#checking-api-availability) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章的 [可用性条件](../03_language_reference/05_Statements.md#availability-condition) 章节中新增了有关 API 可用性检查相关的内容。
* 在 [控制流](../02_language_guide/05_Control_Flow.md) 篇章的 [尽早退出](../02_language_guide/05_Control_Flow.md#early-exit) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章的 [Guard 语句](../03_language_reference/05_Statements.md#guard-statement) 章节新增了与 `guard` 语句相关的内容。
* 在 [协议](../02_language_guide/21_Protocols.md) 篇章中 [协议扩展](../02_language_guide/21_Protocols.md#protocol-extensions) 章节中新增了有关协议扩展的内容。
* 在 [访问控制](../02_language_guide/26_Access_Control.md) 篇章的 [单元测试 target 的访问级别](../02_language_guide/26_Access_Control.md#access-levels-for-unit-test-targets) 章节中新增了有关单元测试访问控制相关的内容。
* 在 [模式](../03_language_reference/08_Patterns.md) 篇章的 [可选模式](../03_language_reference/08_Patterns.md#optional-pattern) 章节中新增了可选模式相关内容。
* 更新 [Repeat-While](../02_language_guide/05_Control_Flow.md#repeat-while) 章节中有关 `repeat-while` 循环相关的内容。
* 更新 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 章节,现在 `String` 类型在 Swift 标准库中不再遵循 `CollectionType` 协议。
* 在 [常量与变量打印](../02_language_guide/01_The_Basics.md#printing) 章节中新增了新 Swift 标准库中有关 `print(-:separator:terminator) ` 相关内容。
* 在 [枚举](../02_language_guide/08_Enumerations.md) 篇章的 [原始值的隐式赋值](../02_language_guide/08_Enumerations.md#implicitly-assigned-raw-values) 章节和 [声明](../03_language_reference/06_Declarations.md) 篇章的 [包含原始值类型的枚举](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-a-raw-value-type) 章节中新增了有关包含 `String` 原始值的枚举用例的行为相关内容。
* 在 [自动闭包](../02_language_guide/07_Closures.md#autoclosures) 章节中新增了有关 `@autoclosure` 特性的相关内容,包括它的 `@autoclosure(escaping)` 形式。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@avaliable``warn-unused-result` 特性的相关内容。
* 更新 [类型特性](../03_language_reference/07_Attributes.md#type-attributes) 章节中有关 `@convention` 特性的相关内容。
* 在 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding) 章节中新增了有关使用 `where` 子句进行多可选绑定的相关内容。
* 在 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节中新增了有关在编译时使用 `+` 运算符拼接字符串字面量的相关内容。
* 在 [元类型](../03_language_reference/03_Types.md#metatype-type-h) 章节中新增了有关元类型值的比较和使用它们通过构造器表达式构造实例相关内容。
* 在 [断言调试](../02_language_guide/01_The_Basics.md#debugging-with-assertions) 章节中新增了一处说明,有关用户定义断言何时会失效。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中对 `@NSManaged` 特性的讨论,现在这个特性可以被应用到一个确定实例方法。
* 更新 [可变参数](../02_language_guide/06_Functions.md#variadic-parameters) 章节,现在可变参数可以声明在函数参数列表的任意位置中。
* 在 [重写可失败构造器](../02_language_guide/14_Initialization.md#overriding-a-failable-initializer) 章节中新增了有关非可失败构造器相当于一个可失败构造器通过父类构造器的结果进行强制拆包的相关内容。
* 在 [任意类型用例的枚举](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-any-type) 章节中新增了有关枚举用例作为函数的内容。
* 在 [构造器表达式](../03_language_reference/04_Expressions.md#initializer-expression) 章节中新增了有关显式引用一个构造器相关内容。
* 在 [编译控制语句](../03_language_reference/05_Statements.md#compiler-control-statements) 章节中新增了有关编译内容以及行控制语句相关内容。
* 在 [元类型](../03_language_reference/03_Types.md#metatype-type-h) 章节新增了一处说明,有关如何从元类型值中构造类实例相关内容。
* 在 [弱引用](../02_language_guide/24_Automatic_Reference_Counting.md#weak-references) 章节新增了一处说明,有关弱引用作为缓存所存在的不足。
* 更新 [类型特性](../02_language_guide/10_Properties.md#type-properties) 章节,提到了存储型特性其实是懒加载。
* 更新 [捕获类型](../02_language_guide/07_Closures.md#capturing-values) 章节,阐明了变量和常量在闭包中如何被捕获。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节,用以描述何时在类中使用 `@objc` 关键字。
* 在 [错误处理](../02_language_guide/17_Error_Handling.md#handling-errors) 章节中新增了一处说明,有关执行 `throw` 语句的性能。在 [Do 语句](../03_language_reference/05_Statements.md#do-statement) 章节的 do 语句部分也新增了类似内容。
* 更新 [类型特性](../02_language_guide/10_Properties.md#type-properties) 章节中有关类、结构体和枚举的存储型和计算型特性相关的内容。
* 更新 [Break 语句](../03_language_reference/05_Statements.md#break-statement) 章节中有关带标签的 break 语句相关内容。
* 在 [属性观察器](../02_language_guide/10_Properties.md#property-observers) 章节更新了一处说明,用来明确 `willSet``didSet` 观察器的行为。
* 在 [访问级别](../02_language_guide/26_Access_Control.md#access-levels) 章节新增了有关 `private` 作用域的相关内容说明。
* 在 [弱引用](../02_language_guide/24_Automatic_Reference_Counting.md#weak-references) 章节新增了有关弱应用在垃圾回收系统和 ARC 之间的区别的说明。
* 更新 [字符串字面量中特殊字符](../02_language_guide/03_Strings_and_Characters.md#special-characters-in-string-literals) 章节,对 Unicode 标量更精确定义。
### 2015-04-08
* 更新至 Swift 1.2。
* Swift 现在自身提供了一个 `Set` 集合类型,更多内容,请看 [Sets](../02_language_guide/04_Collection_Types.md#sets) 。
* `@autoclosure` 现在是一个参数声明的属性,而不是参数类型的属性。这里还有一个新的参数声明属性 `@noescape`。更多内容,请看 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 。
* 对于类型属性和方法现在可以使用 `static` 关键字作为声明描述符,更多内容,请看 [类型变量属性](../03_language_reference/06_Declarations.md#type-variable-properties)。
* Swift 现在包含一个 `as?``as!` 的向下可失败类型转换运算符。更多内容,请看 [协议遵循性检查](../02_language_guide/21_Protocols.md#checking-for-protocol-conformance)。
* 新增 [字符串索引](../02_language_guide/03_Strings_and_Characters.md#string-indices) 的新指导章节。
* 在 [溢出运算符](../02_language_guide/27_Advanced_Operators.md#overflow-operators) 一节中删除了溢出除运算符(`&/`)和求余溢出运算符(`&%`)。
* 更新常量和常量属性在声明和构造时的规则,更多内容,请看 [常量声明](../03_language_reference/06_Declarations.md#constant-declaration) 。
* 更新字符串字面量中 Unicode 标量集的定义,请看 [字符串字面量中的特殊字符](../02_language_guide/03_Strings_and_Characters.md#special-characters-in-string-literals) 。
* 更新 [区间运算符](../02_language_guide/02_Basic_Operators.md#range-operators) 章节,注意当半开区间运算符含有相同的起止索引时,其区间为空。
* 更新 [闭包引用类型](../02_language_guide/07_Closures.md#closures-are-reference-types) 章节,对于变量的捕获规则进行了阐明。
* 更新 [值溢出](../02_language_guide/27_Advanced_Operators.md#value-overflow) 章节,对有符号整数和无符号整数的溢出行为进行了阐明。
* 更新 [协议声明](../03_language_reference/06_Declarations.md#protocol-declaration) 章节,对协议声明时的作用域和成员等内容进行了阐明。
* 更新 [捕获列表](../02_language_guide/24_Automatic_Reference_Counting.md#defining-a-capture-list) 章节,对于闭包捕获列表中的弱引用和无主引用的使用语法进行了阐明。
* 更新 [运算符](../03_language_reference/02_Lexical_Structure.md#operator) 章节明确指明一些例子来说明自定义运算符所支持的特性如数学运算符各种符号Unicode 符号块等。
* 在函数作用域中的常量声明时可以不被初始化,它必须在第一次使用前被赋值。更多的内容,请看 [常量声明](../03_language_reference/06_Declarations.md#constant-declaration)。
* 在构造器中,常量属性有且仅能被赋值一次。更多内容,请看 [在构造过程中给常量属性赋值](../02_language_guide/14_Initialization.md#assigning-constant-properties-during-initialization)。
* 多个可选绑定现在可以在`if`语句后面以逗号分隔的赋值列表的方式出现,更多内容,请看 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding)。
* 一个 [可选链表达式](../03_language_reference/04_Expressions.md#optional-chaining-expression) 必须出现在后缀表达式中。
* 协议类型转换不再局限于 `@obj` 修饰的协议了。
* 在运行时可能会失败的类型转换可以使用 `as?``as!` 运算符,而确保不会失败的类型转换现在使用 `as` 运算符。更多内容,请看 [类型转换运算符](../03_language_reference/04_Expressions.md#type-casting-operator)。
### 2014-10-16
* 更新至 Swift 1.1。
* 新增 [失败构造器](../02_language_guide/14_Initialization.md#failable-initializers) 的完整指引。
* 在协议中新增了 [失败构造器要求](../02_language_guide/21_Protocols.md#failable-initializer-requirements) 的描述。
* 常量和变量的 `Any` 类型现可以包含函数实例。更新了有关 `Any` 相关的示例来展示如何在 `switch` 语句中如何检查并转换到一个函数类型。
* 带有原始值的枚举类型增加了一个 `rawValue` 属性替代 `toRaw()` 方法,同时使用了一个以 `rawValue` 为参数的失败构造器来替代 `fromRaw()` 方法。更多的内容,请看 [原始值](../02_language_guide/08_Enumerations.md#raw-values) 和 [带原始值的枚举类型](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-a-raw-value-type)。
* 新增 [Failable Initializer](../03_language_reference/06_Declarations.md#failable-initializers) 的参考章节,它可以触发初始化失败。
* 自定义运算符现在可以包含 `?` 字符,更新了 [运算符](../03_language_reference/02_Lexical_Structure.md#operator) 涉及改进后的规则的部分,并且在 [自定义运算符](../02_language_guide/27-Advanced-Operators.md#custom-operators) 章节中删除了重复的运算符有效字符集合。
### 2014-08-18
* 描述 Swift 1.0 的新文档。Swift 是苹果公司发布的全新编程语言,用于 iOS 和 OS X 应用开发。
* 在协议中新增了 [对构造器的规定](../02_language_guide/21_Protocols.md#initializer-requirements) 章节。
* 新增 [类专属协议](../02_language_guide/21_Protocols.md#class-only-protocol) 章节。
* [断言](../02_language_guide/01_The_Basics.md#assertions-and-preconditions) 现在可以使用字符串内插语法,并删除了文档中有冲突的注释。
* 更新 [连接字符串和字符](../02_language_guide/03_Strings_and_Characters.md#concatenating-strings-and-characters) 章节来说明字符串和字符不能再用 `+` 号运算符或者复合加法运算符 `+=` 相互连接,这两种运算符现在只能用于字符串之间相连。请使用 `String` 类型的 `append` 方法在一个字符串的尾部增加单个字符。
* 在 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节增加了有关 `availability` 特性的一些内容。
* [可选类型](../02_language_guide/01_The_Basics.md#optionals) 若有值时,不再隐式的转换为 `true`,同样,若无值时,也不再隐式的转换为 `false`,这是为了避免在判别 optional `Bool` 的值时产生困惑。 替代的方案是,用`==``!=` 运算符显式地去判断 Optinal 是否是 `nil`,以确认其是否包含值。
* Swift 新增了一个 [Nil 合并运算符](../02_language_guide/02_Basic_Operators.md#nil-coalescing-operator) (`a ?? b`) , 该表达式中,如果 Optional `a` 的值存在,则取得它并返回,若 Optional `a``nil`,则返回默认值 `b`
* 更新和扩展 [字符串的比较](../02_language_guide/03_Strings_and_Characters.md#comparing-strings) ,用以反映和展示'字符串和字符的比较',以及'前缀prefix/后缀postfix比较'都开始基于扩展字符集extended grapheme clusters规范的等价比较。
* 现在,你可以通过下标赋值或者 [可选调用链](../02_language_guide/16_Optional_Chaining.md) 中的可变方法和操作符来给属性设值。相应地更新了有关 [通过可选链接访问属性](../02_language_guide/16_Optional_Chaining.md#accessing-properties-through-optional-chaining) 的内容,并扩展了 [通过可选链接调用方法](../02_language_guide/16_Optional_Chaining.md#calling-methods-through-optional-chaining) 时检查方法调用成功的示例,以显示如何检查属性设置是否成功。
* 在可选链中新增了 [访问可选类型的下标脚注](../02_language_guide/16_Optional_Chaining.md#accessing-subscripts-through-optional-chaining) 章节。
* 更新 [访问和修改数组](../02_language_guide/04_Collection_Types.md#accessing-and-modifying-a-dictionary) 章节以标示,从该版本起,不能再通过 `+=` 运算符给一个数组新增一个新的项。对应的替代方案是,使 `append` 方法,或者通过 `+=` 运算符来新增一个只有一个项的数组。
* 新增一处说明,在 [范围运算符](../02_language_guide/02_Basic_Operators.md#range-operators) 中,比如, `a..b``a..<b` ,起始值 `a` 不能大于结束值 `b`
* 重写 [继承](../02_language_guide/13_Inheritance.md) 篇章删除了本章中有关构造器重写的介绍性报道转而将更多的注意力放到新增的部分——子类的新功能以及如何通过重写overrides修改已有的功能。另外 [重写属性的 Getters 和 Setters](../02_language_guide/13_Inheritance.md#overriding-property-etters-and-setters) 中的例子已经被替换为展示如何重写一个 `description` 属性。 (而有关如何在子类的构造器中修改继承属性的默认值的例子,已经被移到 [构造过程](../02_language_guide/14_Initialization.md) 篇章。)
* 更新 [构造器的继承与重写](../02_language_guide/14_Initialization.md#initializer-inheritance-and-overriding) 章节以标示: 重写一个特定的构造器必须使用 `override` 修饰符。
* 更新 [Required 构造器](../02_language_guide/14_Initialization.md#required-initializers) 章节以标示:`required` 修饰符现在需要出现在所有子类的 required 构造器的声明中,而 required 构造器的实现,现在可以仅从父类自动继承。
* 中置Infix的 [运算符函数](../02_language_guide/27_Advanced_Operators.md#operator-functions) 不再需要 `@infix` 属性。
* [前置和后置运算符](../02_language_guide/27_Advanced_Operators.md#prefix-and-postfix-operators) 的 `@prefix``@postfix` 属性,已变更为 `prefix``postfix` 声明修饰符。
* 新增一处说明,在 Prefix 和 postfix 运算符被作用于同一个操作数时 [前置和后置运算符](../02_language_guide/27_Advanced_Operators.md#prefix-and-postfix-operators) 的执行顺序。
* [组合赋值运算符](../02_language_guide/27_Advanced_Operators.md#compound-assignment-operators) 的运算符函数不再使用 `@assignment` 属性来定义函数。
* 在定义 [自定义操作符](../02_language_guide/27_Advanced_Operators.md#custom-operators) 时,`修饰符Modifiers的出现顺序发生变化`。比如现在,你该编写 `prefix operator` 而不是 `operator prefix`
* 在 [声明修饰符](../03_language_reference/06_Declarations.md#declaration-modifiers) 章节新增了有关 `dynamic` 声明修饰符的内容。
* 新增有关 [字面量](../03_language_reference/02_Lexical_Structure.md#literal) 类型推导内容的内容。
* 新增更多有关柯里化函数的内容。
* 新增 [权限控制](../02_language_guide/26_Access_Control.md) 篇章。
* 更新 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 章节,在 Swift 中现在 `Character` 类型代表的是扩展字符集extended grapheme cluster中的一个 Unicode为此新增了 [Extended Grapheme Clusters](../02_language_guide/03_Strings_and_Characters.md#extended-grapheme-clusters) 章节。同时,[Unicode 标量](../02_language_guide/03-Strings-And-Characters.md#unicode-scalars-representation) 和 [字符串比较](../02_language_guide/03-Strings-And-Characters.md#comparing-strings) 章节新增了更多内容。
* 更新 [字符串字面量](../02_language_guide/03_Strings_and_Characters.md#string-literals) 章节在一个字符串中Unicode 标量Unicode scalars`\u{n}`的形式来表示,`n` 是一个最大可以有8位的16进制数。
* `NSString` `length` 属性已被映射到 Swift 的内建 `String`类型。(注意,这两属性的类型是`utf16Count`,而非 `utf16count`)。
* Swift 的内建 `String` 类型不再拥有 `uppercaseString``lowercaseString` 属性。在 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 章节中删除了对应部分,并更新了各种对应的代码用例。
* 新增 [没有外部名的构造器参数](../02_language_guide/14_Initialization.md#initializer-parameters-without-external-names) 章节。
* 新增 [Required 构造器](../02_language_guide/14_Initialization.md#required-initializers) 章节。
* 新增 [可选元组返回类型](../02_language_guide/06_Functions.md#optional-tuple-return-types) 章节。
* 更新 [类型注解](../02_language_guide/01_The_Basics.md#type-annotations) 章节,多个相关变量可以用"类型注解”在同一行中声明为同一类型。
* `@optional`, `@lazy`, `@final`, `@required` 等关键字被更新为 `optional`, `lazy`, `final`, `required` 参见 [声明修饰符](../03_language_reference/06_Declarations.md#declaration-modifiers)。
* 更新了整本书中有关 `..<` 的引用,从半闭区间改为了 [半开区间](../02_language_guide/02_Basic_Operators.md#half-open-range-operator)。
* 更新 [读取和修改字典](../02_language_guide/04_Collection_Types.md#accessing-and-modifying-a-dictionary) 章节, `Dictionary` 现在增加了一个 Boolean 型的属性:`isEmpty`
* 解释了哪些字符(集)可被用来定义 [自定义操作符](../02_language_guide/27_Advanced_Operators.md#custom-operators)。
* `nil` 和布尔运算中的 `true``false` 现在被定义为 [字面量](../03_language_reference/02_Lexical_Structure.md#literal)。
* Swift 中的数组 `Array` 类型从现在起具备了完整的值语义。具体内容被更新到 [集合的可变性](../02_language_guide/04_Collection_Types.md#mutability-of-collections) 和 [数组](../02_language_guide/04_Collection_Types.md#arrays) 两小节,以反映这个新的变化。 此外,还解释了如何给 Strings, Arrays 和 Dictionaries 进行赋值和拷贝。
* [数组类型速记语法](../02_language_guide/04_Collection_Types.md#array-type-shorthand-syntax) 从 `SomeType []` 更新为 ` [SomeType]`
* 新增 [字典类型的速记语法](../02_language_guide/04_Collection_Types.md#dictionary-type-shorthand-syntax) 章节,现在书写格式为: ` [KeyType: ValueType]`
* 新增 [字典键类型的哈希值](../02_language_guide/04_Collection_Types.md#hash-values-for-set-types) 章节。
* [闭包表达式](../02_language_guide/07_Closures.md#closure-expressions) 示例中使用新的全局函数 `sorted` 取代原先的全局函数 `sort` 去展示如何返回一个全新的数组。
* 更新 [结构体逐一成员构造器](../02_language_guide/14_Initialization.md#memberwise-initializers-for-structure-types) 章节,即使结构体的成员 `没有默认值`,逐一成员构造器也可以自动获得。
* [半开区间运算符](../02_language_guide/02_Basic_Operators.md#half-open-range-operator) 中`..` 更新为 `..<`
* 新增 [泛型拓展](../02_language_guide/22_Generics.md#extending-a-generic-type) 的示例。

View File

@ -1,3 +0,0 @@
# Swift 文档修订历史
本章描述了 Swift 文档修订历史。

View File

@ -1,314 +1,22 @@
# Swift 文档修订历史
> 2016.9.23: 已经更新到 Swift 3.0。
# 3.0 更新说明
Swift 3.0 是自 Swift 开源以来第一个大的版本更新。从语言角度不兼容之前的 Swift 2.2 和 Swift 2.3 版本。Swift 3.0 的更新说明,大家可以查看[官方 blog 的说明](https://swift.org/blog/swift-3-0-released/),也可以关注 [SwiftGG](http://swift.gg) 最新的文章。学习官方文档,是掌握语言特性点的最佳途径,感谢翻译的小伙伴们为 Swift 社区所做贡献!
### 2021-07-14
# 3.0 译者记录
相关[issue](https://github.com/numbbbbb/the-swift-programming-language-in-chinese/issues/628)
- Functions: [crayygy](https://github.com/crayygy)
- Control Flow: [Realank](https://github.com/Realank)
- Closures: [LanfordCai](https://github.com/LanfordCai)
- Protocols: [chenmingbiao](https://github.com/chenmingbiao)
- The Basics:[chenmingbiao](https://github.com/chenmingbiao)
- Advanced Operators: [mmoaay](https://github.com/mmoaay)
* 更新 [并发](../02_language_guide/28_Concurrency.md) 篇章里的示例,使用当前的并发 API。
### 2021-06-07
* 更新至 Swift 5.5。
* 在 [并发](../02_language_guide/28_Concurrency.md) 篇章、[Actor 声明](03_language_reference/06_Declarations.md#actor-declaration)、[异步函数和方法](03_language_reference/06_Declarations.md#asynchronous-functions-and-methods) 和 [Await 运算符](03_language_reference/04_Expressions.md#await-operators) 中新增了有关异步函数、任务和 actor 的内容。
### 2021-04-26
* 更新至 Swift 5.4。
* 新增 [结果构造器](../02_language_guide/27_Advanced_Operators.md#result-builders) 和 [resultBuilder](03_language_reference/07_Attributes.md#resultbuilder) 章节,其中包含结果构造器的内容。
* 新增 [指针类型的隐式转换](03_language_reference/04_Expressions.md#implicit-conversion-to-a-pointer) 章节,其中包含在函数调用时输入输出形参可以隐式转换到不安全指针的内容。
* 更新 [可变参数](../02_language_guide/06_Functions.md#variadic-parameters) 和 [函数声明](03_language_reference/06_Declarations.md#function-declaration) 章节,现在函数可以有多个可变参数了。
* 更新 [隐式成员表达式](03_language_reference/04_Expressions.md#implicit-member-expression) 章节,现在可以链式调用隐式成员表达式了。
### 2020-09-16
* 更新至 Swift 5.3。
* 在 [尾随闭包](../02_language_guide/07_Closures.md#trailing-closures) 章节中新增了有关多个尾随闭包的内容,在 [函数调用表达式](../03_language_reference/04_Expressions.md#function-call-expression) 章节中新增了有关尾随闭包如何匹配形参的内容。
* 在 [使用合成实现来采纳协议](../02_language_guide/21_Protocols.md#adopting-a-protocol-using-a-synthesized-implementation) 章节中新增了枚举合成实现 `Comparable` 的内容。
* 新增 [包含上下文关系的 where 分句](../02_language_guide/22_Generics.md#contextual-where-clauses) 章节,现在可以在更多位置编写范型 `where` 分句。
* 新增 [无主可选引用](../02_language_guide/24_Automatic_Reference_Counting.md#unowned-optional-references) 章节,其中包含可选值使用无主引用的内容。
* 在 [main](../03_language_reference/07_Attributes.md#main) 章节中新增有关 `@main` 特性的内容。
* 在 [字面量表达式](../03_language_reference/04_Expressions.md#literal-expression) 章节中新增 `#filePath`,并更新了 `#file` 的讨论。
* 更新 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures) 章节,现在闭包可以在更多场景隐式引用 `self`
* 更新 [用 Do-Catch 处理错误](../02_language_guide/17_Error_Handling.md#handling-errors-using-do-Catch) 和 [Do 语句](../03_language_reference/05_Statements.md#do-statements) 章节,现在 `catch` 子句可以匹配多个错误。
* 新增更多有关 `Any` 的内容并移动到新增的 [Any 类型](../03_language_reference/03_Types.md#any-type) 章节。
* 更新 [属性观察器](../02_language_guide/10_Properties.md#property-observers) 章节,现在 lazy 属性可以有观察器。
* 更新 [协议声明](../03_language_reference/06_Declarations.md#protocol-declaration) 章节,现在枚举的成员可以用于遵循协议的要求。
* 更新 [存储型变量和属性的观察器](../03_language_reference/06_Declarations.md#stored-variable-observers-and-property-observers) 章节,描述观察器之前 getter 是何时被调用的。
* 更新 [内存安全](../02_language_guide/25_Memory_Safety.md) 篇章并提及原子操作。
### 2020-03-24
* 更新至 Swift 5.2。
* 在 [Key-Path 表达式](../03_language_reference/04_Expressions.md#key-path-expression) 章节中新增了有关传递 key path 代替闭包的内容。
* 在 [特殊名称方法](../03_language_reference/06_Declarations.md#methods-with-special-names) 章节中新增了有关让类、结构体和枚举的实例作为函数调用语法糖的内容。
* 更新 [下标选项](../02_language_guide/12_Subscripts.md#subscript-options) 章节,现在下标支持形参默认值。
* 更新 [自身类型](../03_language_reference/03_Types.md#self-type-h) 章节,现在 `Self` 可以在更多上下文中使用。
* 更新 [隐式解析可选类型](../02_language_guide/01_The_Basics.md#implicityly-unwrapped-optionals) 章节,明确了隐式解析可选值可以作为可选值或者非可选值使用。
### 2019-09-10
* 更新至 Swift 5.1。
* 在 [不透明类型](../02_language_guide/23_Opaque_Types.md) 篇章中新增了有关函数返回值遵循指定协议,而不需要提供指定返回类型的内容。
* 在 [属性包装器](../02_language_guide/10_Properties.md#property-wrappers) 章节中新增了有关属性包装器的内容。
* 在 [冻结](../03_language_reference/07_Attributes.md#frozen) 章节中新增了有关因库演变而需要的枚举和结构体冻结。
* 新增 [隐式返回的函数](../02_language_guide/06_Functions.md#functions-with-an-implicit-return) 和 [简化 Getter 声明](../02_language_guide/10_Properties.md#shorthand-getter-declaration) 章节,其中包含函数省略 `return` 的内容。
* 在 [类型下标](../02_language_guide/12_Subscripts.md#type-subscripts) 章节中新增了有关在类型中使用下标的内容。
* 更新 [枚举 Case 模式匹配](../03_language_reference/08_Patterns.md#enumeration-case-pattern) 章节,现在枚举 case 模式匹配支持匹配可选值。
* 更新 [结构体的逐一成员构造器](../02_language_guide/14_Initialization.md#memberwise-initializers-for-structure-types) 章节,现在逐一成员构造器支持在属性有默认值时省略形参。
* 在 [动态查找成员](../03_language_reference/07_Attributes.md#dynamicmemberlookup) 章节中新增了有关在运行时用 key path 查找动态成员的内容。
* 在 [条件编译代码块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 中的目标环境里添加了 `macCatalyst`
* 更新 [自身类型](../03_language_reference/03_Types.md#self-type-h) 章节,现在 `Self` 可以指向当前类,结构体或者枚举声明时的类型。
### 2019-03-25
* 更新至 Swift 5。
* 新增 [拓展字符串分隔符](../02_language_guide/03_Strings_and_Characters.md#extended-string-delimiters) 章节。更新 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节,拓展有关字符串分隔符的内容。
* 新增 [动态调用](../03_language_reference/07_Attributes.md#dynamiccallable) 章节,其中包含使用 `dynamicCallable` 属性动态调用实例作为函数的内容。
* 新增 [unknown](../03_language_reference/07_Attributes.md#unknown) 和 [未来枚举匹配](../03_language_reference/05_Statements.md#future-case) 章节,其中包含了使用 `unknown` 来处理未来枚举可能发生改变的情形。
* 在 [Key-Path 表达式](../03_language_reference/04_Expressions.md#key-path-expression) 章节新增了有关标示 key path (\\.self) 的内容。
* 在 [可选编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节新增了有关小于比较符 `<` 的内容。
### 2018-09-17
* 更新至 Swift 4.2。
* 在 [遍历枚举情形](../02_language_guide/08_Enumerations.md#iterating-over-enumeration-cases) 章节新增了有关访问所有枚举情形的内容。
* 在 [编译诊断](../03_language_reference/05_Statements.md#compile-time-diagnostic-statement) 章节新增了有关 `#error``#warning` 的内容。
* 在 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `inlinable``usableFromInline` 属性的内容。
* 在 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `requires-stored-property-inits``warn-unqualified-access` 属性的内容。
* 在 [可选编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节新增了有关如何根据 Swift 编译器版本对代码进行对应编译处理的内容。
* 在 [字面量语法](../03_language_reference/04_Expressions.md#literal-expression) 章节新增了有关 `#dsohandle` 的内容。
### 2018-03-29
* 更新至 Swift 4.1。
* 在 [等价运算符](../02_language_guide/27_Advanced_Operators.md#equivalence-operators) 章节新增了有关等价运算符的合成实现的内容。
* 在 [声明](../03_language_reference/06_Declarations.md) 篇章中 [声明拓展](../03_language_reference/06_Declarations.md#extension-declaration) 章节和 [协议](../02_language_guide/21_Protocols.md) 篇章中 [有条件地遵循协议](../02_language_guide/21_Protocols.md#Conditionally-Conforming-to-a-Protocol) 章节新增了有关协议有条件遵循的内容。
* 在 [关联类型约束中使用协议](../02_language_guide/22_Generics.md#using-a-protocol-in-its-associated-types-constraints) 章节中新增了有关递归协议约束的内容。
* 在 [条件编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节中新增了有关 `canImport()``targetEnvironment()` 平台条件的内容。
### 2017-12-04
* 更新至 Swift 4.0.3。
* 更新 [Key-Path 表达式](../03_language_reference/04_Expressions.md#key-path-expression) 章节,现在 key path 支持下标子路径。
### 2017-09-19
* 更新至 Swift 4.0。
* 在 [内存安全](../02_language_guide/25_Memory_Safety.md) 章节新增了有关内存互斥访问的内容。
* 新增 [带有泛型 Where 子句联类型](../02_language_guide/22_Generics.md#associated-types-with-a-generic-where-clause) 章节,现在可以使用泛型 `where` 子句约束关联类型。
* 在 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 篇章中 [字面量](../02_language_guide/03_Strings_and_Characters.md#string-literals) 章节以及 [词法结构](../03_language_reference/02_Lexical_Structure.md) 篇章的 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节中新增了有关多行字符串字面量的内容。
* 更新 [声明属性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 中 `objc` 属性的讨论,现在该属性会在更少的位置被推断出来。
* 新增 [范型下标](../02_language_guide/22_Generics.md#generic-subscripts) 章节,现在下标也支持范型特性了。
* 更新 [协议](../02_language_guide/21_Protocols.md) 篇章中 [协议组合](../02_language_guide/21_Protocols.md#protocol-composition) 章节和 [类型](../03_language_reference/03_Types.md) 篇章中 [协议组合类型](../03_language_reference/03_Types.md#protocol-composition-type-h) 章节的讨论,现在协议组合类型支持进行父类约束了。
* 更新 [拓展声明](../03_language_reference/06_Declarations.md#extension-declaration) 中有关协议扩展的讨论,现在它们不支持 `final` 特性了。
* 在 [断言和前置条件](../02_language_guide/01_The_Basics.md#assertions-and-preconditions) 章节中新增了部分前置条件和致命错误的内容。
### 2017-03-27
* 更新至 Swift 3.1。
* 新增 [范型 Where 子句扩展](../02_language_guide/22_Generics.md#extensions-with-a-generic-where-clause) 章节,包含需要的扩展内容。
* 在 [For-In 循环](../02_language_guide/05_Control_Flow.md#for-in-loops) 章节中新增了区间迭代的例子。
* 在 [可失败构造器](../02_language_guide/14_Initialization.md#failable-initializers) 章节中新增了可失败数值转换的例子。
* 在 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关使用 Swift 语言版本的 `available` 特性的内容 。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节中的讨论,注意在写函数类型时不允许使用参数标签。
* 更新 [条件编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节中的 Swift 语言版本号的讨论,现在可以使用可选的补丁版本号。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节的讨论,现在 Swift 区分了采用多参数的函数和采用元组类型的单个参数的函数。
* 在 [表达式](../03_language_reference/04_Expressions.md) 篇章中删除了动态表达式的章节,现在 `type(of:)` 是 Swift 标准库函数。
### 2016-10-27
* 更新至 Swift 3.0.1。
* 更新 [自动引用计数](../02_language_guide/24_Automatic_Reference_Counting.md) 章节中有关 weak 和 unowned 引用的讨论。
* 在 [声明标识符](../03_language_reference/06_Declarations.md#declaration-modifiers) 章节中新增了有关新的标识符 `unowned``unowend(safe)``unowned(unsafe)` 的内容。
* 在 [Any 和 AnyObject 的类型转换](../02_language_guide/18_Type_Casting.md#type-casting-for-any-and-anyobject) 章节中新增了一处说明,有关使用类型 `Any` 作为可选值。
* 更新 [表达式](../03_language_reference/04_Expressions.md) 章节,把括号表达式和元组表达式的描述分开。
### 2016-09-13
* 更新至 Swift 3.0。
* 更新 [函数](../02_language_guide/06_Functions.md) 篇章和 [函数声明](../03_language_reference/06_Declarations.md#function-declaration) 章节中有关函数的讨论,所有函数参数默认都有函数标签。
* 更新 [高级操作符](../02_language_guide/27_Advanced_Operators.md) 篇章中有关操作符的讨论,现在你可以作为类型函数来实现,替代之前的全局函数实现方式。
* 在 [访问控制](../02_language_guide/26_Access_Control.md) 章节中新增有关对新的访问级别描述符 `open``fileprivate` 的内容。
* 更新 [函数声明](../03_language_reference/06_Declarations.md#function-declaration) 章节中有关 `inout` 的讨论,注意它现在出现在参数类型的前面,而不是在参数名称的前面。
* 更新 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures) 和 [自动闭包](../02_language_guide/07_Closures.md#autoclosures) 章节还有 [属性](../03_language_reference/07_Attributes.md) 篇章中有关 `@noescape``@autoclosure` 的讨论,现在他们是类型属性,而不是定义属性。
* 在 [高级操作符](../02_language_guide/27_Advanced_Operators.md) 篇章中 [自定义中缀操作符的优先级](./02_language_guide/27_Advanced_Operators.md#precedence-and-associativity-for-custom-infix-operators) 章节和 [定义](../03_language_reference/06_Declarations.md) 篇章中 [优先级组声明](../03_language_reference/06_Declarations.md#precedence-group-declaration-modifiers) 章节中新增了有关操作符优先级组的内容。
* 更新一些讨论,使用 macOS 替换掉 OS X Error 替换掉 ErrorProtocol。更新一些协议名称比如使用 ExpressibleByStringLiteral 替换掉 StringLiteralConvertible。
* 更新 [泛型](../02_language_guide/22_Generics.md) 篇章中 [泛型 Where 语句](../02_language_guide/22_Generics.md#extensions-with-a-generic-where-clause) 章节和 [泛型形参和实参](../03_language_reference/09_Generic_Parameters_and_Arguments.md) 篇章的讨论,现在泛型的 where 语句写在一个声明的最后。
* 更新 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures) 章节中的讨论,现在闭包默认为非逃逸的。
* 更新 [基础部分](../02_language_guide/01_The_Basics.md) 篇章中 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章中 [While 语句](../03_language_reference/05_Statements.md#while-statement) 章节中的讨论,现在 if`while``guard` 语句使用逗号分隔条件列表,不需要使用 `where` 语句。
* 在 [控制流](../02_language_guide/05_Control_Flow.md) 篇章中 [Switch](../02_language_guide/05_Control_Flow.md#switch) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章中 [Switch 语句](../03_language_reference/05_Statements.md#switch-statement) 章节中新增了 switch cases 可以使用多模式的内容。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节有关现在函数参数标签不包含在函数类型中的讨论。
* 更新 [协议](../02_language_guide/21_Protocols.md) 篇章中 [协议组合](../02_language_guide/21_Protocols.md#protocol-composition) 章节和 [类型](../03_language_reference/03_Types.md) 篇章中 [协议组合类型](../03_language_reference/03_Types.md#protocol-composition-type-h) 章节中有关使用新的 Protocol1 & Protocol2 语法的内容。
* 更新动态类型表达式章节中使用新的 `type(of:)` 表达式的讨论。
* 更新 [行控制表达式](../03_language_reference/05_Statements.md#line-control-statement) 章节中使用 `#sourceLocation(file:line:)` 表达式的讨论。
* 更新 [永不返回函数](../03_language_reference/06_Declarations.md#functions-that-never-return) 章节中使用 新的 `Never` 类型的讨论。
* 在 [字面量表达式](../03_language_reference/04_Expressions.md#literal-expression) 章节中新增了有关 `playground` 字面量的内容。
* 更新 [In-Out 参数](../03_language_reference/06_Declarations.md#in-out-parameters) 章节,标明只有非逃逸闭包能捕获 `in-out` 参数。
* 更新 [默认参数值](../02_language_guide/06_Functions.md#default-parameter-values) 章节,现在默认参数不能在调用时候重新排序。
* 更新 [属性](../03_language_reference/07_Attributes.md) 篇章中有关属性参数使用分号的说明。
* 在 [重新抛出函数和方法](../03_language_reference/06_Declarations.md#rethrowing-functions-and-methods) 章节中新增了有关在 catch 代码块中抛出错误的重新抛出函数的内容。
* 在 [Selector 表达式](../03_language_reference/04_Expressions.md#selector-expression7) 章节中新增了中有关访问 Objective-C 中 Selector 的 getter 和 setter 的内容。
* 在 [类型别名声明](../03_language_reference/06_Declarations.md#type-alias-declaration) 章节中中新增了有关泛型类型别名和在协议内使用类型别名的内容。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节中有关函数类型的讨论,标明函数类型作为参数类型必须使用括号包裹。
* 更新 [属性](../03_language_reference/07_Attributes.md) 篇章,标明 `@IBAction``@IBOutlet``@NSManaged` 隐式含有 `@objc` 属性。
* 在 [声明属性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `@GKInspectable` 的内容。
* 更新 [可选协议要求](../02_language_guide/21_Protocols.md#optional-protocol-requirements) 章节中有关只能在与 `Objective-C` 交互的代码中才能使用可选协议要求的内容。
* 删除 [函数声明](../03_language_reference/06_Declarations.md#function-declaration) 章节中有关显式使用 `let` 关键字作为函数参数的内容。
* 删除 [语句](../03_language_reference/05_Statements.md) 章节中有关 `Boolean` 协议的内容, 现在这个协议已经被 Swift 标准库删除。
* 更正 [声明属性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@NSApplicationMain` 协议的内容。
### 2016-03-21
* 更新至 Swift 2.2。
* 在 [编译配置语句](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节新增了中有关如何根据 Swift 版本进行条件编译。
* 在 [显示成员表达式](../03_language_reference/04_Expressions.md#explicit-member-expression) 章节中新增了有关如何区分只有参数名不同的方法和构造器的内容。
* 在 [选择器表达式](../03_language_reference/04_Expressions.md#selector-expression7) 章节中新增了了针对 Objective-C 选择器的 `#selector` 语法。
* 更新 [关联类型](../02_language_guide/22_Generics.md#associated-types) 和 [协议关联类型声明](../03_language_reference/06_Declarations.md#protocol-associated-type-declaration) 章节中有关使用 `associatedtype` 关键词修饰关联类型的讨论。
* 更新 [可失败构造器](../02_language_guide/14_Initialization.md#failable-initializers) 章节中有关当构造器在实例完全初始化之前返回 `nil` 的相关内容。
* 在 [比较运算符](../02_language_guide/02_Basic_Operators.md#comparison-operators) 章节中新增了比较元组的内容。
* 在 [关键字和标点符号](../03_language_reference/02_Lexical_Structure.md#keywords-and-punctuation) 章节中新增了使用关键字作为外部参数名的内容。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@objc` 特性的讨论,并指出枚举和枚举用例。
* 更新 [操作符](../03_language_reference/02_Lexical_Structure.md#operator) 章节中对于自定义运算符的包含了 `.` 的讨论。
* 在 [重新抛出错误的函数和方法](../03_language_reference/06_Declarations.md#rethrowing-functions-and-methods) 章节中新增了一处说明,重新抛出错误函数不能直接抛出错误。
* 在 [属性观察器](../02_language_guide/10_Properties.md#property-observers) 章节中新增了一处说明,当作为 in-out 参数传递属性时,属性观察器的调用行为。
* 在 [Swift 初见](../01_welcome_to_swift/03_a_swift_tour.md) 篇章中新增了错误处理的章节。
* 更新 [弱引用](../02_language_guide/24_Automatic_Reference_Counting.md#weak-references) 章节中的图片用以更清楚的展示重新分配过程。
* 删除 C 语言风格的 `for` 循环,`++` 前缀和后缀运算符,以及 `--` 前缀和后缀运算符。
* 删除对变量函数参数和柯里化函数的特殊语法的讨论。
### 2015-10-20
* 更新至 Swift 2.1。
* 更新 [字符串插值](../02_language_guide/03_Strings_and_Characters.md#string-interpolation) 和 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节,现在字符串插值可包含字符串字面量。
* 在 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures) 章节中新增了有关 `@noescape` 属性的相关内容。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 和 [编译配置语句](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节中与 tvOS 相关的内容。
* 在 [In-Out 参数](../03_language_reference/06_Declarations.md#in-out-parameters) 章节中新增了与 in-out 参数行为相关的内容。
* 在 [捕获列表](../03_language_reference/04_Expressions.md#capture-lists) 章节新增了有关指定闭包捕获列表被捕获时捕获值的相关内容。
* 更新 [可选链式调用访问属性](../02_language_guide/16_Optional_Chaining.md#accessing-properties-through-optional-chaining) 章节,阐明了如何通过可选链式调用进行赋值。
* 改进 [自动闭包](../02_language_guide/07_Closures.md#autoclosures) 章节中对自闭包的讨论。
* 在 [Swift 初见](../01_welcome_to_swift/03_a_swift_tour.md) 篇章中新增了一个使用 `??` 操作符的例子。
### 2015-09-16
* 更新至 Swift 2.0。
* 在 [错误处理](../02_language_guide/17_Error_Handling.md) 篇章中新增了有关错误处理的相关内容,包括 [Do 语句](../03_language_reference/05_Statements.md#do-statement)、 [Throw 语句](../03_language_reference/05_Statements.md#throw-statement)、 [Defer 语句](../03_language_reference/05_Statements.md##defer-statements) 以及 [try 运算符](../03_language_reference/04_Expressions.md#try-operator)。
* 更新 [错误表示和抛出](../02_language_guide/17_Error_Handling.md#representing-and-throwing-errors) 章节,现在所有类型都可以遵循 `ErrorType` 协议了。
* 在 [将错误装换成可选值](../02_language_guide/17_Error_Handling.md#converting-errors-to-optional-values) 篇章增加了 `try?` 关键字相关内容。
* 在 [枚举](../02_language_guide/08_Enumerations.md) 篇章的 [递归枚举](../02_language_guide/08_Enumerations.md#recursive-enumerations) 章节以及以及 [声明](../03_language_reference/06_Declarations.md) 篇章的 [任意类型用例的枚举](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-any-type) 章节中新增了递归枚举相关内容。
* 在 [控制流](../02_language_guide/05_Control_Flow.md) 篇章的 [API 可用性检查](../02_language_guide/05_Control_Flow.md#checking-api-availability) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章的 [可用性条件](../03_language_reference/05_Statements.md#availability-condition) 章节中新增了有关 API 可用性检查相关的内容。
* 在 [控制流](../02_language_guide/05_Control_Flow.md) 篇章的 [尽早退出](../02_language_guide/05_Control_Flow.md#early-exit) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章的 [Guard 语句](../03_language_reference/05_Statements.md#guard-statement) 章节新增了与 `guard` 语句相关的内容。
* 在 [协议](../02_language_guide/21_Protocols.md) 篇章中 [协议扩展](../02_language_guide/21_Protocols.md#protocol-extensions) 章节中新增了有关协议扩展的内容。
* 在 [访问控制](../02_language_guide/26_Access_Control.md) 篇章的 [单元测试 target 的访问级别](../02_language_guide/26_Access_Control.md#access-levels-for-unit-test-targets) 章节中新增了有关单元测试访问控制相关的内容。
* 在 [模式](../03_language_reference/08_Patterns.md) 篇章的 [可选模式](../03_language_reference/08_Patterns.md#optional-pattern) 章节中新增了可选模式相关内容。
* 更新 [Repeat-While](../02_language_guide/05_Control_Flow.md#repeat-while) 章节中有关 `repeat-while` 循环相关的内容。
* 更新 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 章节,现在 `String` 类型在 Swift 标准库中不再遵循 `CollectionType` 协议。
* 在 [常量与变量打印](../02_language_guide/01_The_Basics.md#printing) 章节中新增了新 Swift 标准库中有关 `print(-:separator:terminator) ` 相关内容。
* 在 [枚举](../02_language_guide/08_Enumerations.md) 篇章的 [原始值的隐式赋值](../02_language_guide/08_Enumerations.md#implicitly-assigned-raw-values) 章节和 [声明](../03_language_reference/06_Declarations.md) 篇章的 [包含原始值类型的枚举](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-a-raw-value-type) 章节中新增了有关包含 `String` 原始值的枚举用例的行为相关内容。
* 在 [自动闭包](../02_language_guide/07_Closures.md#autoclosures) 章节中新增了有关 `@autoclosure` 特性的相关内容,包括它的 `@autoclosure(escaping)` 形式。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@avaliable``warn-unused-result` 特性的相关内容。
* 更新 [类型特性](../03_language_reference/07_Attributes.md#type-attributes) 章节中有关 `@convention` 特性的相关内容。
* 在 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding) 章节中新增了有关使用 `where` 子句进行多可选绑定的相关内容。
* 在 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节中新增了有关在编译时使用 `+` 运算符拼接字符串字面量的相关内容。
* 在 [元类型](../03_language_reference/03_Types.md#metatype-type-h) 章节中新增了有关元类型值的比较和使用它们通过构造器表达式构造实例相关内容。
* 在 [断言调试](../02_language_guide/01_The_Basics.md#debugging-with-assertions) 章节中新增了一处说明,有关用户定义断言何时会失效。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中对 `@NSManaged` 特性的讨论,现在这个特性可以被应用到一个确定实例方法。
* 更新 [可变参数](../02_language_guide/06_Functions.md#variadic-parameters) 章节,现在可变参数可以声明在函数参数列表的任意位置中。
* 在 [重写可失败构造器](../02_language_guide/14_Initialization.md#overriding-a-failable-initializer) 章节中新增了有关非可失败构造器相当于一个可失败构造器通过父类构造器的结果进行强制拆包的相关内容。
* 在 [任意类型用例的枚举](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-any-type) 章节中新增了有关枚举用例作为函数的内容。
* 在 [构造器表达式](../03_language_reference/04_Expressions.md#initializer-expression) 章节中新增了有关显式引用一个构造器相关内容。
* 在 [编译控制语句](../03_language_reference/05_Statements.md#compiler-control-statements) 章节中新增了有关编译内容以及行控制语句相关内容。
* 在 [元类型](../03_language_reference/03_Types.md#metatype-type-h) 章节新增了一处说明,有关如何从元类型值中构造类实例相关内容。
* 在 [弱引用](../02_language_guide/24_Automatic_Reference_Counting.md#weak-references) 章节新增了一处说明,有关弱引用作为缓存所存在的不足。
* 更新 [类型特性](../02_language_guide/10_Properties.md#type-properties) 章节,提到了存储型特性其实是懒加载。
* 更新 [捕获类型](../02_language_guide/07_Closures.md#capturing-values) 章节,阐明了变量和常量在闭包中如何被捕获。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节,用以描述何时在类中使用 `@objc` 关键字。
* 在 [错误处理](../02_language_guide/17_Error_Handling.md#handling-errors) 章节中新增了一处说明,有关执行 `throw` 语句的性能。在 [Do 语句](../03_language_reference/05_Statements.md#do-statement) 章节的 do 语句部分也新增了类似内容。
* 更新 [类型特性](../02_language_guide/10_Properties.md#type-properties) 章节中有关类、结构体和枚举的存储型和计算型特性相关的内容。
* 更新 [Break 语句](../03_language_reference/05_Statements.md#break-statement) 章节中有关带标签的 break 语句相关内容。
* 在 [属性观察器](../02_language_guide/10_Properties.md#property-observers) 章节更新了一处说明,用来明确 `willSet``didSet` 观察器的行为。
* 在 [访问级别](../02_language_guide/26_Access_Control.md#access-levels) 章节新增了有关 `private` 作用域的相关内容说明。
* 在 [弱引用](../02_language_guide/24_Automatic_Reference_Counting.md#weak-references) 章节新增了有关弱应用在垃圾回收系统和 ARC 之间的区别的说明。
* 更新 [字符串字面量中特殊字符](../02_language_guide/03_Strings_and_Characters.md#special-characters-in-string-literals) 章节,对 Unicode 标量更精确定义。
### 2015-04-08
* 更新至 Swift 1.2。
* Swift 现在自身提供了一个 `Set` 集合类型,更多内容,请看 [Sets](../02_language_guide/04_Collection_Types.md#sets) 。
* `@autoclosure` 现在是一个参数声明的属性,而不是参数类型的属性。这里还有一个新的参数声明属性 `@noescape`。更多内容,请看 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 。
* 对于类型属性和方法现在可以使用 `static` 关键字作为声明描述符,更多内容,请看 [类型变量属性](../03_language_reference/06_Declarations.md#type-variable-properties)。
* Swift 现在包含一个 `as?``as!` 的向下可失败类型转换运算符。更多内容,请看 [协议遵循性检查](../02_language_guide/21_Protocols.md#checking-for-protocol-conformance)。
* 新增 [字符串索引](../02_language_guide/03_Strings_and_Characters.md#string-indices) 的新指导章节。
* 在 [溢出运算符](../02_language_guide/27_Advanced_Operators.md#overflow-operators) 一节中删除了溢出除运算符(`&/`)和求余溢出运算符(`&%`)。
* 更新常量和常量属性在声明和构造时的规则,更多内容,请看 [常量声明](../03_language_reference/06_Declarations.md#constant-declaration) 。
* 更新字符串字面量中 Unicode 标量集的定义,请看 [字符串字面量中的特殊字符](../02_language_guide/03_Strings_and_Characters.md#special-characters-in-string-literals) 。
* 更新 [区间运算符](../02_language_guide/02_Basic_Operators.md#range-operators) 章节,注意当半开区间运算符含有相同的起止索引时,其区间为空。
* 更新 [闭包引用类型](../02_language_guide/07_Closures.md#closures-are-reference-types) 章节,对于变量的捕获规则进行了阐明。
* 更新 [值溢出](../02_language_guide/27_Advanced_Operators.md#value-overflow) 章节,对有符号整数和无符号整数的溢出行为进行了阐明。
* 更新 [协议声明](../03_language_reference/06_Declarations.md#protocol-declaration) 章节,对协议声明时的作用域和成员等内容进行了阐明。
* 更新 [捕获列表](../02_language_guide/24_Automatic_Reference_Counting.md#defining-a-capture-list) 章节,对于闭包捕获列表中的弱引用和无主引用的使用语法进行了阐明。
* 更新 [运算符](../03_language_reference/02_Lexical_Structure.md#operator) 章节明确指明一些例子来说明自定义运算符所支持的特性如数学运算符各种符号Unicode 符号块等。
* 在函数作用域中的常量声明时可以不被初始化,它必须在第一次使用前被赋值。更多的内容,请看 [常量声明](../03_language_reference/06_Declarations.md#constant-declaration)。
* 在构造器中,常量属性有且仅能被赋值一次。更多内容,请看 [在构造过程中给常量属性赋值](../02_language_guide/14_Initialization.md#assigning-constant-properties-during-initialization)。
* 多个可选绑定现在可以在`if`语句后面以逗号分隔的赋值列表的方式出现,更多内容,请看 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding)。
* 一个 [可选链表达式](../03_language_reference/04_Expressions.md#optional-chaining-expression) 必须出现在后缀表达式中。
* 协议类型转换不再局限于 `@obj` 修饰的协议了。
* 在运行时可能会失败的类型转换可以使用 `as?``as!` 运算符,而确保不会失败的类型转换现在使用 `as` 运算符。更多内容,请看 [类型转换运算符](../03_language_reference/04_Expressions.md#type-casting-operator)。
### 2014-10-16
* 更新至 Swift 1.1。
* 新增 [失败构造器](../02_language_guide/14_Initialization.md#failable-initializers) 的完整指引。
* 在协议中新增了 [失败构造器要求](../02_language_guide/21_Protocols.md#failable-initializer-requirements) 的描述。
* 常量和变量的 `Any` 类型现可以包含函数实例。更新了有关 `Any` 相关的示例来展示如何在 `switch` 语句中如何检查并转换到一个函数类型。
* 带有原始值的枚举类型增加了一个 `rawValue` 属性替代 `toRaw()` 方法,同时使用了一个以 `rawValue` 为参数的失败构造器来替代 `fromRaw()` 方法。更多的内容,请看 [原始值](../02_language_guide/08_Enumerations.md#raw-values) 和 [带原始值的枚举类型](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-a-raw-value-type)。
* 新增 [Failable Initializer](../03_language_reference/06_Declarations.md#failable-initializers) 的参考章节,它可以触发初始化失败。
* 自定义运算符现在可以包含 `?` 字符,更新了 [运算符](../03_language_reference/02_Lexical_Structure.md#operator) 涉及改进后的规则的部分,并且在 [自定义运算符](../02_language_guide/27-Advanced-Operators.md#custom-operators) 章节中删除了重复的运算符有效字符集合。
### 2014-08-18
* 描述 Swift 1.0 的新文档。Swift 是苹果公司发布的全新编程语言,用于 iOS 和 OS X 应用开发。
* 在协议中新增了 [对构造器的规定](../02_language_guide/21_Protocols.md#initializer-requirements) 章节。
* 新增 [类专属协议](../02_language_guide/21_Protocols.md#class-only-protocol) 章节。
* [断言](../02_language_guide/01_The_Basics.md#assertions-and-preconditions) 现在可以使用字符串内插语法,并删除了文档中有冲突的注释。
* 更新 [连接字符串和字符](../02_language_guide/03_Strings_and_Characters.md#concatenating-strings-and-characters) 章节来说明字符串和字符不能再用 `+` 号运算符或者复合加法运算符 `+=` 相互连接,这两种运算符现在只能用于字符串之间相连。请使用 `String` 类型的 `append` 方法在一个字符串的尾部增加单个字符。
* 在 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节增加了有关 `availability` 特性的一些内容。
* [可选类型](../02_language_guide/01_The_Basics.md#optionals) 若有值时,不再隐式的转换为 `true`,同样,若无值时,也不再隐式的转换为 `false`,这是为了避免在判别 optional `Bool` 的值时产生困惑。 替代的方案是,用`==``!=` 运算符显式地去判断 Optinal 是否是 `nil`,以确认其是否包含值。
* Swift 新增了一个 [Nil 合并运算符](../02_language_guide/02_Basic_Operators.md#nil-coalescing-operator) (`a ?? b`) , 该表达式中,如果 Optional `a` 的值存在,则取得它并返回,若 Optional `a``nil`,则返回默认值 `b`
* 更新和扩展 [字符串的比较](../02_language_guide/03_Strings_and_Characters.md#comparing-strings) ,用以反映和展示'字符串和字符的比较',以及'前缀prefix/后缀postfix比较'都开始基于扩展字符集extended grapheme clusters规范的等价比较。
* 现在,你可以通过下标赋值或者 [可选调用链](../02_language_guide/16_Optional_Chaining.md) 中的可变方法和操作符来给属性设值。相应地更新了有关 [通过可选链接访问属性](../02_language_guide/16_Optional_Chaining.md#accessing-properties-through-optional-chaining) 的内容,并扩展了 [通过可选链接调用方法](../02_language_guide/16_Optional_Chaining.md#calling-methods-through-optional-chaining) 时检查方法调用成功的示例,以显示如何检查属性设置是否成功。
* 在可选链中新增了 [访问可选类型的下标脚注](../02_language_guide/16_Optional_Chaining.md#accessing-subscripts-through-optional-chaining) 章节。
* 更新 [访问和修改数组](../02_language_guide/04_Collection_Types.md#accessing-and-modifying-a-dictionary) 章节以标示,从该版本起,不能再通过 `+=` 运算符给一个数组新增一个新的项。对应的替代方案是,使 `append` 方法,或者通过 `+=` 运算符来新增一个只有一个项的数组。
* 新增一处说明,在 [范围运算符](../02_language_guide/02_Basic_Operators.md#range-operators) 中,比如, `a..b``a..<b` ,起始值 `a` 不能大于结束值 `b`
* 重写 [继承](../02_language_guide/13_Inheritance.md) 篇章删除了本章中有关构造器重写的介绍性报道转而将更多的注意力放到新增的部分——子类的新功能以及如何通过重写overrides修改已有的功能。另外 [重写属性的 Getters 和 Setters](../02_language_guide/13_Inheritance.md#overriding-property-etters-and-setters) 中的例子已经被替换为展示如何重写一个 `description` 属性。 (而有关如何在子类的构造器中修改继承属性的默认值的例子,已经被移到 [构造过程](../02_language_guide/14_Initialization.md) 篇章。)
* 更新 [构造器的继承与重写](../02_language_guide/14_Initialization.md#initializer-inheritance-and-overriding) 章节以标示: 重写一个特定的构造器必须使用 `override` 修饰符。
* 更新 [Required 构造器](../02_language_guide/14_Initialization.md#required-initializers) 章节以标示:`required` 修饰符现在需要出现在所有子类的 required 构造器的声明中,而 required 构造器的实现,现在可以仅从父类自动继承。
* 中置Infix的 [运算符函数](../02_language_guide/27_Advanced_Operators.md#operator-functions) 不再需要 `@infix` 属性。
* [前置和后置运算符](../02_language_guide/27_Advanced_Operators.md#prefix-and-postfix-operators) 的 `@prefix``@postfix` 属性,已变更为 `prefix``postfix` 声明修饰符。
* 新增一处说明,在 Prefix 和 postfix 运算符被作用于同一个操作数时 [前置和后置运算符](../02_language_guide/27_Advanced_Operators.md#prefix-and-postfix-operators) 的执行顺序。
* [组合赋值运算符](../02_language_guide/27_Advanced_Operators.md#compound-assignment-operators) 的运算符函数不再使用 `@assignment` 属性来定义函数。
* 在定义 [自定义操作符](../02_language_guide/27_Advanced_Operators.md#custom-operators) 时,`修饰符Modifiers的出现顺序发生变化`。比如现在,你该编写 `prefix operator` 而不是 `operator prefix`
* 在 [声明修饰符](../03_language_reference/06_Declarations.md#declaration-modifiers) 章节新增了有关 `dynamic` 声明修饰符的内容。
* 新增有关 [字面量](../03_language_reference/02_Lexical_Structure.md#literal) 类型推导内容的内容。
* 新增更多有关柯里化函数的内容。
* 新增 [权限控制](../02_language_guide/26_Access_Control.md) 篇章。
* 更新 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 章节,在 Swift 中现在 `Character` 类型代表的是扩展字符集extended grapheme cluster中的一个 Unicode为此新增了 [Extended Grapheme Clusters](../02_language_guide/03_Strings_and_Characters.md#extended-grapheme-clusters) 章节。同时,[Unicode 标量](../02_language_guide/03-Strings-And-Characters.md#unicode-scalars-representation) 和 [字符串比较](../02_language_guide/03-Strings-And-Characters.md#comparing-strings) 章节新增了更多内容。
* 更新 [字符串字面量](../02_language_guide/03_Strings_and_Characters.md#string-literals) 章节在一个字符串中Unicode 标量Unicode scalars`\u{n}`的形式来表示,`n` 是一个最大可以有8位的16进制数。
* `NSString` `length` 属性已被映射到 Swift 的内建 `String`类型。(注意,这两属性的类型是`utf16Count`,而非 `utf16count`)。
* Swift 的内建 `String` 类型不再拥有 `uppercaseString``lowercaseString` 属性。在 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 章节中删除了对应部分,并更新了各种对应的代码用例。
* 新增 [没有外部名的构造器参数](../02_language_guide/14_Initialization.md#initializer-parameters-without-external-names) 章节。
* 新增 [Required 构造器](../02_language_guide/14_Initialization.md#required-initializers) 章节。
* 新增 [可选元组返回类型](../02_language_guide/06_Functions.md#optional-tuple-return-types) 章节。
* 更新 [类型注解](../02_language_guide/01_The_Basics.md#type-annotations) 章节,多个相关变量可以用"类型注解”在同一行中声明为同一类型。
* `@optional`, `@lazy`, `@final`, `@required` 等关键字被更新为 `optional`, `lazy`, `final`, `required` 参见 [声明修饰符](../03_language_reference/06_Declarations.md#declaration-modifiers)。
* 更新了整本书中有关 `..<` 的引用,从半闭区间改为了 [半开区间](../02_language_guide/02_Basic_Operators.md#half-open-range-operator)。
* 更新 [读取和修改字典](../02_language_guide/04_Collection_Types.md#accessing-and-modifying-a-dictionary) 章节, `Dictionary` 现在增加了一个 Boolean 型的属性:`isEmpty`
* 解释了哪些字符(集)可被用来定义 [自定义操作符](../02_language_guide/27_Advanced_Operators.md#custom-operators)。
* `nil` 和布尔运算中的 `true``false` 现在被定义为 [字面量](../03_language_reference/02_Lexical_Structure.md#literal)。
* Swift 中的数组 `Array` 类型从现在起具备了完整的值语义。具体内容被更新到 [集合的可变性](../02_language_guide/04_Collection_Types.md#mutability-of-collections) 和 [数组](../02_language_guide/04_Collection_Types.md#arrays) 两小节,以反映这个新的变化。 此外,还解释了如何给 Strings, Arrays 和 Dictionaries 进行赋值和拷贝。
* [数组类型速记语法](../02_language_guide/04_Collection_Types.md#array-type-shorthand-syntax) 从 `SomeType []` 更新为 ` [SomeType]`
* 新增 [字典类型的速记语法](../02_language_guide/04_Collection_Types.md#dictionary-type-shorthand-syntax) 章节,现在书写格式为: ` [KeyType: ValueType]`
* 新增 [字典键类型的哈希值](../02_language_guide/04_Collection_Types.md#hash-values-for-set-types) 章节。
* [闭包表达式](../02_language_guide/07_Closures.md#closure-expressions) 示例中使用新的全局函数 `sorted` 取代原先的全局函数 `sort` 去展示如何返回一个全新的数组。
* 更新 [结构体逐一成员构造器](../02_language_guide/14_Initialization.md#memberwise-initializers-for-structure-types) 章节,即使结构体的成员 `没有默认值`,逐一成员构造器也可以自动获得。
* [半开区间运算符](../02_language_guide/02_Basic_Operators.md#half-open-range-operator) 中`..` 更新为 `..<`
* 新增 [泛型拓展](../02_language_guide/22_Generics.md#extending-a-generic-type) 的示例。
Language Reference:
- Attributes: [WhoJave](https://github.com/WhoJave)
- Statements: [chenmingjia](https://github.com/chenmingjia)
- Declarations: [chenmingjia](https://github.com/chenmingjia)
- Expressions: [chenmingjia](https://github.com/chenmingjia)
- Types: [lettleprince](https://github.com/lettleprince)
- Generic Parameters and Arguments: [chenmingjia](https://github.com/chenmingjia)
感谢阅读!

View File

@ -1,49 +1,48 @@
# Summary
* 欢迎使用 Swift
* [关于 Swift](01_welcome_to_swift/01_about_swift.md)
* [版本兼容性](01_welcome_to_swift/02_version_compatibility.md)
* [Swift 初见](01_welcome_to_swift/03_a_swift_tour.md)
* [Swift 版本历史记录](04_revision_history/04_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 教程
* [基础部分](02_language_guide/01_The_Basics.md)
* [基本运算符](02_language_guide/02_Basic_Operators.md)
* [字符串和字符](02_language_guide/03_Strings_and_Characters.md)
* [集合类型](02_language_guide/04_Collection_Types.md)
* [控制流](02_language_guide/05_Control_Flow.md)
* [函数](02_language_guide/06_Functions.md)
* [闭包](02_language_guide/07_Closures.md)
* [枚举](02_language_guide/08_Enumerations.md)
* [类和结构体](02_language_guide/09_Structures_And_Classes.md)
* [属性](02_language_guide/10_Properties.md)
* [方法](02_language_guide/11_Methods.md)
* [下标](02_language_guide/12_Subscripts.md)
* [继承](02_language_guide/13_Inheritance.md)
* [构造过程](02_language_guide/14_Initialization.md)
* [析构过程](02_language_guide/15_Deinitialization.md)
* [可选链](02_language_guide/16_Optional_Chaining.md)
* [错误处理](02_language_guide/17_Error_Handling.md)
* [并发](02_language_guide/28_Concurrency.md)
* [类型转换](02_language_guide/18_Type_Casting.md)
* [嵌套类型](02_language_guide/19_Nested_Types.md)
* [扩展](02_language_guide/20_Extensions.md)
* [协议](02_language_guide/21_Protocols.md)
* [](02_language_guide/22_Generics.md)
* [不透明类型](02_language_guide/23_Opaque_Types.md)
* [自动引用计数](02_language_guide/24_Automatic_Reference_Counting.md)
* [内存安全](02_language_guide/25_Memory_Safety.md)
* [访问控制](02_language_guide/26_Access_Control.md)
* [高级运算符](02_language_guide/27_Advanced_Operators.md)
* [基础部分](chapter2/01_The_Basics.md)
* [基本运算符](chapter2/02_Basic_Operators.md)
* [字符串和字符](chapter2/03_Strings_and_Characters.md)
* [集合类型](chapter2/04_Collection_Types.md)
* [控制流](chapter2/05_Control_Flow.md)
* [函数](chapter2/06_Functions.md)
* [闭包](chapter2/07_Closures.md)
* [枚举](chapter2/08_Enumerations.md)
* [类和结构体](chapter2/09_Structures_And_Classes.md)
* [属性](chapter2/10_Properties.md)
* [方法](chapter2/11_Methods.md)
* [下标](chapter2/12_Subscripts.md)
* [继承](chapter2/13_Inheritance.md)
* [构造过程](chapter2/14_Initialization.md)
* [析构过程](chapter2/15_Deinitialization.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/27_Opaque_Types.md)
* [自动引用计数](chapter2/23_Automatic_Reference_Counting.md)
* [内存安全](chapter2/24_Memory_Safety.md)
* [访问控制](chapter2/25_Access_Control.md)
* [高级运算符](chapter2/26_Advanced_Operators.md)
* 语言参考
* [关于语言参考](03_language_reference/01_About_the_Language_Reference.md)
* [词法结构](03_language_reference/02_Lexical_Structure.md)
* [类型](03_language_reference/03_Types.md)
* [表达式](03_language_reference/04_Expressions.md)
* [语句](03_language_reference/05_Statements.md)
* [声明](03_language_reference/06_Declarations.md)
* [特性](03_language_reference/07_Attributes.md)
* [模式](03_language_reference/08_Patterns.md)
* [泛型参数](03_language_reference/09_Generic_Parameters_and_Arguments.md)
* [语法总结](03_language_reference/10_Summary_of_the_Grammar.md)
* [关于语言参考](chapter3/01_About_the_Language_Reference.md)
* [词法结构](chapter3/02_Lexical_Structure.md)
* [类型](chapter3/03_Types.md)
* [表达式](chapter3/04_Expressions.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)
* 翻译贡献者
* [翻译贡献者](contributors.md)

View File

@ -2,7 +2,7 @@
Swift 是一种非常好的编写软件的方式,无论是手机,台式机,服务器,还是其他运行代码的设备。它是一种安全,快速和互动的编程语言,将现代编程语言的精华和苹果工程师文化的智慧,以及来自开源社区的多样化贡献结合了起来。编译器对性能进行了优化,编程语言对开发进行了优化,两者互不干扰,鱼与熊掌兼得。
Swift 对于初学者来说也很友好。它是一门满足工业标准的编程语言,但又有着脚本语言般的表达力和可玩性。它支持代码预览playgrounds这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。
Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的系统编程语言。它支持代码预览playgrounds这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。
Swift 通过采用现代编程模式来避免大量常见编程错误:

View File

@ -0,0 +1,11 @@
# 版本兼容性
本书描述的是在 Xcode 11 中的默认 Swift 版本 Swift 5.1。你可以使用 Xcode11 来构建 Swift 5.1、Swift 4.2 或 Swift 4 写的项目。
当您使用 Xcode 11 构建 Swift 4 和 Swift 4.2 代码时,除了下面的功能仅支持 Swift 5.1,其他大多数功能都依然可用。
* 返回值是不透明类型的函数依赖 Swift 5.1 运行时。
* **try?** 表达式不会为已返回可选类型的代码引入额外的可选类型层级。
* 大数字的整型字面量初始化代码的类型将会被正确推导,例如 **UInt64(0xffff_ffff_ffff_ffff)** 将会被推导为整型类型而非溢出。
用 Swift 5.1 写的项目可以依赖用 Swift 4.2 或 Swift 4 写的项目反之亦然。这意味着如果你将一个大的项目分解成多个框架framework你可以每次一个框架地迁移 Swift 4 代码到 Swift 5.1。

View File

@ -10,9 +10,15 @@ print("Hello, world!")
这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解到。
> 注意
>
> 最好的体验是把这一章作为 Playground 文件在 Xcode 中打开。 Playgrounds 允许你可以编辑代码并立刻看到输出结果。
>
> [Download Playground](https://docs.swift.org/swift-book/GuidedTour/GuidedTour.playground.zip)
## 简单值 {#simple-values}
使用 `let` 来声明常量,使用 `var` 来声明变量。一个常量的值,在编译的时候,并不需要有明确的值,但是你只能为它赋值一次。这说明你可以用一个常量来命名一个值,一次赋值就可在多个地方使用。
使用 `let` 来声明常量,使用 `var` 来声明变量。一个常量的值,在编译的时候,并不需要有明确的值,但是你只能为它赋值一次。这说明你可以用一个常量来命名一个值,一次赋值就可在多个地方使用。
```swift
var myVariable = 42
@ -37,7 +43,7 @@ let explicitDouble: Double = 70
值永远不会被隐式转换为其他类型。如果你需要把一个值转换成其他类型,请显式转换。
```swift
let label = "The width is "
let label = "The width is"
let width = 94
let widthLabel = label + String(width)
```
@ -59,7 +65,7 @@ let fruitSummary = "I have \(apples + oranges) pieces of fruit."
>
> 使用 `\()` 来把一个浮点计算转换成字符串,并加上某人的名字,和他打个招呼。
使用三个双引号(`"""`)来包含多行字符串内容。每行行首的缩进会被去除,只要和结尾引号的缩进相匹配。举个例子:
使用三个双引号(`"""`)来包含多行字符串内容,字符串中的内容(包括引号、空格、换行符等)都会保留下来。举个例子:
```swift
let quotation = """
@ -81,21 +87,14 @@ var occupations = [
occupations["Jayne"] = "Public Relations"
```
数组在添加元素时会自动变大。
```swift
shoppingList.append("blue paint")
print(shoppingList)
```
使用初始化语法来创建一个空数组或者空字典。
```swift
let emptyArray: [String] = []
let emptyDictionary: [String: Float] = [:]
let emptyArray = [String]()
let emptyDictionary = [String: Float]()
```
如果类型信息可以被推断出来,你可以用 `[]``[:]` 来创建空数组和空字典——比如,在给变量赋新值或者给函数传参数的时候。
如果类型信息可以被推断出来,你可以用 `[]``[:]` 来创建空数组和空字典——就像你声明变量或者给函数传参数的时候一样
```swift
shoppingList = []
@ -139,7 +138,6 @@ if let name = optionalName {
> 把 `optionalName` 改成 `nil`greeting 会是什么?添加一个 `else` 语句,当 `optionalName``nil` 时给 greeting 赋一个不同的值。
如果变量的可选值是 `nil`,条件会判断为 `false`,大括号中的代码会被跳过。如果不是 `nil`,会将值解包并赋给 `let` 后面的常量,这样代码块中就可以使用这个值了。
另一种处理可选值的方法是通过使用 `??` 操作符来提供一个默认值。如果可选值缺失的话,可以使用默认值来代替。
```swift
@ -148,14 +146,6 @@ let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickName ?? fullName)"
```
你还可以使用较短的代码解包一个值,并且对该被包装值使用相同的名称。
```swift
if let nickname {
print("Hey, \(nickName)")
}
```
`switch` 支持任意类型的数据以及各种比较操作——不仅仅是整数以及测试相等。
```swift
@ -189,7 +179,7 @@ let interestingNumbers = [
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (_, numbers) in interestingNumbers {
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
@ -197,12 +187,11 @@ for (_, numbers) in interestingNumbers {
}
}
print(largest)
// 输出 "25"
```
> 练习
>
> 将 _ 替换成变量名,以确定哪种类型的值是最大的
> 添加另一个变量来记录最大数字的种类kind同时仍然记录这个最大数字的值
使用 `while` 来重复运行一段代码直到条件改变。循环条件也可以在结尾,保证能至少循环一次。
@ -245,7 +234,7 @@ greet(person:"Bob", day: "Tuesday")
> 练习
>
> 删除 `day` 参数,在这个欢迎语中添加一个参数来表示今天的特价菜
> 删除 `day` 参数,添加一个参数来表示今天吃了什么午饭
默认情况下,函数使用它们的参数名称作为它们参数的标签,在参数名称前可以自定义参数标签,或者使用 `_` 表示不使用参数标签。
@ -325,7 +314,7 @@ var numbers = [20, 19, 7, 12]
hasAnyMatches(list: numbers, condition: lessThanTen)
```
函数实际上是一种特殊的闭包:它是一段能之后被调取的代码。闭包中的代码能访问闭包作用域中的变量和函数,即使闭包是在一个不同的作用域被执行的——你已经在嵌套函数的例子中看过了。你可以使用 `{}` 来创建一个匿名闭包。使用 `in` 将参数和返回值类型的声明与闭包函数体进行分离。
函数实际上是一种特殊的闭包:它是一段能之后被调取的代码。闭包中的代码能访问闭包作用域中的变量和函数,即使闭包是在一个不同的作用域被执行的 - 你已经在嵌套函数的例子中看过了。你可以使用 `{}` 来创建一个匿名闭包。使用 `in` 将参数和返回值类型的声明与闭包函数体进行分离。
```swift
numbers.map({
@ -346,7 +335,7 @@ let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)
```
你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。当一个闭包是传给函数的唯一参数,你可以完全忽略括号。
你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。当一个闭包是传给函数的唯一参数,你可以完全忽略括号。
```swift
let sortedNumbers = numbers.sorted { $0 > $1 }
@ -430,7 +419,7 @@ test.simpleDescription()
>
> 创建 `NamedShape` 的另一个子类 `Circle`,构造器接收两个参数,一个是半径一个是名称,在子类 `Circle` 中实现 `area()``simpleDescription()` 方法。
除了简单的存储属性,还有使用 getter 和 setter 的计算属性
除了储存简单的属性之外,属性可以有 getter 和 setter 。
```swift
class EquilateralTriangle: NamedShape {
@ -461,7 +450,7 @@ triangle.perimeter = 9.9
print(triangle.sideLength)
```
`perimeter` 的 setter 中,新值的名字是 `newValue`。你可以在 `set` 之后的圆括号中显式设置一个名字。
`perimeter` 的 setter 中,新值的名字是 `newValue`。你可以在 `set` 之后显式设置一个名字。
注意 `EquilateralTriangle` 类的构造器执行了三步:
@ -495,7 +484,7 @@ triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)
```
处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加 `?`。如果 `?` 之前的值是 `nil``?` 后面的东西都会被忽略,并且整个表达式返回 `nil`。否则,可选值会被解包,之后的所有代码都会按照解包后的值运行。在这两种情况下,整个表达式的值也是一个可选值。
处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加 `?`。如果 `?` 之前的值是 `nil``?` 后面的东西都会被忽略,并且整个表达式返回 `nil`。否则,`?` 之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值。
```swift
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
@ -536,7 +525,7 @@ let aceRawValue = ace.rawValue
默认情况下Swift 按照从 0 开始每次加 1 的方式为原始值进行赋值,不过你可以通过显式赋值进行改变。在上面的例子中,`Ace` 被显式赋值为 1并且剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用 `rawValue` 属性来访问一个枚举成员的原始值。
使用 `init?(rawValue:)` 初始化构造器来从原始值创建一个枚举实例。如果存在与原始值相应的枚举成员就返回该枚举成员,否则就返回 `nil`
使用 `init?(rawValue:)` 初始化构造器来创建一个带有原始值的枚举成员。如果存在与原始值相应的枚举成员就返回该枚举成员,否则就返回 `nil`
```swift
if let convertedRank = Rank(rawValue: 3) {
@ -544,7 +533,7 @@ if let convertedRank = Rank(rawValue: 3) {
}
```
枚举值是实际值,并不是原始值的另一种表达方法。实际上,如果没有比较有意义的原始值,你就不需要提供原始值。
枚举的关联值是实际值,并不是原始值的另一种表达方法。实际上,如果没有比较有意义的原始值,你就不需要提供原始值。
```swift
enum Suit {
@ -570,9 +559,9 @@ let heartsDescription = hearts.simpleDescription()
>
> 给 `Suit` 添加一个 `color()` 方法,对 `spades``clubs` 返回 “black” ,对 `hearts``diamonds` 返回 “red” 。
注意在上面的例子中用了两种方式引用 `hearts` 枚举成员:给 `hearts` 常量赋值时,枚举成员 `Suit.hearts` 需要用全名来引用,因为常量没有显式指定类型。在 `switch` 里,枚举成员使用缩写 `.hearts` 来引用,因为 `self` 的值已经是一个 `suit` 类型。在任何已知变量类型的情况下可以使用缩写。
注意在上面的例子中用了两种方式引用 `hearts` 枚举成员:给 `hearts` 常量赋值时,枚举成员 `Suit.hearts` 需要用全名来引用,因为常量没有显式指定类型。在 `switch` 里,枚举成员使用缩写 `.hearts` 来引用,因为 `self` 的值已经是一个 `suit` 类型,在已知变量类型的情况下可以使用缩写。
如果枚举成员的实例有原始值,那么这些值是在声明的时候就已经决定了,这意味着不同枚举实例的枚举成员总会有一个相同的原始值。当然我们也可以为枚举成员设定关联值,关联值是在创建实例时决定的。这意味着同一枚举成员不同实例的关联值可以不同。你可以把关联值想象成枚举成员实例的存储属性。例如,考虑从服务器获取日出和日落的时间的情况。服务器会返回正常结果或者错误信息。
如果枚举成员的实例有原始值,那么这些值是在声明的时候就已经决定了,这意味着不同枚举实例的枚举成员总会有一个相同的原始值。当然我们也可以为枚举成员设定关联值,关联值是在创建实例时决定的。这意味着不同的枚举成员的关联值可以不同。你可以把关联值想象成枚举成员的寄存属性。例如,考虑从服务器获取日出和日落的时间的情况。服务器会返回正常结果或者错误信息。
```swift
enum ServerResponse {
@ -595,7 +584,7 @@ case let .failure(message):
>
> 给 `ServerResponse` 和 switch 添加第三种情况。
注意 `ServerResponse` 的值在`switch`分支匹配时,日升和日落时间是如何从该值中提取出来的。
注意日升和日落时间是如何从 `ServerResponse` 中提取到并`switch` `case` 相匹配的。
使用 `struct` 来创建一个结构体。结构体和类有很多相同的地方,包括方法和构造器。它们之间最大的一个区别就是结构体是传值,类是传引用。
@ -613,53 +602,7 @@ let threeOfSpadesDescription = threeOfSpades.simpleDescription()
> 练习
>
> 一个方法,创建一副完整的扑克牌,这些牌是所有 rank 和 suit 的组合
## 并发性 {#concurrency}
使用 `async` 标记异步运行的函数
```swift
func fetchUserID(from server: String) async -> Int {
if server == "primary" {
return 97
}
return 501
}
```
您还可以通过在函数名前添加 `await` 来标记对异步函数的调用
```swift
func fetchUsername(from server: String) async -> String {
let userID = await fetchUserID(from: server)
if userID == 501 {
return "John Appleseed"
}
return "Guest"
}
```
使用 `async let` 来调用异步函数,并让其与其它异步函数并行运行。
使用 `await` 以使用该异步函数返回的值。
```swift
func connectUser(to server: String) async {
async let userID = fetchUserID(from: server)
async let username = fetchUsername(from: server)
let greeting = await "Hello \(username), user ID \(userID)"
print(greeting)
}
```
使用 `Task` 从同步代码中调用异步函数且不等待它们返回结果
```swift
Task {
await connectUser(to: "primary")
}
//Prints "Hello Guest, user ID 97"
```
> `Card` 添加一个方法,创建一副完整的扑克牌并把每张牌的 rank 和 suit 对应起来
## 协议和扩展 {#protocols-and-extensions}
@ -699,7 +642,7 @@ let bDescription = b.simpleDescription
> 练习
>
> `ExampleProtocol` 再增加一个要求。你需要怎么改 `SimpleClass``SimpleStructure` 才能保证它们仍旧遵循这个协议?
> 写一个实现这个协议的枚举。
注意声明 `SimpleStructure` 时候 `mutating` 关键字用来标记一个会修改结构体的方法。`SimpleClass` 的声明不需要标记任何方法,因为类中的方法通常可以修改类属性(类的性质)。
@ -820,7 +763,7 @@ print(fridgeIsOpen)
```swift
func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
var result: [Item] = []
var result = [Item]()
for _ in 0..<numberOfTimes {
result.append(item)
}
@ -845,16 +788,15 @@ possibleInteger = .some(100)
```swift
func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
where T.Element: Equatable, T.Element == U.Element
{
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
}
return false
return false
}
anyCommonElements([1, 2, 3], [3])
```
@ -863,4 +805,4 @@ anyCommonElements([1, 2, 3], [3])
>
> 修改 `anyCommonElements(_:_:)` 函数来创建一个函数,返回一个数组,内容是两个序列的共有元素。
`<T: Equatable>``<T> ... where T: Equatable` 的写法是等价的。
`<T: Equatable>``<T> ... where T: Equatable>` 的写法是等价的。

View File

@ -0,0 +1,267 @@
# Swift 文档修订历史
### 2019-06-03
* 更新至 Swift 5.1。
* 在 [不透明类型](../chapter2/27_Opaque_Types.md) 篇章中新增了有关函数返回值遵循指定协议,而不需要提供指定返回类型的内容。
* 新增 [隐式返回的函数](../chapter2/06_Functions.md#functions-with-an-implicit-return) 和 [简化 Getter 声明](../chapter2/10_Properties.md#shorthand-getter-declaration) 章节,其中包含函数省略 `return` 的内容。
* 在 [类型下标](../chapter2/12_Subscripts.md#type-subscripts) 章节中新增有关在类型中使用下标的内容。
* 更新 [结构体的逐一成员构造器](../chapter2/14_Initialization.md#memberwise-initializers-for-structure-types) 章节,现在逐一成员构造器支持在属性有默认值时省略形参。
* 在 [动态查找成员](../chapter3/07_Attributes.md#dynamicmemberlookup) 章节中新增了有关在运行时用 key path 查找动态成员的内容。
* 更新 [自身类型](../chapter3/03_Types.md#self-type-h) 章节,现在 `Self` 可以指向当前类,结构体或者枚举声明时的类型。
### 2019-03-25
* 更新至 Swift 5。
* 新增 [拓展字符串分隔符](../chapter2/03_Strings_And_Characters.md#extended-string-delimiters) 章节。更新 [字符串字面量](../chapter3/03_Lexical_Structure.md#string-literal) 章节,拓展有关字符串分隔符的内容。
* 新增 [动态调用](../chapter3/07_Attributes.md#dynamiccallable) 章节,其中包含使用 `dynamicCallable` 属性动态调用实例作为函数的内容。
* 新增 [unknown](../chapter3/07_Attributes.md#unknown) 和 [未来枚举匹配](../chapter3/05_Statements.md#future-case2) 章节,其中包含了使用 `unknown` 来处理未来枚举可能发生改变的情形。
* 在 [Key-Path 表达式](../chapter3/04_Expressions.md#key-path-expression) 章节新增了有关标示 key path (\\.self) 的内容。
* 在 [可选编译块](../chapter3/05_Statements.md#Conditional-Compilation-Block) 章节新增了有关小于比较符 `<` 的内容。
### 2018-09-17
* 更新至 Swift 4.2。
* 在 [遍历枚举情形](../chapter2/08_Enumerations.md#iterating-over-enumeration-cases) 章节新增了有关访问所有枚举情形的内容。
* 在 [编译诊断](../chapter3/05_Statements.md#compile-time-diagnostic-statement) 章节新增了有关 `#error``#warning` 的内容。
* 在 [属性声明](../chapter3/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `inlinable``usableFromInline` 属性的内容。
* 在 [属性声明](../chapter3/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `requires_stored_property_inits``warn_unqualified_access` 属性的内容。
* 在 [可选编译块](../chapter3/05_Statements.md#Conditional-Compilation-Block) 章节新增了有关如何根据 Swift 编译器版本对代码进行对应编译处理的内容。
* 在 [字面量语法](../chapter3/04_Expressions.md#literal-expression) 章节新增了有关 `#dsohandle` 的内容。
### 2018-03-29
* 更新至 Swift 4.1。
* 在 [等价运算符](../chapter2/26_Advanced_Operators.md#equivalence-operators) 章节新增了有关等价运算符的合成实现的内容。
* 在 [声明](../chapter3/06_Declarations.md) 篇章中 [申明拓展](../chapter3/06_Declarations.md#extension-declaration) 章节和 [协议](../chapter2/21_Protocols.md) 篇章中 [有条件地遵循协议](../chapter2/21_Protocols.md#Conditionally-Conforming-to-a-Protocol) 章节新增了有关协议有条件遵循的内容。
* 在 [关联类型约束中使用协议](../chapter2/22_Generics.md#using-a-protocol-in-its-associated-types-constraints) 章节中新增了有关递归协议约束的内容。
* 在 [条件编译块](../chapter3/05_Statements.md#Conditional-Compilation-Block) 章节中新增了有关 `canImport()``targetEnvironment()` 平台条件的内容。
### 2017-12-04
* 更新至 Swift 4.0.3。
* 更新 [Key-Path 表达式](../chapter3/04_Expressions.md#key-path-expression) 章节,现在 key path 支持下标子路径。
### 2017-09-19
* 更新至 Swift 4.0。
* 在 [内存安全](../chapter2/24_MemorySafety.md) 章节新增了有关内存互斥访问的内容。
* 新增 [带有泛型 Where 子句联类型](../chapter2/22_Generics.md#associated-types-with-a-generic-where-clause) 章节,现在可以使用泛型 `where` 子句约束关联类型。
* 在 [字符串和字符](../chapter2/03_Strings_And_Characters.md) 篇章中 [字面量](../chapter2/03_Strings_And_Characters.md#string-literals) 章节以及 [词法结构](../chapter3/02_Lexical_Structure.md) 篇章的 [字符串字面量](../chapter3/02_Lexical_Structure.md#string-literal) 章节中新增了有关多行字符串字面量的内容。
* 更新 [声明属性](../chapter3/07_Attributes.md#Ideclaration-attributes) 中 `objc` 属性的讨论,现在该属性会在更少的位置被推断出来。
* 新增 [范型下标](../chapter2/22_Generics.md#generic-subscripts) 章节,现在下标也支持范型特性了。
* 更新 [协议](../chapter2/21_Protocols.md) 篇章中 [协议组合](../chapter2/21_Protocols.md#protocol-composition) 章节和 [类型](../chapter3/03_Types.md) 篇章中 [协议组合类型](../chapter3/03_Types.md#protocol-composition-type-h) 章节的讨论,现在协议组合类型支持进行父类约束了。
* 更新 [拓展声明](../chapter3/06_Declarations.md#extension-declaration) 中有关协议扩展的讨论,现在它们不支持 `final` 特性了。
* 在 [断言和前置条件](../chapter2/01_TheBasics.md#assertions-and-preconditions) 章节中新增了部分前置条件和致命错误的内容。
### 2017-03-27
* 更新至 Swift 3.1。
* 新增 [范型 Where 子句扩展](../chapter2/22_Generics.md#extensions-with-a-generic-where-clause) 章节,包含需要的扩展内容。
* 在 [For-In 循环](../chapter2/05_Control_Flow.md#for-in-loops) 章节中新增了区间迭代的例子。
* 在 [到可失败构造器](http://typora-app/chapter2/14_Initialization.md#failable-initializers) 章节中新增了可失败数值转换的例子。
* 在 [声明特性](../chapter3/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关使用 Swift 语言版本的 `available` 特性的内容 。
* 更新 [函数类型](../chapter3/03_Types.md#function-type-h) 章节中的讨论,注意在写函数类型时不允许使用参数标签。
* 更新 [条件编译块](../chapter3/05_Statements.md#Conditional-Compilation-Block) 章节中的 Swift 语言版本号的讨论,现在可以使用可选的补丁版本号。
* 更新 [函数类型](../chapter3/03_Types.md#function-type-h) 章节的讨论,现在 Swift 区分了采用多参数的函数和采用元组类型的单个参数的函数。
* 在 [表达式](../chapter3/04_Expressions.md) 篇章中删除了动态表达式的章节,现在 `type(of:)` 是 Swift 标准库函数。
### 2016-10-27
* 更新至 Swift 3.0.1。
* 更新 [自动引用计数](../chapter2/23_Automatic_Reference_Counting.md) 章节中有关 weak 和 unowned 引用的讨论。
* 在 [声明标识符](../chapter3/06_Declarations.md#declaration-modifiers) 章节中新增了有关新的标识符 `unowned``unowend(safe)``unowned(unsafe)` 的内容。
* 在 [Any 和 AnyObject 的类型转换](../chapter2/18_Type_Casting.md#type-casting-for-any-and-anyobject) 章节中新增了一处说明,有关使用类型 `Any` 作为可选值。
* 更新 [表达式](../chapter3/04_Expressions.md) 章节,把括号表达式和元组表达式的描述分开。
### 2016-09-13
* 更新至 Swift 3.0。
* 更新 [函数](../chapter2/06_Functions.md) 篇章和 [函数声明](../chapter3/06_Declarations.md#function-declaration) 章节中有关函数的讨论,所有函数参数默认都有函数标签。
* 更新 [高级操作符](../chapter2/26_Advanced_Operators.md) 篇章中有关操作符的讨论,现在你可以作为类型函数来实现,替代之前的全局函数实现方式。
* 在 [访问控制](../chapter2/25_Access_Control.md) 章节中新增有关对新的访问级别描述符 `open``fileprivate` 的内容。
* 更新 [函数声明](../chapter3/06_Declarations.md#function-declaration) 章节中有关 `inout` 的讨论,注意它现在出现在参数类型的前面,而不是在参数名称的前面。
* 更新 [逃逸闭包](../chapter2/07_Closures.md#escaping-closures) 和 [自动闭包](../chapter2/07_Closures.md#autoclosures) 章节还有 [属性](../chapter3/07_Attributes.md) 篇章中有关 `@noescape``@autoclosure` 的讨论,现在他们是类型属性,而不是定义属性。
* 在 [高级操作符](../chapter2/26_Advanced_Operators.md) 篇章中 [自定义中缀操作符的优先级](./chapter2/26_Advanced_Operators.md#precedence-and-associativity-for-custom-infix-operators) 章节和 [定义](../chapter3/06_Declarations.md) 篇章中 [优先级组声明](../chapter3/06_Declarations.md#precedence-group-declaration-modifiers) 章节中新增了有关操作符优先级组的内容。
* 更新一些讨论,使用 macOS 替换掉 OS X Error 替换掉 ErrorProtocol。更新一些协议名称比如使用 ExpressibleByStringLiteral 替换掉 StringLiteralConvertible。
* 更新 [泛型](../chapter2/22_Generics.md) 篇章中 [泛型 Where 语句](../chapter2/22_Generics.md#extensions-with-a-generic-where-clause) 章节和 [泛型形参和实参](../chapter3/09_Generic_Parameters_And_Arguments.md) 篇章的讨论,现在泛型的 where 语句写在一个声明的最后。
* 更新 [逃逸闭包](../chapter2/07_Closures.md#escaping-closures) 章节中的讨论,现在闭包默认为非逃逸的。
* 更新 [基础部分](../chapter2/01_TheBasics.md) 篇章中 [可选绑定](../chapter2/01_TheBasics.md#optional-binding) 章节和 [语句](../chapter3/05_Statements.md) 篇章中 [While 语句](../chapter3/05_Statements.md#while-statement) 章节中的讨论,现在 if`while``guard` 语句使用逗号分隔条件列表,不需要使用 `where` 语句。
* 在 [控制流](../chapter2/05_Control_Flow.md) 篇章中 [Switch](../chapter2/05_Control_Flow.md#switch) 章节和 [语句](../chapter3/05_Statements.md) 篇章中 [Switch 语句](../chapter3/05_Statements.md#switch-statement) 章节中新增了 switch cases 可以使用多模式的内容。
* 更新 [函数类型](../chapter3/03_Types.md#function-type-h) 章节有关现在函数参数标签不包含在函数类型中的讨论。
* 更新 [协议](../chapter2/21_Protocols.md) 篇章中 [协议组合](../chapter2/21_Protocols.md#protocol-composition) 章节和 [类型](../chapter3/03_Types.md) 篇章中 [协议组合类型](../chapter3/03_Types.md#protocol-composition-type-h) 章节中有关使用新的 Protocol1 & Protocol2 语法的内容。
* 更新动态类型表达式章节中使用新的 `type(of:)` 表达式的讨论。
* 更新 [行控制表达式](../chapter3/05_Statements.md#line-control-statement) 章节中使用 `#sourceLocation(file:line:)` 表达式的讨论。
* 更新 [永不返回函数](../chapter3/06_Declarations.md#functions-that-never-return) 章节中使用 新的 `Never` 类型的讨论。
* 在 [字面量表达式](../chapter3/04_Expressions.md#literal-expression) 章节中新增了有关 `playground` 字面量的内容。
* 更新 [In-Out 参数](../chapter3/06_Declarations.md#in-out_parameters) 章节,标明只有非逃逸闭包能捕获 `in-out` 参数。
* 更新 [默认参数值](../chapter2/06_Functions.md#default-parameter-values) 章节,现在默认参数不能在调用时候重新排序。
* 更新 [属性](../chapter3/07_Attributes.md) 篇章中有关属性参数使用分号的说明。
* 在 [重新抛出函数和方法](../chapter3/06_Declarations.md#rethrowing-functions-and-methods) 章节中新增了有关在 catch 代码块中抛出错误的重新抛出函数的内容。
* 在 [Selector 表达式](../chapter3/04_Expressions.md#selector-expression7) 章节中新增了中有关访问 Objective-C 中 Selector 的 getter 和 setter 的内容。
* 在 [类型别名声明](../chapter3/06_Declarations.md#type-alias-declaration) 章节中中新增了有关泛型类型别名和在协议内使用类型别名的内容。
* 更新 [函数类型](../chapter3/03_Types.md#function-type-h) 章节中有关函数类型的讨论,标明函数类型作为参数类型必须使用括号包裹。
* 更新 [属性](../chapter3/07_Attributes.md) 篇章,标明 `@IBAction``@IBOutlet``@NSManaged` 隐式含有 `@objc` 属性。
* 在 [声明属性](../chapter3/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `@GKInspectable` 的内容。
* 更新 [可选协议要求](../chapter2/21_Protocols.md#optional-protocol-requirements) 章节中有关只能在与 `Objective-C` 交互的代码中才能使用可选协议要求的内容。
* 删除 [函数声明](../chapter3/06_Declarations.md#function-declaration) 章节中有关显式使用 `let` 关键字作为函数参数的内容。
* 删除 [语句](../chapter3/05_Statements.md) 章节中有关 `Boolean` 协议的内容, 现在这个协议已经被 Swift 标准库删除。
* 更正 [声明属性](../chapter3/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@NSApplicationMain` 协议的内容。
### 2016-03-21
* 更新至 Swift 2.2。
* 在 [编译配置语句](../chapter3/05_Statements.md#Conditional-Compilation-Block) 章节新增了中有关如何根据 Swift 版本进行条件编译。
* 在 [显示成员表达式](../chapter3/04_Expressions.md#explicit-member-expression) 章节中新增了有关如何区分只有参数名不同的方法和构造器的内容。
* 在 [选择器表达式](../chapter3/04_Expressions.md#selector-expression7) 章节中新增了了针对 Objective-C 选择器的 `#selector` 语法。
* 更新 [关联类型](../chapter2/22_Generics.md#associated-types) 和 [协议关联类型声明](../chapter3/06_Declarations.md#protocol_associated_type_declaration) 章节中有关使用 `associatedtype` 关键词修饰关联类型的讨论。
* 更新 [可失败构造器](../chapter2/14_Initialization.md#failable-initializers) 章节中有关当构造器在实例完全初始化之前返回 `nil` 的相关内容。
* 在 [比较运算符](../chapter2/BasicOperators.md#comparison-operators) 章节中新增了比较元组的内容。
* 在 [关键字和标点符号](../chapter3/02_Lexical_Structure.md#keywords-and-punctuation) 章节中新增了使用关键字作为外部参数名的内容。
* 更新 [声明特性](../chapter3/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@objc` 特性的讨论,并指出枚举和枚举用例。
* 更新 [操作符](../chapter3/02_Lexical_Structure.md#operator) 章节中对于自定义运算符的包含了 `.` 的讨论。
* 在 [重新抛出错误的函数和方法](../chapter3/06_Declarations.md#rethrowing-functions-and-methods) 章节中新增了一处说明,重新抛出错误函数不能直接抛出错误。
* 在 [属性观察器](../chapter2/10_Properties.md#property-observers) 章节中新增了一处说明,当作为 in-out 参数传递属性时,属性观察器的调用行为。
* 在 [Swift 初见](./03_a_swift_tour.md) 篇章中新增了错误处理的章节。
* 更新 [弱引用](../chapter2/23_Automatic_Reference_Counting.md#weak-references) 章节中的图片用以更清楚的展示重新分配过程。
* 删除 C 语言风格的 `for` 循环,`++` 前缀和后缀运算符,以及 `--` 前缀和后缀运算符。
* 删除对变量函数参数和柯里化函数的特殊语法的讨论。
### 2015-10-20
* 更新至 Swift 2.1。
* 更新 [字符串插值](../chapter2/03_Strings_And_Characters.md#string-interpolation) 和 [字符串字面量](../chapter3/02_Lexical_Structure.md#string-literal) 章节,现在字符串插值可包含字符串字面量。
* 在 [逃逸闭包](../chapter2/07_Closures.md#escaping-closures) 章节中新增了有关 `@noescape` 属性的相关内容。
* 更新 [声明特性](../chapter3/07_Attributes.md#Ideclaration-attributes) 和 [编译配置语句](../chapter3/05_Statements.md#Conditional-Compilation-Block) 章节中与 tvOS 相关的内容。
* 在 [In-Out 参数](../chapter3/06_Declarations.md#in-out_parameters) 章节中新增了与 in-out 参数行为相关的内容。
* 在 [捕获列表](../chapter3/04_Expressions.md#capture-lists) 章节新增了有关指定闭包捕获列表被捕获时捕获值的相关内容。
* 更新 [可选链式调用访问属性](../chapter2/16_Optional_Chaining.md#accessing-properties-through-optional-chaining) 章节,阐明了如何通过可选链式调用进行赋值。
* 改进 [自动闭包](../chapter2/07_Closures.md#autoclosures) 章节中对自闭包的讨论。
* 在 [Swift 初见](./03_a_swift_tour.md) 篇章中新增了一个使用 `??` 操作符的例子。
### 2015-09-16
* 更新至 Swift 2.0。
* 在 [错误处理](../chapter2/17_Error_Handling.md) 篇章中新增了有关错误处理的相关内容,包括 [Do 语句](../chapter3/05_Statements.md#do-statement)、 [Throw 语句](../chapter3/05_Statements.md#throw-statement)、 [Defer 语句](../chapter3/05_Statements.md##defer-statements) 以及 [try 运算符](../chapter3/04_Expressions.md#try-operator)。
* 更新 [错误表示和抛出](../chapter2/17_Error_Handling.md#representing-and-throwing-errors) 章节,现在所有类型都可以遵循 `ErrorType` 协议了。
* 在 [将错误装换成可选值](../chapter2/17_Error_Handling.md#converting_errors_to_optional_values) 篇章增加了 `try?` 关键字相关内容。
* 在 [枚举](../chapter2/08_Enumerations.md) 篇章的 [递归枚举](../chapter2/08_Enumerations.md#recursive-enumerations) 章节以及以及 [声明](../chapter3/06_Declarations.md) 篇章的 [任意类型用例的枚举](../chapter3/06_Declarations.md#enumerations-with-cases-of-any-type) 章节中新增了递归枚举相关内容。
* 在 [控制流](../chapter2/05_Control_Flow.md) 篇章的 [API 可用性检查](../chapter2/05_Control_Flow.md#checking-api-availability) 章节和 [语句](../chapter3/05_Statements.md) 篇章的 [可用性条件](../chapter3/05_Statements.md#availability-condition) 章节中新增了有关 API 可用性检查相关的内容。
* 在 [控制流](../chapter2/05_Control_Flow.md) 篇章的 [尽早退出](../chapter2/05_Control_Flow.md#early-exit) 章节和 [语句](../chapter3/05_Statements.md) 篇章的 [Guard 语句](../chapter3/05_Statements.md#guard-statement) 章节新增了与 `guard` 语句相关的内容。
* 在 [协议](../chapter2/21_Protocols.md) 篇章中 [协议扩展](../chapter2/21_Protocols.md#protocol-extensions) 章节中新增了有关协议扩展的内容。
* 在 [访问控制](../chapter2/25_Access_Control.md) 篇章的 [单元测试 target 的访问级别](../chapter2/25_Access_Control.md#access-levels-for-unit-test-targets) 章节中新增了有关单元测试访问控制相关的内容。
* 在 [模式](../chapter3/08_Patterns.md) 篇章的 [可选模式](../chapter3/08_Patterns.md#optional-pattern) 章节中新增了可选模式相关内容。
* 更新 [Repeat-While](../chapter2/05_Control_Flow.md#repeat-while) 章节中有关 `repeat-while` 循环相关的内容。
* 更新 [字符串和字符](../chapter2/03_Strings_And_Characters.md) 章节,现在 `String` 类型在 Swift 标准库中不再遵循 `CollectionType` 协议。
* 在 [常量与变量打印](../chapter2/01_TheBasics.md#printing) 章节中新增了新 Swift 标准库中有关 `print(_:separator:terminator) ` 相关内容。
* 在 [枚举](../chapter2/08_Enumerations.md) 篇章的 [原始值的隐式赋值](../chapter2/08_Enumerations.md#implicitly-assigned-raw-values) 章节和 [声明](../chapter3/06_Declarations.md) 篇章的 [包含原始值类型的枚举](../chapter3/06_Declarations.md#enumerations-with-cases-of-a-raw-value-type) 章节中新增了有关包含 `String` 原始值的枚举用例的行为相关内容。
* 在 [自动闭包](../chapter2/07_Closures.md#autoclosures) 章节中新增了有关 `@autoclosure` 特性的相关内容,包括它的 `@autoclosure(escaping)` 形式。
* 更新 [声明特性](../chapter3/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@avaliable``warn_unused_result` 特性的相关内容。
* 更新 [类型特性](../chapter3/07_Attributes.md#type-attributes) 章节中有关 `@convention` 特性的相关内容。
* 在 [可选绑定](../chapter2/01_TheBasics.md#optional-binding) 章节中新增了有关使用 `where` 子句进行多可选绑定的相关内容。
* 在 [字符串字面量](../chapter3/02_Lexical_Structure.md#string-literal) 章节中新增了有关在编译时使用 `+` 运算符拼接字符串字面量的相关内容。
* 在 [元类型](../chapter3/03_Types.md#metatype-type-h) 章节中新增了有关元类型值的比较和使用它们通过构造器表达式构造实例相关内容。
* 在 [断言调试](../chapter2/01_TheBasics.md#debugging-with-assertions) 章节中新增了一处说明,有关用户定义断言何时会失效。
* 更新 [声明特性](../chapter3/07_Attributes.md#Ideclaration-attributes) 章节中对 `@NSManaged` 特性的讨论,现在这个特性可以被应用到一个确定实例方法。
* 更新 [可变参数](../chapter2/06_Functions.md#variadic-parameters) 章节,现在可变参数可以声明在函数参数列表的任意位置中。
* 在 [重写可失败构造器](../chapter2/14_Initialization.md#overriding-a-failable-initializer) 章节中新增了有关非可失败构造器相当于一个可失败构造器通过父类构造器的结果进行强制拆包的相关内容。
* 在 [任意类型用例的枚举](../chapter3/06_Declarations.md#enumerations-with-cases-of-any-type) 章节中新增了有关枚举用例作为函数的内容。
* 在 [构造器表达式](../chapter3/04_Expressions.md#initializer-expression) 章节中新增了有关显式引用一个构造器相关内容。
* 在 [编译控制语句](../chapter3/05_Statements.md#compiler-control-statements) 章节中新增了有关编译内容以及行控制语句相关内容。
* 在 [元类型](../chapter3/03_Types.md#metatype-type-h) 章节新增了一处说明,有关如何从元类型值中构造类实例相关内容。
* 在 [弱引用](../chapter2/23_Automatic_Reference_Counting.md#weak-references) 章节新增了一处说明,有关弱引用作为缓存所存在的不足。
* 更新 [类型特性](../chapter2/10_Properties.md#type-properties) 章节,提到了存储型特性其实是懒加载。
* 更新 [捕获类型](../chapter2/07_Closures.md#capturing_values) 章节,阐明了变量和常量在闭包中如何被捕获。
* 更新 [声明特性](../chapter3/07_Attributes.md#Ideclaration-attributes) 章节,用以描述何时在类中使用 `@objc` 关键字。
* 在 [错误处理](../chapter2/17_Error_Handling.md#handling-errors) 章节中新增了一处说明,有关执行 `throw` 语句的性能。在 [Do 语句](../chapter3/05_Statements.md#do-statement) 章节的 do 语句部分也新增了类似内容。
* 更新 [类型特性](../chapter2/10_Properties.md#type-properties) 章节中有关类、结构体和枚举的存储型和计算型特性相关的内容。
* 更新 [Break 语句](../chapter3/05_Statements.md#break_statement) 章节中有关带标签的 break 语句相关内容。
* 在 [属性观察器](../chapter2/10_Properties.md#property-observers) 章节更新了一处说明,用来明确 `willSet``didSet` 观察器的行为。
* 在 [访问级别](../chapter2/25_Access_Control.md#access-levels) 章节新增了有关 `private` 作用域的相关内容说明。
* 在 [弱引用](../chapter2/23_Automatic_Reference_Counting.md#weak-references) 章节新增了有关弱应用在垃圾回收系统和 ARC 之间的区别的说明。
* 更新 [字符串字面量中特殊字符](../chapter2/03_Strings_And_Characters.md#special-characters-in-string-literals) 章节,对 Unicode 标量更精确定义。
### 2015-04-08
* 更新至 Swift 1.2。
* Swift 现在自身提供了一个 `Set` 集合类型,更多内容,请看 [Sets](../chapter2/CollectionTypes.md#sets) 。
* `@autoclosure` 现在是一个参数声明的属性,而不是参数类型的属性。这里还有一个新的参数声明属性 `@noescape`。更多内容,请看 [属性声明](../chapter3/07_Attributes.md#Ideclaration-attributes) 。
* 对于类型属性和方法现在可以使用 `static` 关键字作为声明描述符,更多内容,请看 [类型变量属性](../chapter3/06_Declarations.md#type-variable-properties)。
* Swift 现在包含一个 `as?``as!` 的向下可失败类型转换运算符。更多内容,请看 [协议遵循性检查](../chapter2/21_Protocols.md#checking-for-protocol-conformance)。
* 新增 [字符串索引](../chapter2/03_Strings_And_Characters.md#string-indices) 的新指导章节。
* 在 [溢出运算符](../chapter2/26_Advanced_Operators.md#overflow-operators) 一节中删除了溢出除运算符(`&/`)和求余溢出运算符(`&%`)。
* 更新常量和常量属性在声明和构造时的规则,更多内容,请看 [常量声明](../chapter3/06_Declarations.md#constant-declaration) 。
* 更新字符串字面量中 Unicode 标量集的定义,请看 [字符串字面量中的特殊字符](../chapter2/03_Strings_And_Characters.md#special-characters-in-string-literals) 。
* 更新 [区间运算符](../chapter2/BasicOperators.md#range-operators) 章节,注意当半开区间运算符含有相同的起止索引时,其区间为空。
* 更新 [闭包引用类型](../chapter2/07_Closures.md#closures-are-reference-types) 章节,对于变量的捕获规则进行了阐明。
* 更新 [值溢出](../chapter2/26_Advanced_Operators.md#value-overflow) 章节,对有符号整数和无符号整数的溢出行为进行了阐明。
* 更新 [协议声明](../chapter3/06_Declarations.md#protocol-declaration) 章节,对协议声明时的作用域和成员等内容进行了阐明。
* 更新 [捕获列表](../chapter2/23_Automatic_Reference_Counting.md#defining-a-capture-list) 章节,对于闭包捕获列表中的弱引用和无主引用的使用语法进行了阐明。
* 更新 [运算符](../chapter3/02_Lexical_Structure.md#operator) 章节明确指明一些例子来说明自定义运算符所支持的特性如数学运算符各种符号Unicode 符号块等。
* 在函数作用域中的常量声明时可以不被初始化,它必须在第一次使用前被赋值。更多的内容,请看 [常量声明](../chapter3/06_Declarations.md#constant-declaration)。
* 在构造器中,常量属性有且仅能被赋值一次。更多内容,请看 [在构造过程中给常量属性赋值](../chapter2/14_Initialization.md{#assigning-constant-properties-during-initialization)。
* 多个可选绑定现在可以在`if`语句后面以逗号分隔的赋值列表的方式出现,更多内容,请看 [可选绑定](../chapter2/01_TheBasics.md#optional-binding)。
* 一个 [可选链表达式](../chapter3/04_Expressions.md#optional-chaining-expression) 必须出现在后缀表达式中。
* 协议类型转换不再局限于 `@obj` 修饰的协议了。
* 在运行时可能会失败的类型转换可以使用 `as?``as!` 运算符,而确保不会失败的类型转换现在使用 `as` 运算符。更多内容,请看 [类型转换运算符](../chapter3/04_Expressions.md#type-casting-operator)。
### 2014-10-16
* 更新至 Swift 1.1。
* 新增 [失败构造器](../chapter2/14_Initialization.md#failable-initializers) 的完整指引。
* 在协议中新增了 [失败构造器要求](../chapter2/21_Protocols.md#failable-initializer-requirements) 的描述。
* 常量和变量的 `Any` 类型现可以包含函数实例。更新了有关 `Any` 相关的示例来展示如何在 `switch` 语句中如何检查并转换到一个函数类型。
* 带有原始值的枚举类型增加了一个 `rawValue` 属性替代 `toRaw()` 方法,同时使用了一个以 `rawValue` 为参数的失败构造器来替代 `fromRaw()` 方法。更多的内容,请看 [原始值](../chapter2/08_Enumerations.md#raw-values) 和 [带原始值的枚举类型](../chapter3/06_Declarations.md#enumerations-with-cases-of-a-raw-value-type)。
* 新增 [Failable Initializer](../chapter3/06_Declarations.md#failable-initializers) 的参考章节,它可以触发初始化失败。
* 自定义运算符现在可以包含 `?` 字符,更新了 [运算符](../chapter3/02_Lexical_Structure.md#operator) 涉及改进后的规则的部分,并且在 [自定义运算符](../chapter2/26_Advanced_Operators.md#custom-operators) 章节中删除了重复的运算符有效字符集合。
### 2014-08-18
* 描述 Swift 1.0 的新文档。Swift 是苹果公司发布的全新编程语言,用于 iOS 和 OS X 应用开发。
* 在协议中新增了 [对构造器的规定](../chapter2/21_Protocols.md#initializer-requirements) 章节。
* 新增 [类专属协议](../chapter2/21_Protocols.md#class-only-protocol) 章节。
* [断言](../chapter2/01_TheBasics.md#assertions-and-preconditions) 现在可以使用字符串内插语法,并删除了文档中有冲突的注释。
* 更新 [连接字符串和字符](../chapter2/03_Strings_And_Characters.md#concatenating-strings-and-characters) 章节来说明字符串和字符不能再用 `+` 号运算符或者复合加法运算符 `+=` 相互连接,这两种运算符现在只能用于字符串之间相连。请使用 `String` 类型的 `append` 方法在一个字符串的尾部增加单个字符。
* 在 [属性申明](../chapter3/07_Attributes.md#Ideclaration-attributes) 章节增加了有关 `availability` 特性的一些内容。
* [可选类型](../chapter2/01_TheBasics.md#optionals) 若有值时,不再隐式的转换为 `true`,同样,若无值时,也不再隐式的转换为 `false`,这是为了避免在判别 optional `Bool` 的值时产生困惑。 替代的方案是,用`==``!=` 运算符显式地去判断 Optinal 是否是 `nil`,以确认其是否包含值。
* Swift 新增了一个 [Nil 合并运算符](../chapter2/BasicOperators.md#nil-coalescing-operator) (`a ?? b`) , 该表达式中,如果 Optional `a` 的值存在,则取得它并返回,若 Optional `a``nil`,则返回默认值 `b`
* 更新和扩展 [字符串的比较](../chapter2/03_Strings_And_Characters.md#comparing-strings) ,用以反映和展示'字符串和字符的比较',以及'前缀prefix/后缀postfix比较'都开始基于扩展字符集extended grapheme clusters规范的等价比较。
* 现在,你可以通过下标赋值或者 [可选调用链](../chapter2/16_Optional_Chaining.md) 中的可变方法和操作符来给属性设值。相应地更新了有关 [通过可选链接访问属性](../chapter2/16_Optional_Chaining.md#accessing-properties-through-optional-chaining) 的内容,并扩展了 [通过可选链接调用方法](../chapter2/16_Optional_Chaining.md#calling-methods-through-optional-chaining) 时检查方法调用成功的示例,以显示如何检查属性设置是否成功。
* 在可选链中新增了 [访问可选类型的下标脚注](../chapter2/16_Optional_Chaining.md#accessing-subscripts-through-optional-chaining) 章节。
* 更新 [访问和修改数组](../chapter2/CollectionTypes.md#accessing-and-modifying-a-dictionary) 章节以标示,从该版本起,不能再通过 `+=` 运算符给一个数组新增一个新的项。对应的替代方案是,使 `append` 方法,或者通过 `+=` 运算符来新增一个只有一个项的数组。
* 新增一处说明,在 [范围运算符](../chapter2/BasicOperators.md#range-operators) 中,比如, `a..b``a..<b` ,起始值 `a` 不能大于结束值 `b`
* 重写 [继承](../chapter2/13_Inheritance.md) 篇章删除了本章中有关构造器重写的介绍性报道转而将更多的注意力放到新增的部分——子类的新功能以及如何通过重写overrides修改已有的功能。另外 [重写属性的 Getters 和 Setters](../chapter2/13_Inheritance.md#overriding-property-etters-and-setters) 中的例子已经被替换为展示如何重写一个 `description` 属性。 (而有关如何在子类的构造器中修改继承属性的默认值的例子,已经被移到 [构造过程](../chapter2/14_Initialization.md) 篇章。)
* 更新 [构造器的继承与重写](../chapter2/14_Initialization.md#initializer-inheritance-and-overriding) 章节以标示: 重写一个特定的构造器必须使用 `override` 修饰符。
* 更新 [Required 构造器](../chapter2/14_Initialization.md#required-initializers) 章节以标示:`required` 修饰符现在需要出现在所有子类的 required 构造器的声明中,而 required 构造器的实现,现在可以仅从父类自动继承。
* 中置Infix的 [运算符函数](../chapter2/26_Advanced_Operators.md#operator-functions) 不再需要 `@infix` 属性。
* [前置和后置运算符](../chapter2/26_Advanced_Operators.md#prefix-and-postfix-operators) 的 `@prefix``@postfix` 属性,已变更为 `prefix``postfix` 声明修饰符。
* 新增一处说明,在 Prefix 和 postfix 运算符被作用于同一个操作数时 [前置和后置运算符](../chapter2/26_Advanced_Operators.md#prefix-and-postfix-operators) 的执行顺序。
* [组合赋值运算符](../chapter2/26_Advanced_Operators.md#compound-assignment-operators) 的运算符函数不再使用 `@assignment` 属性来定义函数。
* 在定义 [自定义操作符](../chapter2/26_Advanced_Operators.md#custom-operators) 时,`修饰符Modifiers的出现顺序发生变化`。比如现在,你该编写 `prefix operator` 而不是 `operator prefix`
* 在 [声明修饰符](../chapter3/06_Declarations.md#declaration-modifiers) 章节新增了有关 `dynamic` 声明修饰符的内容。
* 新增有关 [字面量](../chapter3/02_Lexical_Structure.md#literal) 类型推导内容的内容。
* 新增更多有关柯里化函数的内容。
* 新增 [权限控制](../chapter2/25_Access_Control.md) 篇章。
* 更新 [字符串和字符](../chapter2/03_Strings_And_Characters.md) 章节,在 Swift 中现在 `Character` 类型代表的是扩展字符集extended grapheme cluster中的一个 Unicode为此新增了 [Extended Grapheme Clusters](../chapter2/03_Strings_And_Characters.md#extended-grapheme-clusters) 章节。同时,[Unicode 标量](../chapter2/03_Strings_And_Characters.md#unicode-scalars-representation) 和 [字符串比较](../chapter2/03_Strings_And_Characters.md#comparing-strings) 章节新增了更多内容。
* 更新 [字符串字面量](../chapter2/03_Strings_And_Characters.md#string-literals) 章节在一个字符串中Unicode 标量Unicode scalars`\u{n}`的形式来表示,`n` 是一个最大可以有8位的16进制数。
* `NSString` `length` 属性已被映射到 Swift 的内建 `String`类型。(注意,这两属性的类型是`utf16Count`,而非 `utf16count`)。
* Swift 的内建 `String` 类型不再拥有 `uppercaseString``lowercaseString` 属性。在 [字符串和字符](../chapter2/03_Strings_And_Characters.md) 章节中删除了对应部分,并更新了各种对应的代码用例。
* 新增 [没有外部名的构造器参数](../chapter2/14_Initialization.md#initializer-parameters-without-external-names) 章节。
* 新增 [Required 构造器](../chapter2/14_Initialization.md#required-initializers) 章节。
* 新增 [可选元组返回类型](../chapter2/06_Functions.md#optional-tuple-return-types) 章节。
* 更新 [类型注解](../chapter2/01_TheBasics.md#type-annotations) 章节,多个相关变量可以用"类型注解”在同一行中声明为同一类型。
* `@optional`, `@lazy`, `@final`, `@required` 等关键字被更新为 `optional`, `lazy`, `final`, `required` 参见 [声明修饰符](../chapter3/06_Declarations.md#declaration-modifiers)。
* 更新了整本书中有关 `..<` 的引用,从半闭区间改为了 [半开区间](../chapter2/BasicOperators.md#half-open-range-operator)。
* 更新 [读取和修改字典](../chapter2/CollectionTypes.md#accessing-and-modifying-a-dictionary) 章节, `Dictionary` 现在增加了一个 Boolean 型的属性:`isEmpty`
* 解释了哪些字符(集)可被用来定义 [自定义操作符](../chapter2/26_Advanced_Operators.md#custom-operators)。
* `nil` 和布尔运算中的 `true``false` 现在被定义为 [字面量](../chapter3/02_Lexical_Structure.md#literal)。
* Swift 中的数组 `Array` 类型从现在起具备了完整的值语义。具体内容被更新到 [集合的可变性](../chapter2/CollectionTypes.md#mutability-of-collections) 和 [数组](../chapter2/CollectionTypes.md#arrays) 两小节,以反映这个新的变化。 此外,还解释了如何给 Strings, Arrays 和 Dictionaries 进行赋值和拷贝。
* [数组类型速记语法](../chapter2/CollectionTypes.md#array-type-shorthand-syntax) 从 `SomeType []` 更新为 ` [SomeType]`
* 新增 [字典类型的速记语法](../chapter2/CollectionTypes.md#dictionary-type-shorthand-syntax) 章节,现在书写格式为: ` [KeyType: ValueType]`
* 新增 [字典键类型的哈希值](../chapter2/CollectionTypes.md#hash-values-for-set-types) 章节。
* [闭包表达式](../chapter2/07_Closures.md#closure-expressions) 示例中使用新的全局函数 `sorted` 取代原先的全局函数 `sort` 去展示如何返回一个全新的数组。
* 更新 [结构体逐一成员构造器](../chapter2/14_Initialization.md#memberwise-initializers-for-structure-types) 章节,即使结构体的成员 `没有默认值`,逐一成员构造器也可以自动获得。
* [半开区间运算符](../chapter2/BasicOperators.md#half-open-range-operator) 中`..` 更新为 `..<`
* 新增 [泛型拓展](../chapter2/22_Generics.md#extending-a-generic-type) 的示例。

View File

@ -10,11 +10,11 @@ Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int` 表示整
Swift 还增加了可选Optional类型用于处理值缺失的情况。可选表示 “那儿有一个值,并且它等于 *x* ” 或者 “那儿没有值” 。可选有点像在 Objective-C 中使用 `nil` ,但是它可以用在任何类型上,不仅仅是类。可选类型比 Objective-C 中的 `nil` 指针更加安全也更具表现力,它是 Swift 许多强大特性的重要组成部分。
Swift 是一门 *类型安全* 的语言,这意味着 Swift 可以让你清楚地知道值的类型。如果你的代码需要一个 `String` ,类型安全会阻止你不小心传入一个 `Int` 。同样的,如果你的代码需要一个 `String`,类型安全会阻止你意外传入一个可选的 `String` 。类型安全可以帮助你在开发阶段尽早发现并修正错误。
Swift 是一门*类型安全*的语言,这意味着 Swift 可以让你清楚地知道值的类型。如果你的代码需要一个 `String` ,类型安全会阻止你不小心传入一个 `Int` 。同样的,如果你的代码需要一个 `String`,类型安全会阻止你意外传入一个可选的 `String` 。类型安全可以帮助你在开发阶段尽早发现并修正错误。
## 常量和变量 {#constants-and-variables}
常量和变量把一个名字(比如 `maximumNumberOfLoginAttempts` 或者 `welcomeMessage` )和一个指定类型的值(比如数字 `10` 或者字符串 `"Hello"` )关联起来。 *常量* 的值一旦设定就不能改变,而 *变量* 的值可以随意更改。
常量和变量把一个名字(比如 `maximumNumberOfLoginAttempts` 或者 `welcomeMessage` )和一个指定类型的值(比如数字 `10` 或者字符串 `"Hello"` )关联起来。*常量*的值一旦设定就不能改变,而*变量*的值可以随意更改。
### 声明常量和变量 {#declaring}
@ -43,7 +43,7 @@ var x = 0.0, y = 0.0, z = 0.0
### 类型注解 {#type-annotations}
当你声明常量或者变量的时候可以加上 *类型注解type annotation* ,说明常量或者变量中要存储的值的类型。如果要添加类型注解,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。
当你声明常量或者变量的时候可以加上*类型注解type annotation*,说明常量或者变量中要存储的值的类型。如果要添加类型注解,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。
这个例子给 `welcomeMessage` 变量添加了类型注解,表示这个变量可以存储 `String` 类型的值:
@ -51,7 +51,7 @@ var x = 0.0, y = 0.0, z = 0.0
var welcomeMessage: String
```
声明中的冒号代表着 *“是...类型”* ,所以这行代码可以被理解为:
声明中的冒号代表着*“是...类型”*,所以这行代码可以被理解为:
“声明一个类型为 `String` ,名字为 `welcomeMessage` 的变量。”
@ -75,7 +75,7 @@ var red, green, blue: Double
### 常量和变量的命名 {#naming}
常量和变量名可以包含几乎所有的字符,包括 Unicode 字符:
常量和变量名可以包含任何字符,包括 Unicode 字符:
```swift
let π = 3.14159
@ -116,9 +116,9 @@ print(friendlyWelcome)
// 输出“Bonjour!”
```
`print(_:separator:terminator:)` 是一个用来输出一个或多个值到适当输出区的全局函数。如果你用 Xcode`print(_:separator:terminator:)` 将会输出内容到“console”面板上。`separator``terminator` 参数具有默认值,因此你调用这个函数的时候可以忽略它们。默认情况下,该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给 `terminator` 参数--例如,`print(someValue, terminator:"")` 。关于参数默认值的更多信息,请参考 [默认参数值](./06_Functions.md#default-parameter-values)。
`print(_:separator:terminator:)` 是一个用来输出一个或多个值到适当输出区的全局函数。如果你用 Xcode`print(_:separator:terminator:)` 将会输出内容到“console”面板上。`separator``terminator` 参数具有默认值,因此你调用这个函数的时候可以忽略它们。默认情况下,该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给 `terminator` 参数--例如,`print(someValue, terminator:"")` 。关于参数默认值的更多信息,请参考 [默认参数值](./06_Functions.md#default_parameter_values)。
Swift 用 *字符串插值string interpolation* 的方式把常量名或者变量名当做占位符加入到长字符串中Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:
Swift 用*字符串插值string interpolation*的方式把常量名或者变量名当做占位符加入到长字符串中Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:
```swift
print("The current value of friendlyWelcome is \(friendlyWelcome)")
@ -127,7 +127,7 @@ print("The current value of friendlyWelcome is \(friendlyWelcome)")
> 注意
>
> 字符串插值所有可用的选项,请参考 [字符串插值](./03_Strings_and_Characters.md#string-interpolation)。
> 字符串插值所有可用的选项,请参考 [字符串插值](./03_Strings_and_Characters.md#string_interpolation)。
## 注释 {#comments}
@ -200,7 +200,7 @@ Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台
> 注意
>
> 尽量不要使用 `UInt`,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用 `Int`,即使你要存储的值已知是非负的。统一使用 `Int` 可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,请参考 [类型安全和类型推断](#type-safety-and-type-inference)。
> 尽量不要使用 `UInt`,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用 `Int`,即使你要存储的值已知是非负的。统一使用 `Int` 可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,请参考 [类型安全和类型推断](#type_safety_and_type_inference)。
## 浮点数 {#floating-point-numbers}
@ -213,15 +213,15 @@ Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台
> 注意
>
> `Double` 精确度很高,至少有 15 位小数,而 `Float` 只有 6 位小数。选择哪个类型取决于你的代码需要处理的值的范围,在两种类型都匹配的情况下,将优先选择 `Double`
> `Double` 精确度很高至少有15位数字,而 `Float` 只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围,在两种类型都匹配的情况下,将优先选择 `Double`
## 类型安全和类型推断 {#type-safety-and-type-inference}
Swift 是一个 *类型安全type safe* 的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。如果你的代码需要一个 `String`,你绝对不可能不小心传进去一个 `Int`
Swift 是一个*类型安全type safe*的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。如果你的代码需要一个 `String`,你绝对不可能不小心传进去一个 `Int`
由于 Swift 是类型安全的,所以它会在编译你的代码时进行 *类型检查type checks*,并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误。
由于 Swift 是类型安全的,所以它会在编译你的代码时进行*类型检查type checks*,并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误。
当你要处理不同类型的值时类型检查可以帮你避免错误。然而这并不是说你每次声明常量和变量的时候都需要显式指定类型。如果你没有显式指定类型Swift 会使用 *类型推断type inference* 来选择合适的类型。有了类型推断,编译器可以在编译代码的时候自动推断出表达式的类型。原理很简单,只要检查你赋的值即可。
当你要处理不同类型的值时类型检查可以帮你避免错误。然而这并不是说你每次声明常量和变量的时候都需要显式指定类型。如果你没有显式指定类型Swift 会使用*类型推断type inference*来选择合适的类型。有了类型推断,编译器可以在编译代码的时候自动推断出表达式的类型。原理很简单,只要检查你赋的值即可。
因为有类型推断,和 C 或者 Objective-C 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。
@ -256,10 +256,10 @@ let anotherPi = 3 + 0.14159
整数字面量可以被写作:
* 一个 *十进制* 数,没有前缀
* 一个 *二进制* 数,前缀是 `0b`
* 一个 *八进制* 数,前缀是 `0o`
* 一个 *十六进制* 数,前缀是 `0x`
* 一个*十进制*数,没有前缀
* 一个*二进制*数,前缀是 `0b`
* 一个*八进制*数,前缀是 `0o`
* 一个*十六进制*数,前缀是 `0x`
下面的所有整数字面量的十进制值都是 `17`:
@ -357,7 +357,7 @@ let integerPi = Int(pi)
## 类型别名 {#type-aliases}
*类型别名type aliases* 就是给现有类型定义另一个名字。你可以使用 `typealias` 关键字来定义类型别名。
*类型别名type aliases*就是给现有类型定义另一个名字。你可以使用 `typealias` 关键字来定义类型别名。
当你想要给现有类型起一个更有意义的名字时,类型别名非常有用。假设你正在处理特定长度的外部资源的数据:
@ -376,7 +376,7 @@ var maxAmplitudeFound = AudioSample.min
## 布尔值 {#booleans}
Swift 有一个基本的 *布尔Boolean类型*,叫做 `Bool`。布尔值指 *逻辑* 上的值因为它们只能是真或者假。Swift 有两个布尔常量,`true``false`
Swift 有一个基本的*布尔Boolean类型*,叫做 `Bool`。布尔值指*逻辑*上的值因为它们只能是真或者假。Swift 有两个布尔常量,`true``false`
```swift
let orangesAreOrange = true
@ -422,9 +422,9 @@ if i == 1 {
## 元组 {#tuples}
*元组tuples* 把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型。
*元组tuples*把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型。
下面这个例子中,`(404, "Not Found")` 是一个描述 *HTTP 状态码HTTP status code* 的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个 `404 Not Found` 状态码。
下面这个例子中,`(404, "Not Found")` 是一个描述 *HTTP 状态码HTTP status code*的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个 `404 Not Found` 状态码。
```swift
let http404Error = (404, "Not Found")
@ -477,15 +477,15 @@ print("The status message is \(http200Status.description)")
// 输出“The status message is OK”
```
作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个 `(Int, String)` 元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考 [函数参数与返回值](./06_Functions.md#Function-Parameters-and-Return-Values)。
作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个 `(Int, String)` 元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考 [函数参数与返回值](./06_Functions.md#Function_Parameters_and_Return_Values)。
> 注意
>
> 当遇到一些相关值的简单分组时,元组是很有用的。元组不适合用来创建复杂的数据结构。如果你的数据结构比较复杂,不要使用元组,用类或结构体去建模。欲获得更多信息,请参考 [结构体和类](./09_Structures_And_Classes.md)。
> 当遇到一些相关值的简单分组时,元组是很有用的。元组不适合用来创建复杂的数据结构。如果你的数据结构比较复杂,不要使用元组,用类或结构体去建模。欲获得更多信息,请参考 [结构体和类](./09_Classes_and_Structures.md)。
## 可选类型 {#optionals}
使用 *可选类型optionals* 来处理值可能缺失的情况。可选类型表示两种可能:
使用*可选类型optionals*来处理值可能缺失的情况。可选类型表示两种可能:
或者有值, 你可以解析可选类型访问这个值, 或者根本没有值。
> 注意
@ -502,7 +502,7 @@ let convertedNumber = Int(possibleNumber)
// convertedNumber 被推测为类型 "Int?" 或者类型 "optional Int"
```
因为该构造器可能会失败,所以它返回一个 *可选类型* optional`Int`,而不是一个 `Int`。一个可选的 `Int` 被写作 `Int?` 而不是 `Int`。问号暗示包含的值是可选类型,也就是说可能包含 `Int` 值也可能 *不包含值* 。(不能包含其他任何值比如 `Bool` 值或者 `String` 值。只能是 `Int` 或者什么都没有。)
因为该构造器可能会失败,所以它返回一个*可选类型*optional`Int`,而不是一个 `Int`。一个可选的 `Int` 被写作 `Int?` 而不是 `Int`。问号暗示包含的值是可选类型,也就是说可能包含 `Int` 值也可能*不包含值*。(不能包含其他任何值比如 `Bool` 值或者 `String` 值。只能是 `Int` 或者什么都没有。)
### nil {#nil}
@ -519,7 +519,7 @@ serverResponseCode = nil
>
> `nil` 不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
如果你声明一个可选变量但是没有赋值,它们会自动被设置为 `nil`
如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 `nil`
```swift
var surveyAnswer: String?
@ -543,7 +543,7 @@ if convertedNumber != nil {
// 输出“convertedNumber contains some integer value.”
```
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(`!`)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的 *强制解析forced unwrapping*
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(`!`)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的*强制解析forced unwrapping*
```swift
if convertedNumber != nil {
@ -560,7 +560,7 @@ if convertedNumber != nil {
### 可选绑定 {#optional-binding}
使用 *可选绑定optional binding* 来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在 `if``while` 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。`if``while` 语句,请参考 [控制流](./05_Control_Flow.md)。
使用*可选绑定optional binding*来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在 `if``while` 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。`if``while` 语句,请参考 [控制流](./05_Control_Flow.md)。
像下面这样在 `if` 语句中写一个可选绑定:
@ -587,38 +587,16 @@ if let actualNumber = Int(possibleNumber) {
如果转换成功,`actualNumber` 常量可以在 `if` 语句的第一个分支中使用。它已经被可选类型 *包含的* 值初始化过,所以不需要再使用 `!` 后缀来获取它的值。在这个例子中,`actualNumber` 只被用来输出转换结果。
如果你在访问它包含的值后不需要引用原来的可选常量或是可选变量,你可以对新的常量或是新的变量使用相同的名称:
你可以在可选绑定中使用常量和变量。如果你想在 `if` 语句的第一个分支中操作 `actualNumber` 的值,你可以改成 `if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。
```swift
let myNumber = Int(possibleNumber)
// 此处 myNumber 为一可选整型
if let myNumber = myNumber {
// 此处 myNumber 为一不可选整型
print("My number is \(myNumber)")
}
// 输出 "My number is 123"
```
正如前一个例子中的代码一样,本例代码首先检查 `myNumber` 是否包含任何值。若 `myNumber` 包含有任何值,则该值将成为新常量 `myNumber` 的值。在 `if` 语句的主体中,写入的 `myNumber` 指向这一个新的非可选常量。在 `if` 语句开始前和语句结束后,写入的 `myNumber` 指向可选的整数常量。
由于这种代码非常常见,你可以通过一个更简短的写法来解包一个可选值:只写你要展开的常量或变量的名称。新的常量/变量将使用相同的名称作为其隐式解包可选值。
``` swift
if let myNumber{
print("My number is \(myNumber)")
}
// 输出 "My number is 123"
```
你可以在可选绑定中使用常量和变量。如果你想在 `if` 语句的第一个分支中操作 `actualNumber` 的值,你可以改成 `if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。你在 `if` 语句中对 `myNumber` 所做的更改将仅作用于该局部变量而非你解包的原始可选常量/变量。
你可以包含多个可选绑定或多个布尔条件在一个 `if` 语句中,只要使用逗号分开就行。只要有任意一个可选绑定的值为 `nil`,或者任意一个布尔条件为 `false`,则整个 `if` 条件判断为 `false`。下面的两个 `if` 语句是等效的:
你可以包含多个可选绑定或多个布尔条件在一个 `if` 语句中,只要使用逗号分开就行。只要有任意一个可选绑定的值为 `nil`,或者任意一个布尔条件为 `false`,则整个 `if` 条件判断为 `false`,这时你就需要使用嵌套 `if` 条件语句来处理,如下所示:
```swift
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// 输出“4 < 42 < 100
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
@ -631,7 +609,7 @@ if let firstNumber = Int("4") {
> 注意
>
> 在 `if` 条件语句中使用常量和变量来创建一个可选绑定,仅在 `if` 语句的句中(`body`)中才能获取到值。相反,在 `guard` 语句中使用常量和变量来创建一个可选绑定,仅在 `guard` 语句外且在语句后才能获取到值,请参考 [提前退出](./05_Control_Flow.md#early-exit)。
> 在 `if` 条件语句中使用常量和变量来创建一个可选绑定,仅在 `if` 语句的句中(`body`)中才能获取到值。相反,在 `guard` 语句中使用常量和变量来创建一个可选绑定,仅在 `guard` 语句外且在语句后才能获取到值,请参考 [提前退出](./05_Control_Flow.md#early_exit)。
### 隐式解析可选类型 {#implicityly-unwrapped-optionals}
@ -639,9 +617,9 @@ if let firstNumber = Int("4") {
有时候在程序架构中第一次被赋值之后可以确定一个可选类型_总会_有值。在这种情况下每次都要判断和解析可选值是非常低效的因为可以确定它总会有值。
这种类型的可选状态被定义为隐式解析可选类型implicitly unwrapped optionals。把想要用作可选的类型的后面的问号`String?`)改成感叹号(`String!`)来声明一个隐式解析可选类型。与其在使用时把感叹号放在可选类型的名称的后面,你可以在定义它时,直接把感叹号放在可选类型的后面。
这种类型的可选状态被定义为隐式解析可选类型implicitly unwrapped optionals。把想要用作可选的类型的后面的问号`String?`)改成感叹号(`String!`)来声明一个隐式解析可选类型。
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考 [无主引用以及隐式解析可选属性](./24_Automatic_Reference_Counting.md#unowned-references-and-implicitly-unwrapped-optional-properties)。
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考 [无主引用以及隐式解析可选属性](./23_Automatic_Reference_Counting.md#unowned_references_and_implicitly_unwrapped_optional_properties)。
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型 `String` 和隐式解析可选类型 `String` 之间的区别:
@ -653,15 +631,13 @@ let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感叹号
```
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。当你使用一个隐式解析可选值时Swift 首先会把它当作普通的可选值如果它不能被当成可选类型使用Swift 会强制解析可选值。在以上的代码中,可选值 `assumedString` 在把自己的值赋给 `implicitString` 之前会被强制解析,原因是 `implicitString` 本身的类型是非可选类型的 `String`。在下面的代码中,`optionalString` 并没有显式的数据类型。那么根据类型推断,它就是一个普通的可选类型
```swift
let optionalString = assumedString
// optionalString 的类型是 "String?"assumedString 也没有被强制解析。
```
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾
如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个感叹号一样。
> 注意
>
> 如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。
你可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:
仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:
```swift
if assumedString != nil {

View File

@ -2,11 +2,11 @@
*运算符*是检查、改变、合并值的特殊符号或短语。例如,加号(`+`)将两个数相加(如 `let i = 1 + 2`)。更复杂的运算例子包括逻辑与运算符 `&&`(如 `if enteredDoorCode && passedRetinaScan`)。
Swift 支持运算符你可能在别的语言比如 C 语言里已经认识了,同时为了减少常见编码错误对它们做了部分改进。如:赋值符(`=`)不再有返回值,这样就消除了手误将判等运算符(`==`)写成赋值符导致代码错误的缺陷。算术运算符(`+``-``*``/``%` 等)的结果会被检测并禁止值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见 [溢出运算符](./27_Advanced_Operators.md#overflow-operators)。
Swift 支持大部分标准 C 语言的运算符,且为了减少常见编码错误做了部分改进。如:赋值符(`=`)不再有返回值,这样就消除了手误将判等运算符(`==`)写成赋值符导致代码错误的缺陷。算术运算符(`+``-``*``/``%` 等)的结果会被检测并禁止值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见 [溢出运算符](./26_Advanced_Operators.md#overflow_operators)。
Swift 还提供了 C 语言没有的区间运算符,例如 `a..<b``a...b`,这方便我们表达一个区间内的数值。
本章节只描述了 Swift 中的基本运算符,[高级运算符](./27_Advanced_Operators.md) 这章会包含 Swift 中的高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
本章节只描述了 Swift 中的基本运算符,[高级运算符](./26_Advanced_Operators.md) 这章会包含 Swift 中的高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
## 术语 {#terminology}
@ -16,7 +16,7 @@ Swift 还提供了 C 语言没有的区间运算符,例如 `a..<b` 或 `a...b`
- *二元*运算符操作两个操作对象(如 `2 + 3`),是*中置*的,因为它们出现在两个操作对象之间。
- *三元*运算符操作三个操作对象,和 C 语言一样Swift 只有一个三元运算符,就是三目运算符(`a ? b : c`)。
受运算符影响的值叫*操作数*,在表达式 `1 + 2` 中,加号 `+`中置运算符,它的两个操作数是值 `1``2`
受运算符影响的值叫*操作数*,在表达式 `1 + 2` 中,加号 `+`二元运算符,它的两个操作数是值 `1``2`
## 赋值运算符 {#assignment-operator}
@ -62,7 +62,7 @@ Swift 中所有数值类型都支持了基本的四则*算术运算符*
10.0 / 2.5 // 等于 4.0
```
与 C 语言和 Objective-C 不同的是Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如 `a &+ b`)。详情参见 [溢出运算符](./27_Advanced_Operators.md#overflow-operators)。
与 C 语言和 Objective-C 不同的是Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如 `a &+ b`)。详情参见 [溢出运算符](./26_Advanced_Operators.md#overflow_operators)。
加法运算符也可用于 `String` 的拼接:
@ -106,7 +106,7 @@ Swift 中所有数值类型都支持了基本的四则*算术运算符*
-9 % 4 // 等于 -1
```
`-9``4` 代入等式,`-2`使 `余数``-9` 同符号时能取到的最大整数:
`-9``4` 代入等式,`-2` 是取到的最大整数:
-9 = (4 × -2) + -1
@ -157,7 +157,7 @@ a += 2
## 比较运算符Comparison Operators {#comparison-operators}
Swift 支持以下的比较运算符
所有标准 C 语言中的*比较运算符*都可以在 Swift 中使用
- 等于(`a == b`
- 不等于(`a != b`
@ -168,7 +168,7 @@ Swift 支持以下的比较运算符:
> 注意
>
> Swift 也提供恒等(`===`)和不恒等(`!==`)这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在 [类与结构](./09_Structures_And_Classes.md) 章节的 **Identity Operators** 部分。
> Swift 也提供恒等(`===`)和不恒等(`!==`)这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在 [类与结构](./09_Classes_and_Structures.md) 章节的 **Identity Operators** 部分。
每个比较运算都返回了一个标识表达式是否成立的布尔值:
@ -267,7 +267,7 @@ if hasHeader {
a != nil ? a! : b
```
上述代码使用了三元运算符。当可选类型 `a` 的值不为空时,进行强制解`a!`),访问 `a` 中的值;反之返回默认值 `b`。无疑空合运算符(`??`)提供了一种更为优雅的方式去封装条件判断和解两种行为,显得简洁以及更具可读性。
上述代码使用了三元运算符。当可选类型 `a` 的值不为空时,进行强制解`a!`),访问 `a` 中的值;反之返回默认值 `b`。无疑空合运算符(`??`)提供了一种更为优雅的方式去封装条件判断和解两种行为,显得简洁以及更具可读性。
> 注意
>
@ -286,7 +286,7 @@ var colorNameToUse = userDefinedColorName ?? defaultColorName
`userDefinedColorName` 变量被定义为一个可选的 `String` 类型,默认值为 `nil`。由于 `userDefinedColorName` 是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为 `colorNameToUse` 的变量赋予一个字符串类型初始值。
由于 `userDefinedColorName` 值为空,因此表达式 `userDefinedColorName ?? defaultColorName` 返回 `defaultColorName` 的值,即 `red`
如果你分配一个非空值(`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在 `userDefinedColorName` 中的值,而非默认值。
如果你分配一个非空值(`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在 `userDefaultColorName` 中的值,而非默认值。
```swift
userDefinedColorName = "green"
@ -402,7 +402,7 @@ if !allowedEntry {
在示例代码中,小心地选择布尔常量或变量有助于代码的可读性,并且避免使用双重逻辑非运算,或混乱的逻辑语句。
### 逻辑与运算符 {#logical-and-operator}
### 逻辑与运算符 #{logical_and_operator}
*逻辑与运算符*`a && b`)表达了只有 `a``b` 的值都为 `true` 时,整个表达式的值才会是 `true`
@ -421,7 +421,7 @@ if enteredDoorCode && passedRetinaScan {
// 输出“ACCESS DENIED”
```
### 逻辑或运算符 {#logical-or-operator}
### 逻辑或运算符 #{logical_or_operator}
逻辑或运算符(`a || b`)是一个由两个连续的 `|` 组成的中置运算符。它表示了两个逻辑表达式的其中一个为 `true`,整个表达式就为 `true`

View File

@ -1,8 +1,8 @@
# 字符串和字符
*字符串*是一系列字符的集合,例如 `"hello, world"``"albatross"`。Swift 的字符串通过 `String` 类型来表示。而 `String` 内容的访问方式有多种,例如以 `Character` 值的集合。
*字符串*是一系列字符的集合,例如 `"hello, world"``"albatross"`。Swift 的字符串通过 `String` 类型来表示。而 `String` 内容的访问方式有多种,例如以 `Character` 值的集合。
Swift 的 `String``Character` 类型提供了一种快速且兼容 Unicode 的方式来处理代码中的文本内容。创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。通过 `+` 符号就可以非常简单的实现两个字符串的拼接操作。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你可以在已有字符串中插入常量、变量、字面量和表达式从而形成更长的字符串,这一过程也被为字符串插值。尤其是在为显示、存储和打印创建自定义字符串值时,字符串插值操作尤其有用。
Swift 的 `String``Character` 类型提供了一种快速且兼容 Unicode 的方式来处理代码中的文本内容。创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。通过 `+` 符号就可以非常简单的实现两个字符串的拼接操作。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你可以在已有字符串中插入常量、变量、字面量和表达式从而形成更长的字符串,这一过程也被为字符串插值。尤其是在为显示、存储和打印创建自定义字符串值时,字符串插值操作尤其有用。
尽管语法简易,但 Swift 中的 `String` 类型的实现却很快速和现代化。每一个字符串都是由编码无关的 Unicode 字符组成,并支持访问字符的多种 Unicode 表示形式。
@ -89,7 +89,7 @@ It also ends with a line break.
```swift
let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imagination is more important than knowledge" - Einstein
// "Imageination is more important than knowledge" - Enistein
let dollarSign = "\u{24}" // $Unicode 标量 U+0024
let blackHeart = "\u{2665}" // ♥Unicode 标量 U+2665
let sparklingHeart = "\u{1F496}" // 💖Unicode 标量 U+1F496
@ -106,9 +106,9 @@ Escaping all three quotes \"\"\"
### 扩展字符串分隔符 {#extended-string-delimiters}
您可以将字符串文字放在扩展分隔符中,这样字符串中的特殊字符将会被直接包含而非转义后的效果。将字符串放在引号(`"`)中并用数字符号(`#`)括起来。例如,打印字符串文字 `#"Line 1 \nLine 2"#` 会打印换行符转义序列(`\n`)而不是给文字换行。
您可以将字符串文字放在扩展分隔符中,这样字符串中的特殊字符将会被直接包含而非转义后的效果。将字符串放在引号(`"`)中并用数字符号(``)括起来。例如,打印字符串文字 `"Line 1 \nLine 2"` 会打印换行符转义序列(`\n`)而不是给文字换行。
如果需要字符串文字中字符的特殊效果,请匹配转义字符(`\`)后面添加与起始位置个数相匹配的 `#` 符。 例如,如果您的字符串是 `#"Line 1 \nLine 2"#` 并且您想要换行,则可以使用 `#"Line 1 \#nLine 2"#` 来代替。 同样,`###"Line1 \###nLine2"###` 也可以实现换行效果。
如果需要字符串文字中字符的特殊效果,请匹配转义字符(`\`)后面添加与起始位置个数相匹配的 `#` 符。 例如,如果您的字符串是 `"Line 1 \nLine 2"` 并且您想要换行,则可以使用 `#“Line 1 \#nLine 2”#` 来代替。 同样,`###"Line1 \###nLine2"###` 也可以实现换行效果。
扩展分隔符创建的字符串文字也可以是多行字符串文字。 您可以使用扩展分隔符在多行字符串中包含文本 `"""`,覆盖原有的结束文字的默认行为。例如:
@ -157,7 +157,7 @@ constantString += " and another Highlander"
## 字符串是值类型 {#strings-are-value-types}
在 Swift 中 `String` 类型是*值类型*。如果你创建了一个新的字符串,那么当其进行常量、变量赋值操作,或在函数/方法中传递时,会进行值拷贝。在前述任一情况下,都会对已有字符串值创建新副本,并对该新副本而非原始字符串进行传递或赋值操作。值类型在 [结构体和枚举是值类型](./09_Structures_And_Classes.md#structures-and-enumerations-are-value-types) 中进行了详细描述。
在 Swift 中 `String` 类型是*值类型*。如果你创建了一个新的字符串,那么当其进行常量、变量赋值操作,或在函数/方法中传递时,会进行值拷贝。在前述任一情况下,都会对已有字符串值创建新副本,并对该新副本而非原始字符串进行传递或赋值操作。值类型在 [结构体和枚举是值类型](./09_Classes_and_Structures.md#structures_and_enumerations_are_value_types) 中进行了详细描述。
Swift 默认拷贝字符串的行为保证了在函数/方法向你传递的字符串所属权属于你,无论该值来自于哪里。你可以确信传递的字符串不会被修改,除非你自己去修改它。
@ -178,7 +178,7 @@ for character in "Dog!🐶" {
// 🐶
```
`for-in` 循环在 [For 循环](./05_Control_Flow.md#for-loops) 中进行了详细描述。
`for-in` 循环在 [For 循环](./05_Control_Flow.md#for_loops) 中进行了详细描述。
另外,通过标明一个 `Character` 类型并用字符字面量进行赋值,可以建立一个独立的字符常量或变量:
@ -269,20 +269,6 @@ let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
`multiplier` 的值也作为字符串中后面表达式的一部分。该表达式计算 `Double(multiplier) * 2.5` 的值并将结果(`7.5`)插入到字符串中。在这个例子中,表达式写为 `\(Double(multiplier) * 2.5)` 并包含在字符串字面量中。
你可以使用扩展字符串分隔符创建字符串,来包含不想作为字符串插值处理的字符。例如:
```swift
print(#"Write an interpolated string in Swift using \(multiplier)."#)
// 打印 "Write an interpolated string in Swift using \(multiplier)."
```
如果要在使用扩展字符串分隔符的字符串中使用字符串插值,需要在反斜杠后面添加与开头和结尾数量相同扩展字符串分隔符。例如:
```swift
print(#"6 times 7 is \#(6 * 7)."#)
// 打印 "6 times 7 is 42."
```
> 注意
>
> 插值字符串中写在括号中的表达式不能包含非转义反斜杠(`\`),并且不能包含回车或换行符。不过,插值字符串可以包含其他字面量。
@ -398,7 +384,7 @@ greeting[index]
```swift
greeting[greeting.endIndex] // error
greeting.index(after: greeting.endIndex) // error
greeting.index(after: endIndex) // error
```
使用 `indices` 属性会创建一个包含全部索引的范围(`Range`),用来在一个字符串中访问单个字符。
@ -416,7 +402,7 @@ for index in greeting.indices {
### 插入和删除 {#inserting-and-removing}
调用 `insert(_:at:)` 方法可以在一个字符串的指定索引插入一个字符,调用 `insert(contentsOf:at:)` 方法可以在一个字符串的指定索引插入一段字符串。
调用 `insert(_:at:)` 方法可以在一个字符串的指定索引插入一个字符,调用 `insert(contentsOf:at:)` 方法可以在一个字符串的指定索引插入一段字符串。
```swift
var welcome = "hello"
@ -444,7 +430,7 @@ welcome.removeSubrange(range)
## 子字符串 {#substrings}
当你从字符串中获取一个子字符串 —— 例如,使用下标或者 `prefix(_:)` 之类的方法 —— 就可以得到一个 `Substring` 的实例,而非另外一个 `String`。Swift 里的 `Substring` 绝大部分函数都跟 `String` 一样,意味着你可以使用同样的方式去操作 `Substring``String`。然而,跟 `String` 不同的是,你只有在短时间内需要操作字符串时,才会使用 `Substring`。当你需要长时间保存结果时,就把 `Substring` 转化为 `String` 的实例:
当你从字符串中获取一个子字符串 —— 例如,使用下标或者 `prefix(_:)` 之类的方法 —— 就可以得到一个 `SubString` 的实例,而非另外一个 `String`。Swift 里的 `SubString` 绝大部分函数都跟 `String` 一样,意味着你可以使用同样的方式去操作 `SubString``String`。然而,跟 `String` 不同的是,你只有在短时间内需要操作字符串时,才会使用 `SubString`。当你需要长时间保存结果时,就把 `SubString` 转化为 `String` 的实例:
```swift
let greeting = "Hello, world!"
@ -456,15 +442,15 @@ let beginning = greeting[..<index]
let newString = String(beginning)
```
就像 `String`,每一个 `Substring` 都会在内存里保存字符集。而 `String``Substring` 的区别在于性能优化上,`Substring` 可以重用原 `String` 的内存空间,或者另一个 `Substring` 的内存空间(`String` 也有同样的优化,但如果两个 `String` 共享内存的话,它们就会相等)。这一优化意味着你在修改 `String``Substring` 之前都不需要消耗性能去复制内存。就像前面说的那样,`Substring` 不适合长期存储 —— 因为它重用了原 `String` 的内存空间,原 `String` 的内存空间必须保留直到它的 `Substring` 不再被使用为止。
就像 `String`,每一个 `SubString` 都会在内存里保存字符集。而 `String``SubString` 的区别在于性能优化上,`SubString` 可以重用原 `String` 的内存空间,或者另一个 `SubString` 的内存空间(`String` 也有同样的优化,但如果两个 `String` 共享内存的话,它们就会相等)。这一优化意味着你在修改 `String``SubString` 之前都不需要消耗性能去复制内存。就像前面说的那样,`SubString` 不适合长期存储 —— 因为它重用了原 `String` 的内存空间,原 `String` 的内存空间必须保留直到它的 `SubString` 不再被使用为止。
上面的例子,`greeting` 是一个 `String`,意味着它在内存里有一片空间保存字符集。而由于 `beginning``greeting``Substring`,它重用了 `greeting` 的内存空间。相反,`newString` 是一个 `String` —— 它是使用 `Substring` 创建的,拥有一片自己的内存空间。下面的图展示了他们之间的关系:
上面的例子,`greeting` 是一个 `String`,意味着它在内存里有一片空间保存字符集。而由于 `beginning``greeting``SubString`,它重用了 `greeting` 的内存空间。相反,`newString` 是一个 `String` —— 它是使用 `SubString` 创建的,拥有一片自己的内存空间。下面的图展示了他们之间的关系:
![](https://docs.swift.org/swift-book/images/stringSubstring@2x.png)
![](https://docs.swift.org/swift-book/_images/stringSubstring_2x.png)
> 注意
>
> `String``Substring` 都遵循 [`StringProtocol`](https://developer.apple.com/documentation/swift/stringprotocol) 协议,这意味着操作字符串的函数使用 `StringProtocol` 会更加方便。你可以传入 `String``Substring` 去调用函数。
> `String``SubString` 都遵循 `StringProtocol<//apple_ref/swift/intf/s:s14StringProtocolP>` 协议,这意味着操作字符串的函数使用 `StringProtocol` 会更加方便。你可以传入 `String``SubString` 去调用函数。
## 比较字符串 {#comparing-strings}
@ -472,7 +458,7 @@ Swift 提供了三种方式来比较文本值:字符串字符相等、前缀
### 字符串/字符相等 {#string-and-character-equality}
字符串/字符可以用等于操作符(`==`)和不等于操作符(`!=`),详细描述在 [比较运算符](./02_Basic_Operators.md#comparison-operators)
字符串/字符可以用等于操作符(`==`)和不等于操作符(`!=`),详细描述在 [比较运算符](./02_Basic_Operators.md#comparison_operators)
```swift
let quotation = "We're a lot alike, you and I."
@ -570,13 +556,13 @@ print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
> 注意
>
> `hasPrefix(_:)``hasSuffix(_:)` 方法都是在每个字符串中逐字符比较其可扩展的字符群集是否标准相等,详细描述在 [字符串/字符相等](#string-and-character-equality)。
> `hasPrefix(_:)``hasSuffix(_:)` 方法都是在每个字符串中逐字符比较其可扩展的字符群集是否标准相等,详细描述在 [字符串/字符相等](#string_and_character_equality)。
## 字符串的 Unicode 表示形式 {#unicode-representations-of-strings}
当一个 Unicode 字符串被写进文本文件或者其他储存时,字符串中的 Unicode 标量会用 Unicode 定义的几种 `编码格式`encoding forms编码。每一个字符串中的小块编码都被称 `代码单元`code units。这些包括 UTF-8 编码格式(编码字符串为 8 位的代码单元), UTF-16 编码格式(编码字符串位 16 位的代码单元),以及 UTF-32 编码格式编码字符串32位的代码单元
Swift 提供了几种不同的方式来访问字符串的 Unicode 表示形式。你可以利用 `for-in` 来对字符串进行遍历,从而以 Unicode 可扩展的字符群集的方式访问每一个 `Character` 值。该过程在 [使用字符](#working-with-characters) 中进行了描述。
Swift 提供了几种不同的方式来访问字符串的 Unicode 表示形式。你可以利用 `for-in` 来对字符串进行遍历,从而以 Unicode 可扩展的字符群集的方式访问每一个 `Character` 值。该过程在 [使用字符](#working_with_characters) 中进行了描述。
另外,能够以其他三种 Unicode 兼容的方式访问字符串的值:
@ -631,11 +617,6 @@ let dogString = "Dog‼🐶"
</tr>
</table>
如果表格无法正确显示,请参考下图:
![image](https://user-images.githubusercontent.com/2572987/145422655-288a7d40-75f5-4329-b43f-0d942b9b4623.png)
```swift
for codeUnit in dogString.utf8 {
print("\(codeUnit) ", terminator: "")
@ -679,11 +660,6 @@ print("")
</tr>
</table>
如果表格无法正确显示,请参考下图:
![image](https://user-images.githubusercontent.com/2572987/145422770-cdcb214a-430e-4a6d-b763-36657b3c8d7e.png)
```swift
for codeUnit in dogString.utf16 {
print("\(codeUnit) ", terminator: "")

View File

@ -2,7 +2,7 @@
Swift 语言提供数组Array、集合Set和字典Dictionary三种基本的*集合类型*用来存储集合数据。数组是有序数据的集。集合是无序无重复数据的集。字典是无序的键值对的集。
![](https://docs.swift.org/swift-book/images/CollectionTypes_intro~dark@2x.png)
![](https://docs.swift.org/swift-book/_images/CollectionTypes_intro_2x.png)
Swift 中的数组、集合和字典必须明确其中保存的键和值类型,这样就可以避免插入一个错误数据类型的值。同理,对于获取到的值你也可以放心,其数据类型是确定的。
@ -37,7 +37,7 @@ Swift 中数组的完整写法为 `Array<Element>`,其中 `Element` 是这个
你可以使用构造语法来创建一个由特定数据类型构成的空数组:
```swift
var someInts: [Int] = []
var someInts = [Int]()
print("someInts is of type [Int] with \(someInts.count) items.")
// 打印“someInts is of type [Int] with 0 items.”
```
@ -235,7 +235,7 @@ for (index, value) in shoppingList.enumerated() {
// Item 5: Bananas
```
更多关于 `for-in` 循环的介绍请参见 [For 循环](./05_Control_Flow.md#for-loops)。
更多关于 `for-in` 循环的介绍请参见 [For 循环](05_Control_Flow.html#for_loops)。
## 集合Sets {#sets}
@ -380,7 +380,7 @@ for genre in favoriteGenres {
// Hip hop
```
更多关于 `for-in` 循环的信息,参见 [For 循环](./05_Control_Flow.md#for-loops)。
更多关于 `for-in` 循环的信息,参见 [For 循环](./05_Control_Flow.md#for_loops)。
Swift 的 `Set` 类型没有确定的顺序,为了按照特定顺序来遍历一个集合中的值可以使用 `sorted()` 方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符 `<` 对元素进行比较的结果来确定。
@ -401,7 +401,7 @@ for genre in favoriteGenres.sorted() {
下面的插图描述了两个集合 `a``b`,以及通过阴影部分的区域显示集合各种操作的结果。
![](https://docs.swift.org/swift-book/images/setVennDiagram@2x.png)
![](https://docs.swift.org/swift-book/_images/setVennDiagram_2x.png)
* 使用 `intersection(_:)` 方法根据两个集合的交集创建一个新的集合。
* 使用 `symmetricDifference(_:)` 方法根据两个集合不相交的值创建一个新的集合。
@ -427,7 +427,7 @@ oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
下面的插图描述了三个集合 `a``b``c`,以及通过重叠区域表述集合间共享的元素。集合 `a` 是集合 `b` 的*父集合*,因为 `a` 包含了 `b` 中所有的元素。相反的,集合 `b` 是集合 `a` 的*子集合*,因为属于 `b` 的元素也被 `a` 包含。集合 `b` 和集合 `c` 是*不相交*的,因为它们之间没有共同的元素。
![](https://docs.swift.org/swift-book/images/setEulerDiagram@2x.png)
![](https://docs.swift.org/swift-book/_images/setEulerDiagram_2x.png)
* 使用“是否相等”运算符(`==`)来判断两个集合包含的值是否全部相同。
* 使用 `isSubset(of:)` 方法来判断一个集合中的所有值是否也被包含在另外一个集合中。
@ -473,7 +473,7 @@ Swift 的字典使用 `Dictionary<Key, Value>` 定义,其中 `Key` 是一种
你可以像数组一样使用构造语法创建一个拥有确定类型的空字典:
```swift
var namesOfIntegers: [Int: String] = [:]
var namesOfIntegers = [Int: String]()
// namesOfIntegers 是一个空的 [Int: String] 字典
```
@ -613,7 +613,7 @@ for (airportCode, airportName) in airports {
// LHR: London Heathrow
```
更多关于 `for-in` 循环的信息,参见 [For 循环](./05_Control_Flow.md#for-loops)。
更多关于 `for-in` 循环的信息,参见 [For 循环](./05_Control_Flow.md#for_loops)。
通过访问 `keys` 或者 `values` 属性,你也可以遍历字典的键或者值:

View File

@ -23,7 +23,7 @@ for name in names {
// Hello, Jack!
```
你也可以通过遍历一个字典来访问它的键值对。遍历字典时,字典的每项元素会以 `(key, value)` 元组的形式返回,你可以在 `for-in` 循环中使用显式的常量名称来解读 `(key, value)` 元组。下面的例子中,字典的键声明为 `animalName` 常量,字典的值会声明为 `legCount` 常量:
你也可以通过遍历一个字典来访问它的键值对。遍历字典时,字典的每项元素会以 `(key, value)` 元组的形式返回,你可以在 `for-in` 循环中使用显式的常量名称来解读 `(key, value)` 元组。下面的例子中,字典的键声明`animalName` 常量,字典的值会声明为 `legCount` 常量:
```swift
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
@ -69,7 +69,7 @@ print("\(base) to the power of \(power) is \(answer)")
这个例子计算 base 这个数的 power 次幂(本例中,是 `3``10` 次幂),从 `1``3``0` 次幂)开始做 `3` 的乘法, 进行 `10` 次,使用 `1``10` 的闭区间循环。这个计算并不需要知道每一次循环中计数器具体的值,只需要执行了正确的循环次数即可。下划线符号 `_` (替代循环中的变量)能够忽略当前值,并且不提供循环遍历时对值的访问。
在某些情况下,你可能不想使用包括两个端点的闭区间。想象一下,你在一个手表上绘制分钟的刻度线。总共 `60` 个刻度,从 `0` 分开始。使用半开区间运算符(`..<`)来表示一个左闭右开的区间。有关区间的更多信息,请参阅 [区间运算符](./02_Basic_Operators.md#range-operators)。
在某些情况下,你可能不想使用包括两个端点的闭区间。想象一下,你在一个手表上绘制分钟的刻度线。总共 `60` 个刻度,从 `0` 分开始。使用半开区间运算符(`..<`)来表示一个左闭右开的区间。有关区间的更多信息,请参阅 [区间运算符](./02_Basic_Operators.md#range_operators)。
```swift
let minutes = 60
@ -97,8 +97,6 @@ for tickMark in stride(from: 3, through: hours, by: hourInterval) {
}
```
以上示例使用 `for-in` 循环来遍历范围、数组、字典和字符串。你可以用它来遍历任何的集合,包括实现了 [Sequence](https://developer.apple.com/documentation/swift/sequence) 协议的自定义类或集合类型。
## While 循环 {#while-loops}
`while` 循环会一直运行一段语句直到条件变成 `false`。这类循环适合使用在第一次迭代前迭代次数未知的情况下。Swift 提供两种 `while` 循环形式:
@ -120,7 +118,7 @@ while condition {
下面的例子来玩一个叫做*蛇和梯子*(也叫做*滑道和梯子*)的小游戏:
![image](https://docs.swift.org/swift-book/images/snakesAndLadders@2x.png)
![image](https://docs.swift.org/swift-book/_images/snakesAndLadders_2x.png)
游戏的规则如下:
@ -334,7 +332,7 @@ default:
> 注意
>
> 虽然在 Swift 中 `break` 不是必须的,但你依然可以在 case 分支中的代码执行完毕前使用 `break` 跳出,详情请参见 [Switch 语句中的 break](#break-in-a-switch-statement)。
> 虽然在 Swift 中 `break` 不是必须的,但你依然可以在 case 分支中的代码执行完毕前使用 `break` 跳出,详情请参见 [Switch 语句中的 break](#break_in_a_switch_statement)。
每一个 case 分支都*必须*包含至少一条语句。像下面这样书写代码是无效的,因为第一个 case 分支是空的:
@ -365,7 +363,7 @@ default:
// 输出“The letter A”
```
为了可读性,符合匹配可以写成多行形式,详情请参考 [复合匹配](#compound-cases)
为了可读性,符合匹配可以写成多行形式,详情请参考 [复合匹配](#compound_cases)
> 注意
>
@ -422,11 +420,11 @@ default:
// 输出“(1, 1) is inside the box”
```
![image](https://docs.swift.org/swift-book/images/coordinateGraphSimple@2x.png)
![image](https://docs.swift.org/swift-book/_images/coordinateGraphSimple_2x.png)
在上面的例子中,`switch` 语句会判断某个点是否是原点 (0, 0),是否在红色的 x 轴上,是否在橘黄色的 y 轴上是否在一个以原点为中心的4x4的蓝色矩形里或者在这个矩形外面。
不像 C 语言Swift 允许多个 case 匹配同一个值。实际上,在这个例子中,四个 `case` 都可以匹配点 (0, 0) 。但是,如果存在多个匹配,那么只会执行第一个被匹配到的 case 分支。考虑点 (0, 0)会首先匹配 `case (0, 0)`,因此剩下的能够匹配的分支都会被忽视掉。
不像 C 语言Swift 允许多个 case 匹配同一个值。实际上,在这个例子中,点 (0, 0)可以匹配所有_四个 case_。但是,如果存在多个匹配,那么只会执行第一个被匹配到的 case 分支。考虑点 (0, 0)会首先匹配 `case (0, 0)`,因此剩下的能够匹配的分支都会被忽视掉。
#### 值绑定Value Bindings {#value-bindings}
@ -447,7 +445,7 @@ case let (x, y):
// 输出“on the x-axis with an x value of 2”
```
![image](https://docs.swift.org/swift-book/images/coordinateGraphMedium@2x.png)
![image](https://docs.swift.org/swift-book/_images/coordinateGraphMedium_2x.png)
在上面的例子中,`switch` 语句会判断某个点是否在红色的 x 轴上,是否在橘黄色的 y 轴上,或者不在坐标轴上。
@ -476,7 +474,7 @@ case let (x, y):
// 输出“(1, -1) is on the line x == -y”
```
![image](https://docs.swift.org/swift-book/images/coordinateGraphComplex@2x.png)
![image](https://docs.swift.org/swift-book/_images/coordinateGraphComplex_2x.png)
在上面的例子中,`switch` 语句会判断某个点是否在绿色的对角线 `x == y` 上,是否在紫色的对角线 `x == -y` 上,或者不在对角线上。
@ -529,7 +527,7 @@ default:
- `return`
- `throw`
我们将会在下面讨论 `continue``break``fallthrough` 语句。`return` 语句将会在 [函数](./06_Functions.md) 章节讨论,`throw` 语句会在 [错误抛出](./17_Error_Handling.md#throwing-errors) 章节讨论。
我们将会在下面讨论 `continue``break``fallthrough` 语句。`return` 语句将会在 [函数](./06_Functions.md) 章节讨论,`throw` 语句会在 [错误抛出](./18_Error_Handling.md#throwing_errors) 章节讨论。
### Continue {#continue}
@ -639,7 +637,7 @@ print(description)
为了实现这个目的,你可以使用标签(*statement label*)来标记一个循环体或者条件语句,对于一个条件语句,你可以使用 `break` 加标签的方式,来结束这个被标记的语句。对于一个循环语句,你可以使用 `break` 或者 `continue` 加标签,来结束或者继续这条被标记语句的执行。
声明一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签作为这个语句的前导关键字introducer keyword并且该标签后面跟随一个冒号。下面是一个针对 `while` 循环体的标签语法,同样的规则适用于所有的循环体和条件语句。
声明一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签作为这个语句的前导关键字introducor keyword并且该标签后面跟随一个冒号。下面是一个针对 `while` 循环体的标签语法,同样的规则适用于所有的循环体和条件语句。
```swift
label name: while condition {
@ -655,7 +653,7 @@ print(description)
游戏的棋盘和之前一样:
![image](https://docs.swift.org/swift-book/images/snakesAndLadders@2x.png)
![image](https://docs.swift.org/swift-book/_images/snakesAndLadders_2x.png)
`finalSquare``board``square``diceRoll` 值被和之前一样的方式初始化:
@ -756,44 +754,12 @@ if #available(iOS 10, macOS 10.12, *) {
以上可用性条件指定,`if` 语句的代码块仅仅在 iOS 10 或 macOS 10.12 及更高版本才运行。最后一个参数,`*`是必须的用于指定在所有其它平台中如果版本号高于你的设备指定的最低版本if 语句的代码块将会运行。
在它一般的形式中,可用性条件使用了一个平台名字和版本的列表。平台名字可以是 `iOS``macOS``watchOS``tvOS`——请访问 [声明属性](../03_language_reference/07_Attributes.md) 来获取完整列表。除了指定像 iOS 8 或 macOS 10.10 的大版本号,也可以指定像 iOS 11.2.6 以及 macOS 10.13.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(平台名称 版本号, ..., *) {
APIs 可用,语句将执行
} else {
APIs 不可用,使用先前版本API的语句将执行
APIs 不可用,语句将执行
}
```
当你在 `guard` 语句中使用可用性条件时,它将细化用于该代码块中其余代码的可用性信息。
```swift
@avaliable(macOS 10.12, *)
struct ColorPreference {
var bestColor = "blue"
}
func chooseBestColor() -> String {
guard #avaliable(macOS 10.12, *) else{
return "gray"
}
let colors = ColorPreference()
return colors.bestColor
}
```
在上面的例子中,结构体 `ColorPreference` 需要 macOS 10.12 或更高的版本。函数 `ChooseBestColor()` 先以一个可用性防护开头,若平台版本过低无法运行 `ColorPreference` 时,将执行该低版本平台可用的行为。而在 `guard` 语句后,你将能够使用 macOS 10.12 或更高版本的API。
除了 `#available` 以外, Swift 还支持通过不可用性条件来进行不可用性检查。举例如下,两种检查都能实现同样的效果:
```swift
if #available(iOS 10, *){
} else {
//回滚代码
}
if #unavailable(iOS 10) {
//回滚代码
}
```
若可用性检查只提供了回滚代码,改用用 `#unavailable` 能提升程序整体的可读性。

View File

@ -106,7 +106,7 @@ greet(person: "Dave")
> 注意
>
> 严格地说,即使没有明确定义返回值,该 `greet(Person)` 函数仍然返回一个值。没有明确定义返回类型的函数返回一个 `Void` 类型特殊值,该值为一个空元组,写成 ()。
> 严格地说,即使没有明确定义返回值,该 `greet(Person)` 函数仍然返回一个值。没有明确定义返回类型的函数返回一个 `Void` 类型特殊值,该值为一个空元组,写成 ()。
调用函数时,可以忽略该函数的返回值:
@ -206,7 +206,7 @@ if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
### 隐式返回的函数 {#functions-with-an-implicit-return}
如果一个函数的整个函数体是一个单行表达式,这个函数可以隐式地返回这个表达式。举个例子,以下的函数有着同样的作用:
```swift
```
func greeting(for person: String) -> String {
"Hello, " + person + "!"
}
@ -225,9 +225,6 @@ print(anotherGreeting(for: "Dave"))
正如你将会在 [简略的 Getter 声明](./10_Properties.md) 里看到的, 一个属性的 getter 也可以使用隐式返回的形式。
>注意
>作为隐式返回值编写的代码需要返回一些值。例如,你不能使用 `print(13)` 作为隐式返回值。然而,你可以使用不返回值的函数(如 `fatalError("Oh no!")`)作为隐式返回值,因为 Swift 知道它们并不会产生任何隐式返回。
## 函数参数标签和参数名称 {#Function-Argument-Labels-and-Parameter-Names}
@ -240,7 +237,7 @@ func someFunction(firstParameterName: Int, secondParameterName: Int) {
someFunction(firstParameterName: 1, secondParameterName: 2)
```
所有的参数都必须有一个独一无二的名字。虽然多个参数拥有同样的参数标签是可能的,但是一个唯一的数标签能够使你的代码更具可读性。
所有的参数都必须有一个独一无二的名字。虽然多个参数拥有同样的参数标签是可能的,但是一个唯一的数标签能够使你的代码更具可读性。
### 指定参数标签 {#specifying-argument-labels}
@ -313,13 +310,15 @@ arithmeticMean(3, 8.25, 18.75)
// 返回 10.0, 是这 3 个数的平均数。
```
一个函数能拥有多个可变参数。可变参数后的第一个形参前必须加上实参标签。实参标签用于区分实参是传递给可变参数,还是后面的形参。
> 注意
>
> 一个函数最多只能拥有一个可变参数。
### 输入输出参数 {#in-out-parameters}
函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为*输入输出参数In-Out Parameters*。
定义一个输入输出参数时,在参数定义前加 `inout` 关键字。一个 `输入输出参数`有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看 [输入输出参数](../03_language_reference/06_Declarations.md#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) 一节。
你只能传递变量给输入输出参数。你不能传入常量或者字面量,因为这些量是不能被修改的。当传入的参数作为输入输出参数时,需要在参数名前加 `&` 符,表示这个值可以被函数修改。
@ -457,7 +456,7 @@ func stepBackward(_ input: Int) -> Int {
}
```
如下名为 `chooseStepFunction(backward:)` 的函数,它的返回类型是 `(Int) -> Int` 类型的函数。`chooseStepFunction(backward:)` 根据布尔值 `backward` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数:
如下名为 `chooseStepFunction(backward:)` 的函数,它的返回类型是 `(Int) -> Int` 类型的函数。`chooseStepFunction(backward:)` 根据布尔值 `backwards` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数:
```swift
func chooseStepFunction(backward: Bool) -> (Int) -> Int {

View File

@ -6,7 +6,7 @@
> 注意
>
> 如果你不熟悉捕获capturing这个概念也不用担心在 [值捕获](#capturing-values) 章节有它更详细的介绍。
> 如果你不熟悉捕获capturing这个概念也不用担心在 [值捕获](#capturing_values) 章节有它更详细的介绍。
在 [函数](./06_Functions.md) 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采用如下三种形式之一:
@ -23,7 +23,7 @@ Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进
## 闭包表达式 {#closure-expressions}
[嵌套函数](./06_Functions.md#Nested-Functions) 作为复杂函数的一部分时,它自包含代码块式的定义和命名形式在使用上带来了方便。当然,编写未完整声明和没有函数名的类函数结构代码是很有用的,尤其是在编码中涉及到函数作为参数的那些方法时。
[嵌套函数](./06_Functions.md#Nested_Functions) 作为复杂函数的一部分时,它自包含代码块式的定义和命名形式在使用上带来了方便。当然,编写未完整声明和没有函数名的类函数结构代码是很有用的,尤其是在编码中涉及到函数作为参数的那些方法时。
*闭包表达式*是一种构建内联闭包的方式,它的语法简洁。在保证不丢失它语法清晰明了的同时,闭包表达式提供了几种优化的语法简写形式。下面通过对 `sorted(by:)` 这一个案例的多次迭代改进来展示这个过程,每次迭代都使用了更加简明的方式描述了相同功能。。
@ -113,13 +113,13 @@ reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
Swift 自动为内联闭包提供了参数名称缩写功能,你可以直接通过 `$0``$1``$2` 来顺序调用闭包的参数,以此类推。
如果你在闭包表达式中使用参数名称缩写,你可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。闭包接受的参数的数量取决于所使用的缩写参数的最大编号。`in` 关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
如果你在闭包表达式中使用参数名称缩写,你可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。`in` 关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
```swift
reversedNames = names.sorted(by: { $0 > $1 } )
```
在这个例子中,`$0``$1` 表示闭包中第一个和第二个 `String` 类型的参数。因为 `$1` 是编号最大的缩写参数,所以可以理解为:该闭包需要两个参数。这里的 `sorted(by:)` 函数希望得到一个参数都是字符串的闭包,因此缩写参数 `$0``$1` 的类型均为 `String`
在这个例子中,`$0``$1` 表示闭包中第一个和第二个 `String` 类型的参数。
### 运算符方法 {#operator-methods}
@ -129,7 +129,7 @@ reversedNames = names.sorted(by: { $0 > $1 } )
reversedNames = names.sorted(by: >)
```
更多关于运算符方法的内容请查看 [运算符方法](./27_Advanced_Operators.md#operator-methods)。
更多关于运算符方法的内容请查看 [运算符方法](./26_Advanced_Operators.md#operator_methods)。
## 尾随闭包 {#trailing-closures}
@ -151,7 +151,7 @@ someFunctionThatTakesAClosure() {
}
```
在 [闭包表达式语法](#closure-expression-syntax) 上章节中的字符串排序闭包可以作为尾随包的形式改写在 `sorted(by:)` 方法圆括号的外面:
在 [闭包表达式语法](#closure_expression_syntax) 上章节中的字符串排序闭包可以作为尾随包的形式改写在 `sorted(by:)` 方法圆括号的外面:
```swift
reversedNames = names.sorted() { $0 > $1 }
@ -214,35 +214,6 @@ let strings = numbers.map {
在上面的例子中,通过尾随闭包语法,优雅地在函数后封装了闭包的具体功能,而不再需要将整个闭包包裹在 `map(_:)` 方法的括号内。
如果一个函数接受多个闭包,您需要省略第一个尾随闭包的参数标签,并为其余尾随闭包添加标签。例如,以下函数将为图片库加载一张图片:
```swift
func loadPicture(from server: Server, completion:(Picture) -> Void,
onFailure: () -> Void) {
if let picture = download("photo.jpg", from: server){
completion(picture)
}else{
onFailure()
}
}
```
当您调用该函数以加载图片时,需要提供两个闭包。第一个闭包是一个完成处理程序,它在成功下载后加载图片;第二个闭包是一个错误处理程序,它向用户显示错误。
```swift
loadPicture(from: someServer){ picture in
someView.currentPicture = picture
} onFailure: {
print("Couldn't download the next picture.")
}
```
在本例中,`loadPicture(from:completion:onFailure:)` 函数将它的网络任务分配到后台,并在网络任务完成时调用两个完成处理程序中的一个。通过这种方法编写函数,您将能够把负责处理网络故障的代码和成功下载后更新用户界面的代码干净地区分开,而不是只使用一个闭包处理两种情况。
>注意
>
>完成处理程序可能很难阅读,特别是您必须嵌套多个完成处理程序时。另一种方法是使用异步代码,如章节[并发](./28_Concurrency.md#function-types-as-return-types) 中所述。
## 值捕获 {#capturing-values}
闭包可以在其被定义的上下文中*捕获*常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
@ -262,7 +233,7 @@ func makeIncrementer(forIncrement amount: Int) -> () -> Int {
}
```
`makeIncrementer` 返回类型为 `() -> Int`。这意味着其返回的是一个*函数*,而非一个简单类型的值。该函数在每次调用时不接受参数,只返回一个 `Int` 类型的值。关于函数返回其他函数的内容,请查看 [函数类型作为返回类型](./06_Functions.md#function-types-as-return-types)。
`makeIncrementer` 返回类型为 `() -> Int`。这意味着其返回的是一个*函数*,而非一个简单类型的值。该函数在每次调用时不接受参数,只返回一个 `Int` 类型的值。关于函数返回其他函数的内容,请查看 [函数类型作为返回类型](./06_Functions.md#function_types_as_return_types)。
`makeIncrementer(forIncrement:)` 函数定义了一个初始值为 `0` 的整型变量 `runningTotal`,用来存储当前总计数值。该值为 `incrementer` 的返回值。
@ -319,7 +290,7 @@ incrementByTen()
> 注意
>
> 如果你将闭包赋值给一个类实例的属性并且该闭包通过访问该实例或其成员而捕获了该实例你将在闭包和该实例间创建一个循环强引用。Swift 使用捕获列表来打破这种循环强引用。更多信息,请参考 [闭包引起的循环强引用](./24_Automatic_Reference_Counting.md#strong-reference-cycles-for-closures)。
> 如果你将闭包赋值给一个类实例的属性并且该闭包通过访问该实例或其成员而捕获了该实例你将在闭包和该实例间创建一个循环强引用。Swift 使用捕获列表来打破这种循环强引用。更多信息,请参考 [闭包引起的循环强引用](./23_Automatic_Reference_Counting.md#strong_reference_cycles_for_closures)。
## 闭包是引用类型 {#closures-are-reference-types}
@ -393,7 +364,7 @@ print(customersInLine.count)
// 打印出“5”
print("Now serving \(customerProvider())!")
// 打印出“Now serving Chris!
// Prints "Now serving Chris!"
print(customersInLine.count)
// 打印出“4”
```
@ -426,7 +397,7 @@ serve(customer: customersInLine.remove(at: 0))
>
> 过度使用 `autoclosures` 会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。
如果你想让一个自动闭包可以“逃逸”,则应该同时使用 `@autoclosure``@escaping` 属性。`@escaping` 属性的讲解见上面的 [逃逸闭包](#escaping-closures)。
如果你想让一个自动闭包可以“逃逸”,则应该同时使用 `@autoclosure``@escaping` 属性。`@escaping` 属性的讲解见上面的 [逃逸闭包](#escaping_closures)。
```swift
// customersInLine i= ["Barry", "Daniella"]

View File

@ -114,7 +114,7 @@ print("\(numberOfChoices) beverages available")
// 打印“3 beverages available”
```
在前面的例子中,通过 `Beverage.allCases` 可以访问到包含 `Beverage` 枚举所有成员的集合。`allCases` 的使用方法和其它一般集合一样——集合中的元素是枚举类型的实例,所以在上面的情况中,这些元素是 `Beverage` 值。在前面的例子中,统计了总共有多少个枚举成员。而在下面的例子中,则使用 `for-in` 循环来遍历所有枚举成员。
在前面的例子中,通过 `Beverage.allCases` 可以访问到包含 `Beverage` 枚举所有成员的集合。`allCases` 的使用方法和其它一般集合一样——集合中的元素是枚举类型的实例,所以在上面的情况中,这些元素是 `Beverage` 值。在前面的例子中,统计了总共有多少个枚举成员。而在下面的例子中,则使用 `for` 循环来遍历所有枚举成员。
```swift
for beverage in Beverage.allCases {
@ -135,11 +135,11 @@ for beverage in Beverage.allCases {
例如,假设一个库存跟踪系统需要利用两种不同类型的条形码来跟踪商品。有些商品上标有使用 `0``9` 的数字的 UPC 格式的一维条形码。每一个条形码都有一个代表数字系统的数字,该数字后接五位代表厂商代码的数字,接下来是五位代表“产品代码”的数字。最后一个数字是检查位,用来验证代码是否被正确扫描:
<img width="252" height="120" alt="" src="https://docs.swift.org/swift-book/images/barcode_UPC@2x.png">
<img width="252" height="120" alt="" src="https://docs.swift.org/swift-book/_images/barcode_UPC_2x.png">
其他商品上标有 QR 码格式的二维码,它可以使用任何 ISO 8859-1 字符,并且可以编码一个最多拥有 2,953 个字符的字符串:
<img width="169" height="169" alt="" src="https://docs.swift.org/swift-book/images/barcode_QR@2x.png">
<img width="169" height="169" alt="" src="https://docs.swift.org/swift-book/_images/barcode_QR_2x.png">
这便于库存跟踪系统用包含四个整型值的元组存储 UPC 码,以及用任意长度的字符串储存 QR 码。
@ -200,7 +200,7 @@ case let .qrCode(productCode):
## 原始值 {#raw-values}
在 [关联值](#associated-values) 小节的条形码例子中,演示了如何声明存储不同类型关联值的枚举成员。作为关联值的替代选择,枚举成员可以被默认值(称为*原始值*)预填充,这些原始值的类型必须相同。
在 [关联值](#associated_values) 小节的条形码例子中,演示了如何声明存储不同类型关联值的枚举成员。作为关联值的替代选择,枚举成员可以被默认值(称为*原始值*)预填充,这些原始值的类型必须相同。
这是一个使用 ASCII 码作为原始值的枚举:
@ -273,7 +273,7 @@ let possiblePlanet = Planet(rawValue: 7)
> 注意
>
> 原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见 [可失败构造器](../03_language_reference/06_Declarations.md#failable-initializers)
> 原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见 [可失败构造器](../chapter3/05_Declarations.html#failable_initializers)
如果你试图寻找一个位置为 `11` 的行星,通过原始值构造器返回的可选 `Planet` 值将是 `nil`

View File

@ -1,6 +1,6 @@
# 结构体和类
*结构体*和*类*作为一种通用而又灵活的结构,成为了人们构建代码的基础。你可以使用定义常量、变量和函数的语法,为你的结构体和类定义属性、添加方法。
*结构体*和*类*作为一种通用而又灵活的结构,成为了人们构建代码的基础。你可以使用定义常量、变量和函数的语法,为你的结构体和类定义属性、添加方法。
与其他编程语言所不同的是Swift 并不要求你为自定义的结构体和类的接口与实现代码分别创建文件。你只需在单一的文件中定义一个结构体或者类,系统将会自动生成面向其它代码的外部接口。
@ -28,14 +28,10 @@ Swift 中结构体和类有很多共同点。两者都可以:
* 析构器允许一个类实例释放任何其所被分配的资源
* 引用计数允许对一个类的多次引用
更多信息请参见 [继承](./13_Inheritance.md)、[类型转换](./18_Type_Casting.md)、[析构过程](./15_Deinitialization.md) 和 [自动引用计数](./24_Automatic_Reference_Counting.md)。
更多信息请参见 [继承](./13_Inheritance.md)、[类型转换](./18_Type_Casting.md)、[析构过程](./15_Deinitialization.md) 和 [自动引用计数](./23_Automatic_Reference_Counting.md)。
类支持的附加功能是以增加复杂性为代价的。作为一般准则,优先使用结构体,因为它们更容易理解,仅在适当或必要时才使用类。实际上,这意味着你的大多数自定义数据类型都会是结构体和枚举。更多详细的比较参见 [在结构和类之间进行选择](https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes)。
> 注意
>
> 类和 actors 共享很多特性。更多信息请参见 [并发](./28_Concurrency.md)。
### 类型定义的语法 {#definition-syntax}
结构体和类有着相似的定义方式。你通过 `struct` 关键字引入结构体,通过 `class` 关键字引入类,并将它们的具体定义放在一对大括号中:
@ -129,7 +125,7 @@ let vga = Resolution(width: 640, height: 480)
Swift 中所有的结构体和枚举类型都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型的属性,在代码中传递的时候都会被复制。
> 注意
> 注意
>
> 标准库定义的集合,例如数组,字典和字符串,都对复制进行了优化以降低性能成本。新集合不会立即复制,而是跟原集合共享同一份内存,共享同样的元素。在集合的某个副本要被修改前,才会复制它的元素。而你在代码中看起来就像是立即发生了复制。
@ -166,7 +162,7 @@ print("hd is still \(hd.width) pixels wide")
`hd` 赋值给 `cinema` 时,`hd` 中所存储的*值*会拷贝到新的 `cinema` 实例中。结果就是两个完全独立的实例包含了相同的数值。由于两者相互独立,因此将 `cinema``width` 修改为 `2048` 并不会影响 `hd` 中的 `width` 的值,如下图所示:
![sharedStateStruct_2x](https://docs.swift.org/swift-book/images/sharedStateStruct@2x.png)
![sharedStateStruct_2x](https://docs.swift.org/swift-book/_images/sharedStateStruct_2x.png)
枚举也遵循相同的行为准则:
@ -214,7 +210,7 @@ alsoTenEighty.frameRate = 30.0
因为类是引用类型,所以 `tenEight``alsoTenEight` 实际上引用的是同一个 `VideoMode` 实例。换句话说,它们是同一个实例的两种叫法,如下图所示:
![sharedStateClass_2x](https://docs.swift.org/swift-book/images/sharedStateClass@2x.png)
![sharedStateClass_2x](https://docs.swift.org/swift-book/_images/sharedStateClass_2x.png)
通过查看 `tenEighty``frameRate` 属性,可以看到它正确地显示了底层的 `VideoMode` 实例的新帧率 `30.0`
@ -225,7 +221,7 @@ print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
这个例子也显示了为何引用类型更加难以理解。如果 `tenEighty``alsoTenEighty` 在你代码中的位置相距很远,那么就很难找到所有修改视频模式的地方。无论在哪使用 `tenEighty`,你都要考虑使用 `alsoTenEighty` 的代码,反之亦然。相反,值类型就更容易理解了,因为你的源码中与同一个值交互的代码都很近。
需要注意的是 `tenEighty``alsoTenEighty` 被声明为常量而不是变量。然而你依然可以改变 `tenEighty.frameRate``alsoTenEighty.frameRate`,这是因为 `tenEighty``alsoTenEighty` 这两个常量的值并未改变。它们并不“存储”这个 `VideoMode` 实例,而仅仅是对 `VideoMode` 实例的引用。所以,改变的是底层 `VideoMode` 实例的 `frameRate` 属性,而不是指向 `VideoMode` 的常量引用的值。
需要注意的是 `tenEighty``alsoTenEighty` 被声明为常量而不是变量。然而你依然可以改变 `tenEighty.frameRate``alsoTenEighty.frameRate`,这是因为 `tenEighty``alsoTenEighty` 这两个常量的值并未改变。它们并不“存储”这个 `VideoMode` 实例,而仅仅是对 `VideoMode` 实例的引用。所以,改变的是底层 `VideoMode` 实例的 `frameRate` 属性,而不是指向 `VideoMode` 的常量引用的值。
### 恒等运算符 {#identity-operators}
@ -247,8 +243,8 @@ if tenEighty === alsoTenEighty {
请注意,“相同”(用三个等号表示,`===`)与“等于”(用两个等号表示,`==`的不同。“相同”表示两个类类型class type的常量或者变量引用同一个类实例。“等于”表示两个实例的值“相等”或“等价”判定时要遵照设计者定义的评判标准。
当在定义你的自定义结构体和类的时候,你有义务来决定判定两个实例“相等”的标准。在章节 [等价操作符](./27_Advanced_Operators.md#equivalence-operators) 中将会详细介绍实现自定义 == 和 != 运算符的流程。
当在定义你的自定义结构体和类的时候,你有义务来决定判定两个实例“相等”的标准。在章节 [等价操作符](./26_Advanced_Operators.md#equivalence_operators) 中将会详细介绍实现自定义 == 和 !== 运算符的流程。
### 指针 {#pointers}
如果你有 CC++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用*指针*来引用内存中的地址。Swift 中引用了某个引用类型实例的常量或变量,与 C 语言中的指针类似,不过它并不直接指向某个内存地址,也不要求你使用星号(`*`来表明你在创建一个引用。相反Swift 中引用的定义方式与其它的常量或变量的一样。如果需要直接与指针交互,你可以使用标准库提供的指针和缓冲区类型 —— 参见 [手动管理内存](https://developer.apple.com/documentation/swift/swift_standard_library/manual_memory_management)。
如果你有 CC++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用*指针*来引用内存中的地址。Swift 中引用了某个引用类型实例的常量或变量,与 C 语言中的指针类似,不过它并不直接指向某个内存地址,也不要求你使用星号(`*`来表明你在创建一个引用。相反Swift 中引用的定义方式与其它的常量或变量的一样。如果需要直接与指针交互,你可以使用标准库提供的指针和缓冲区类型 —— 参见 [手动管理内存](https://developer.apple.com/documentation/swift/swift_standard_library/manual_memory_management)。

View File

@ -6,12 +6,11 @@
另外,还可以定义属性观察器来监控属性值的变化,以此来触发自定义的操作。属性观察器可以添加到类本身定义的存储属性上,也可以添加到从父类继承的属性上。
你也可以利用属性包装器来复用多个属性的 getter 和 setter 中的代码。
## 存储属性 {#stored-properties}
简单来说,一个存储属性就是存储在特定类或结构体实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字 `var` 定义),也可以是*常量存储属性*(用关键字 `let` 定义)。
可以在定义存储属性的时候指定默认值,请参考 [默认构造器](./14_Initialization.md#default-initializers) 一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考 [构造过程中常量属性的修改](./14_Initialization.md#assigning-constant-properties-during-initialization) 一节。
可以在定义存储属性的时候指定默认值,请参考 [默认构造器](./14_Initialization.md#default_initializers) 一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考 [构造过程中常量属性的修改](./14_Initialization.md#assigning_constant_properties_during_initialization) 一节。
下面的例子定义了一个名为 `FixedLengthRange` 的结构体,该结构体用于描述整数的区间,且这个范围值在被创建后不能被修改。
@ -69,7 +68,7 @@ class DataImporter {
class DataManager {
lazy var importer = DataImporter()
var data: [String] = []
var data = [String]()
// 这里会提供数据管理功能
}
@ -99,13 +98,13 @@ print(manager.importer.fileName)
### 存储属性和实例变量 {#stored-properties-and-instance-variables}
如果有过 Objective-C 经验,应该知道 Objective-C 为类实例存储值和引用提供两种方法。除了属性之外,还可以使用实例变量作为一个备份存储将变量值赋值给属性。
如果有过 Objective-C 经验,应该知道 Objective-C 为类实例存储值和引用提供两种方法。除了属性之外,还可以使用实例变量作为一个备份存储将变量值赋值给属性。
Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的备份存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。属性的全部信息——包括命名、类型和内存管理特征——作为类型定义的一部分,都定义在一个地方。
## 计算属性 {#computed-properties}
除存储属性外,类、结构体和枚举可以定义*计算属性*。计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter来间接获取和设置其他属性或变量的值。
除存储属性外,类、结构体和枚举可以定义*计算属性*。计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter来间接获取和设置其他属性或变量的值。
```swift
struct Point {
@ -132,7 +131,6 @@ struct Rect {
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
// initialSquareCenter 位于5.0 5.0
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// 打印“square.origin is now at (10.0, 10.0)”
@ -148,11 +146,11 @@ print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
上述例子中创建了一个名为 `square``Rect` 实例,初始值原点是 `(0, 0)`,宽度高度都是 `10`。如下图中蓝色正方形所示。
`square``center` 属性可以通过点运算符(`square.center`)来访问,这会调用该属性的 getter 来获取它的值。跟直接返回已经存在的值不同getter 实际上通过计算然后返回一个新的 `Point` 来表示 `square` 的中心点。如代码所示,它正确返回了中心点 `(5, 5)`
`square``center` 属性可以通过点运算符(`square.center`)来访问,这会调用该属性的 getter 来获取它的值。跟直接返回已经存在的值不同getter 实际上通过计算然后返回一个新的 `Point` 来表示 `square` 的中心点。如代码所示,它正确返回了中心点 `(5, 5)`
`center` 属性之后被设置了一个新的值 `(15, 15)`,表示向右上方移动正方形到如下图橙色正方形所示的位置。设置属性 `center` 的值会调用它的 setter 来修改属性 `origin``x``y` 的值,从而实现移动正方形到新的位置。
<img src="https://docs.swift.org/swift-book/images/computedProperties@2x.png" alt="Computed Properties sample" width="388" height="387" />
<img src="https://docs.swift.org/swift-book/_images/computedProperties_2x.png" alt="Computed Properties sample" width="388" height="387" />
### 简化 Setter 声明 {#shorthand-setter-declaration}
@ -226,12 +224,7 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外。
你可以在以下位置添加属性观察器:
* 自定义的存储属性
* 继承的存储属性
* 继承的计算属性
对于继承的属性,你可以在子类中通过重写属性的方式为它添加属性观察器。对于自定义的计算属性来说,使用它的 setter 监控和响应值的变化,而不是尝试创建观察器。属性重写请参考 [重写](./13_Inheritance.md#overriding)。
你可以为除了延时加载存储属性之外的其他存储属性添加属性观察器,你也可以在子类中通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。你不必为非重写的计算属性添加属性观察器,因为你可以直接通过它的 setter 监控和响应值的变化。属性重写请参考 [重写](./13_Inheritance.md#overriding)。
可以为属性添加其中一个或两个观察器:
@ -246,7 +239,7 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
>
> 在父类初始化方法调用之后,在子类构造器中给父类的属性赋值时,会调用父类属性的 `willSet``didSet` 观察器。而在父类初始化方法调用之前,给子类的属性赋值时不会调用子类属性的观察器。
>
> 有关构造器代理的更多信息,请参考 [值类型的构造器代理](./14_Initialization.md#initializer-delegation-for-value-types) 和 [类的构造器代理](./14_Initialization.md#initializer-delegation-for-class-types)。
> 有关构造器代理的更多信息,请参考 [值类型的构造器代理](./14_Initialization.md#initializer_delegation_for_value_types) 和 [类的构造器代理](./14_Initialization.md#initializer_delegation_for_class_types)。
下面是一个 `willSet``didSet` 实际运用的例子,其中定义了一个名为 `StepCounter` 的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。
@ -285,248 +278,7 @@ stepCounter.totalSteps = 896
> 注意
>
> 如果将带有观察器的属性通过 in-out 方式传入函数,`willSet``didSet` 也会调用。这是因为 in-out 参数采用了拷入拷出内存模式:即在函数内部使用的是参数的 copy函数结束后又对参数重新赋值。关于 in-out 参数详细的介绍,请参考 [输入输出参数](../03_language_reference/06_Declarations.md#in-out-parameters)
## 属性包装器 {#property-wrappers}
属性包装器在管理属性如何存储和定义属性的代码之间添加了一个分隔层。举例来说,如果你的属性需要线程安全性检查或者需要在数据库中存储它们的基本数据,那么必须给每个属性添加同样的逻辑代码。当使用属性包装器时,你只需在定义属性包装器时编写一次管理代码,然后应用到多个属性上来进行复用。
定义一个属性包装器,你需要创建一个定义 `wrappedValue` 属性的结构体、枚举或者类。在下面的代码中,`TwelveOrLess` 结构体确保它包装的值始终是小于等于 12 的数字。如果要求它存储一个更大的数字,它则会存储 12 这个数字。
```swift
@propertyWrapper
struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
```
这个 setter 确保新值小于或等于 12而且返回被存储的值。
> 注意
>
> 上面例子以 `private` 的方式声明 `number` 变量,这使得 `number` 仅在 `TwelveOrLess` 的实现中使用。写在其他地方的代码通过使用 `wrappedValue` 的 getter 和 setter 来获取这个值,但不能直接使用 `number`。有关 `private` 的更多信息,请参考 [访问控制](./26_Access_Control.md)
通过在属性之前写上包装器名称作为特性的方式,你可以把一个包装器应用到一个属性上去。这里有个存储小矩形的结构体,通过 `TwelveOrLess` 属性包装器来确保它的长宽均小于等于 12。
```swift
struct SmallRectangle {
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
}
var rectangle = SmallRectangle()
print(rectangle.height)
// 打印 "0"
rectangle.height = 10
print(rectangle.height)
// 打印 "10"
rectangle.height = 24
print(rectangle.height)
// 打印 "12"
```
`height``width` 属性从 `TwelveOrLess` 的定义中获取它们的初始值。该定义把 `TwelveOrLess.number` 设置为 0。把数字 10 存进 `rectangle.height` 中的操作能成功,是因为数字 10 很小。尝试存储 24 的操作实际上存储的值为 12这是因为对于这个属性的 setter 的规则来说24 太大了。
当你把一个包装器应用到一个属性上时,编译器将合成提供包装器存储空间和通过包装器访问属性的代码。(属性包装器只负责存储被包装值,所以没有合成这些代码。)不利用这个特性语法的情况下,你可以写出使用属性包装器行为的代码。举例来说,这是先前代码清单中的 `SmallRectangle` 的另一个版本。这个版本将其属性明确地包装在 `TwelveOrLess` 结构体中,而不是把 `@TwelveOrLess` 作为特性写下来:
```swift
struct SmallRectangle {
private var _height = TwelveOrLess()
private var _width = TwelveOrLess()
var height: Int {
get { return _height.wrappedValue }
set { _height.wrappedValue = newValue }
}
var width: Int {
get { return _width.wrappedValue }
set { _width.wrappedValue = newValue }
}
}
```
`_height``_width` 属性存着这个属性包装器的一个实例,即 `TwelveOrLess``height``width` 的 getter 和 setter 把对 `wrappedValue` 属性的访问包装起来。
### 设置被包装属性的初始值 {#setting-initial-values-for-wrapped-properties}
上面例子中的代码通过在 `TwelveOrLess` 的定义中赋予 `number` 一个初始值来设置被包装属性的初始值。使用这个属性包装器的代码没法为被 `TwelveOrLess` 包装的属性指定其他初始值。举例来说,`SmallRectangle` 的定义没法给 `height` 或者 `width` 一个初始值。为了支持设定一个初始值或者其他自定义操作,属性包装器需要添加一个构造器。这是 `TwelveOrLess` 的扩展版本,称为 `SmallNumber``SmallNumber` 定义了能设置被包装值和最大值的构造器:
```swift
@propertyWrapper
struct SmallNumber {
private var maximum: Int
private var number: Int
var wrappedValue: Int {
get { return number }
set { number = min(newValue, maximum) }
}
init() {
maximum = 12
number = 0
}
init(wrappedValue: Int) {
maximum = 12
number = min(wrappedValue, maximum)
}
init(wrappedValue: Int, maximum: Int) {
self.maximum = maximum
number = min(wrappedValue, maximum)
}
}
```
`SmallNumber` 的定义包括三个构造器——`init()``init(wrappedValue:)``init(wrappedValue:maximum:)`——下面的示例使用这三个构造器来设置被包装值和最大值。有关构造过程和构造器语法的更多信息,请参考 [构造过程](./14_Initialization.md)。
当你把包装器应用于属性且没有设定初始值时Swift 使用 `init()` 构造器来设置包装器。举个例子:
```swift
struct ZeroRectangle {
@SmallNumber var height: Int
@SmallNumber var width: Int
}
var zeroRectangle = ZeroRectangle()
print(zeroRectangle.height, zeroRectangle.width)
// 打印 "0 0"
```
调用 `SmallNumber()` 来创建包装 `height``width``SmallNumber` 的实例。构造器内部的代码使用默认值 0 和 12 设置初始的被包装值和初始的最大值。像之前使用在 `SmallRectangle` 中使用 `TwelveOrLess` 的例子,这个属性包装器仍然提供所有的初始值。与这个例子不同的是,`SmallNumber` 也支持把编写这些初始值作为声明属性的一部分。
当你为属性指定初始值时Swift 使用 `init(wrappedValue:)` 构造器来设置包装器。举个例子:
```swift
struct UnitRectangle {
@SmallNumber var height: Int = 1
@SmallNumber var width: Int = 1
}
var unitRectangle = UnitRectangle()
print(unitRectangle.height, unitRectangle.width)
// 打印 "1 1"
```
当你对一个被包装的属性写下 `= 1` 时,这被转换为调用 `init(wrappedValue:)` 构造器。调用 `SmallNumber(wrappedValue: 1)`来创建包装 `height``width``SmallNumber` 的实例。构造器使用此处指定的被包装值,且使用的默认最大值为 12。
当你在自定义特性后面把实参写在括号里时Swift 使用接受这些实参的构造器来设置包装器。举例来说如果你提供初始值和最大值Swift 使用 `init(wrappedValue:maximum:)` 构造器:
```swift
struct NarrowRectangle {
@SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
@SmallNumber(wrappedValue: 3, maximum: 4) var width: Int
}
var narrowRectangle = NarrowRectangle()
print(narrowRectangle.height, narrowRectangle.width)
// 打印 "2 3"
narrowRectangle.height = 100
narrowRectangle.width = 100
print(narrowRectangle.height, narrowRectangle.width)
// 打印 "5 4"
```
调用 `SmallNumber(wrappedValue: 2, maximum: 5)` 来创建包装 `height``SmallNumber` 的一个实例。调用 `SmallNumber(wrappedValue: 3, maximum: 4)` 来创建包装 `width``SmallNumber` 的一个实例。
通过将实参包含到属性包装器中,你可以设置包装器的初始状态,或者在创建包装器时传递其他的选项。这种语法是使用属性包装器最通用的方法。你可以为这个属性提供任何所需的实参,且它们将被传递给构造器。
当包含属性包装器实参时你也可以使用赋值来指定初始值。Swift 将赋值视为 `wrappedValue` 参数,且使用接受被包含的实参的构造器。举个例子:
```swift
struct MixedRectangle {
@SmallNumber var height: Int = 1
@SmallNumber(maximum: 9) var width: Int = 2
}
var mixedRectangle = MixedRectangle()
print(mixedRectangle.height)
// 打印 "1"
mixedRectangle.height = 20
print(mixedRectangle.height)
// 打印 "12"
```
调用 `SmallNumber(wrappedValue: 1)` 来创建包装 `height``SmallNumber` 的一个实例,这个实例使用默认最大值 12。调用 `SmallNumber(wrappedValue: 2, maximum: 9)` 来创建包装 `width``SmallNumber` 的一个实例。
### 从属性包装器中呈现一个值 {#projecting-a-value-from-a-property-wrapper}
除了被包装值,属性包装器可以通过定义被呈现值暴露出其他功能。举个例子,管理对数据库的访问的属性包装器可以在它的被呈现值上暴露出 `flushDatabaseConnection()` 方法。除了以货币符号(\$)开头,被呈现值的名称和被包装值是一样的。因为你的代码不能够定义以 $ 开头的属性,所以被呈现值永远不会与你定义的属性有冲突。
在之前 `SmallNumber` 的例子中,如果你尝试把这个属性设置为一个很大的数值,属性包装器会在存储这个数值之前调整这个数值。以下的代码把被呈现值添加到 `SmallNumber` 结构体中来追踪在存储新值之前属性包装器是否为这个属性调整了新值。
```swift
@propertyWrapper
struct SmallNumber {
private var number: Int
private(set) var projectedValue: Bool
var wrappedValue: Int {
get { return number }
set {
if newValue > 12 {
number = 12
projectedValue = true
} else {
number = newValue
projectedValue = false
}
}
}
init() {
self.number = 0
self.projectedValue = false
}
}
struct SomeStructure {
@SmallNumber var someNumber: Int
}
var someStructure = SomeStructure()
someStructure.someNumber = 4
print(someStructure.$someNumber)
// 打印 "false"
someStructure.someNumber = 55
print(someStructure.$someNumber)
// 打印 "true"
```
写下 `someStructure.$someNumber` 即可访问包装器的被呈现值。在存储一个比较小的数值时,如 4 `someStructure.$someNumber` 的值为 `false`。但是,在尝试存储一个较大的数值时,如 55 ,被呈现值变为 `true`
属性包装器可以返回任何类型的值作为它的被呈现值。在这个例子里,属性包装器要暴露的信息是:那个数值是否被调整过,所以它暴露出布尔型值来作为它的被呈现值。需要暴露出更多信息的包装器可以返回其他数据类型的实例,或者可以返回自身来暴露出包装器的实例,并把其作为它的被呈现值。
当从类型的一部分代码中访问被呈现值,例如属性 getter 或实例方法,你可以在属性名称之前省略 `self.`,就像访问其他属性一样。以下示例中的代码用 `$height``$width` 引用包装器 `height``width` 的被呈现值:
```swift
enum Size {
case small, large
}
struct SizedRectangle {
@SmallNumber var height: Int
@SmallNumber var width: Int
mutating func resize(to size: Size) -> Bool {
switch size {
case .small:
height = 10
width = 20
case .large:
height = 100
width = 100
}
return $height || $width
}
}
```
因为属性包装器语法只是具有 getter 和 setter 的属性的语法糖,所以访问 `height``width` 的行为与访问任何其他属性的行为相同。举个例子,`resize(to:)` 中的代码使用它们的属性包装器来访问 `height``width`。如果调用 `resize(to: .large)``.large` 的 switch case 分支语句把矩形的高度和宽度设置为 100。属性包装器防止这些属性的值大于 12且把被呈现值设置成为 `true` 来记下它调整过这些值的事实。在 `resize(to:)` 的最后,返回语句检查 `$height``$width` 来确认是否属性包装器调整过 `height``width`
> 如果将带有观察器的属性通过 in-out 方式传入函数,`willSet``didSet` 也会调用。这是因为 in-out 参数采用了拷入拷出内存模式:即在函数内部使用的是参数的 copy函数结束后又对参数重新赋值。关于 in-out 参数详细的介绍,请参考 [输入输出参数](../chapter3/05_Declarations.html#in-out_parameters)
## 全局变量和局部变量 {#global-and-local-variables}
@ -538,26 +290,10 @@ struct SizedRectangle {
> 注意
>
> 全局的常量或变量都是延迟计算的,跟 [延时加载存储属性](#lazy-stored-properties) 相似,不同的地方在于,全局的常量或变量不需要标记 `lazy` 修饰符。
> 全局的常量或变量都是延迟计算的,跟 [延时加载存储属性](#lazy_stored_properties) 相似,不同的地方在于,全局的常量或变量不需要标记 `lazy` 修饰符。
>
> 局部范围的常量和变量从不延迟计算。
可以在局部存储型变量上使用属性包装器,但不能在全局变量或者计算型变量上使用。比如下面的代码,`myNumber` 使用 `SmallNumber` 作为属性包装器。
```swift
func someFunction() {
@SmallNumber var myNumber: Int = 0
myNumber = 10
// 这时 myNumber 是 10
myNumber = 24
// 这时 myNumber 是 12
}
```
就像将 `SmallNumber` 应用到属性上一样,将 `myNumber` 赋值为 10 是有效的。而因为这个属性包装器不允许值大于 12`myNumber` 赋值为 24 时则会变成 12。
## 类型属性 {#type-properties}
实例属性属于一个特定类型的实例,每创建一个实例,实例都拥有属于自己的一套属性值,实例之间的属性相互独立。
@ -628,7 +364,7 @@ print(SomeClass.computedTypeProperty)
下图展示了如何把两个声道结合来模拟立体声的音量。当声道的音量是 `0`,没有一个灯会亮;当声道的音量是 `10`,所有灯点亮。本图中,左声道的音量是 `9`,右声道的音量是 `7`
<img src="https://docs.swift.org/swift-book/images/staticPropertiesVUMeter@2x.png" alt="Static Properties VUMeter" width="243" height="357" />
<img src="https://docs.swift.org/swift-book/_images/staticPropertiesVUMeter_2x.png" alt="Static Properties VUMeter" width="243" height="357" />
上面所描述的声道模型使用 `AudioChannel` 结构体的实例来表示:

View File

@ -47,7 +47,7 @@ counter.reset()
// 计数值现在是0
```
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见 [指定外部参数名](./06_Functions.md#specifying-external-parameter-names)。方法参数也一样,因为方法就是函数,只是这个函数与某个类型相关联了。
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见 [指定外部参数名](./06_Functions.md#specifying_external_parameter_names)。方法参数也一样,因为方法就是函数,只是这个函数与某个类型相关联了。
### self 属性 {#the-self-property}
@ -105,9 +105,9 @@ print("The point is now at (\(somePoint.x), \(somePoint.y))")
// 打印“The point is now at (3.0, 4.0)”
```
上面的 `Point` 结构体定义了一个可变方法 `moveBy(x:y:)` 来移动 `Point` 实例到给定的位置。该方法被调用时修改了这个点,而不是返回一个新的点。方法定义时加上了 `mutating` 关键字,从而允许修改属性。
上面的 `Point` 结构体定义了一个可变方法 `moveByxy :)` 来移动 `Point` 实例到给定的位置。该方法被调用时修改了这个点,而不是返回一个新的点。方法定义时加上了 `mutating` 关键字,从而允许修改属性。
注意不能在结构体类型的常量a constant of structure type上调用可变方法因为其属性不能被改变即使属性是变量属性详情参见 [常量结构体的存储属性](./10_Properties.md#stored-properties-of-constant-structure-instances)
注意不能在结构体类型的常量a constant of structure type上调用可变方法因为其属性不能被改变即使属性是变量属性详情参见 [常量结构体的存储属性](./10_Properties.md#stored_properties_of_constant_structure_instances)
```swift
let fixedPoint = Point(x: 3.0, y: 3.0)
@ -213,7 +213,7 @@ struct LevelTracker {
除了类型属性和类型方法,`LevelTracker` 还监测每个玩家的进度。它用实例属性 `currentLevel` 来监测每个玩家当前的等级。
为了便于管理 `currentLevel` 属性,`LevelTracker` 定义了实例方法 `advance(to:)`。这个方法会在更新 `currentLevel` 之前检查所请求的新等级是否已经解锁。`advance(to:)` 方法返回布尔值以指示是否能够设置 `currentLevel`。因为允许在调用 `advance(to:)` 时候忽略返回值,不会产生编译警告,所以函数被标注为 `@discardableResult` 属性,更多关于属性信息,请参考 [特性](../03_language_reference/07_Attributes.md) 章节。
为了便于管理 `currentLevel` 属性,`LevelTracker` 定义了实例方法 `advance(to:)`。这个方法会在更新 `currentLevel` 之前检查所请求的新等级是否已经解锁。`advance(to:)` 方法返回布尔值以指示是否能够设置 `currentLevel`。因为允许在调用 `advance(to:)` 时候忽略返回值,不会产生编译警告,所以函数被标注为 `@discardableResult` 属性,更多关于属性信息,请参考 [特性](../chapter3/07_Attributes.html)章节。
下面,`Player` 类使用 `LevelTracker` 来监测和更新每个玩家的发展进度:

View File

@ -2,11 +2,11 @@
*下标*可以定义在类、结构体和枚举中,是访问集合、列表或序列中元素的快捷方式。可以使用下标的索引,设置和获取值,而不需要再调用对应的存取方法。举例来说,用下标访问一个 `Array` 实例中的元素可以写作 `someArray[index]`,访问 `Dictionary` 实例中的元素可以写作 `someDictionary[key]`
一个类型可以定义多个下标,通过不同索引类型进行对应的重载。下标不限于一维,你可以定义具有多个入参的下标满足自定义类型的需求。
一个类型可以定义多个下标,通过不同索引类型进行重载。下标不限于一维,你可以定义具有多个入参的下标满足自定义类型的需求。
## 下标语法 {#subscript-syntax}
下标允许你通过在实例名称后面的方括号中传入一个或者多个索引值来对实例进行查询。它的语法类似于实例方法语法和计算型属性语法定义下标使用 `subscript` 关键字,与定义实例方法类似,都是指定一个或多个输入参数和一个返回类型与实例方法不同的是,下标可以设定为读写或只读。这种行为由 getter 和 setter 实现,类似计算型属性:
下标允许你通过在实例名称后面的方括号中传入一个或者多个索引值来对实例进行存取。语法类似于实例方法语法和计算型属性语法的混合。与定义实例方法类似,定义下标使用 `subscript` 关键字,指定一个或多个输入参数和返回类型与实例方法不同的是,下标可以设定为读写或只读。这种行为由 getter 和 setter 实现,有点类似计算型属性:
```swift
subscript(index: Int) -> Int {
@ -19,9 +19,9 @@ subscript(index: Int) -> Int {
}
```
`newValue` 的类型和下标操作的返回类型相同。如同计算型属性,可以不指定 setter 的参数(`newValue`。如果不指定参数setter 会提供一个名为 `newValue` 的默认参数。
`newValue` 的类型和下标的返回类型相同。如同计算型属性,可以不指定 setter 的参数(`newValue`。如果不指定参数setter 会提供一个名为 `newValue` 的默认参数。
如同只读计算型属性,对于只读下标的声明,你可以通过省略 `get` 关键字和对应的大括号组来进行简写
如同只读计算型属性,可以省略只读下标的 `get` 关键字:
```swift
subscript(index: Int) -> Int {
@ -29,7 +29,7 @@ subscript(index: Int) -> Int {
}
```
下面代码演示了只读下标的实现,这里定义了一个 `TimesTable` 结构体,用来表示对应整数的乘法表:
下面代码演示了只读下标的实现,这里定义了一个 `TimesTable` 结构体,用来表示传入整数的乘法表:
```swift
struct TimesTable {
@ -45,7 +45,7 @@ print("six times three is \(threeTimesTable[6])")
在上例中,创建了一个 `TimesTable` 实例,用来表示整数 `3` 的乘法表。数值 `3` 被传递给结构体的构造函数,作为实例成员 `multiplier` 的值。
你可以通过下标访问 `threeTimesTable` 实例,例如上面演示的 `threeTimesTable[6]`。这条语句查询了乘法表中 `3` 的第六个元素,返回 `3``6` 倍即 `18`
你可以通过下标访问 `threeTimesTable` 实例,例如上面演示的 `threeTimesTable[6]`。这条语句查询了 `3` 的乘法表中的第六个元素,返回 `3``6` 倍即 `18`
> 注意
>
@ -53,9 +53,9 @@ print("six times three is \(threeTimesTable[6])")
## 下标用法 {#subscript-usage}
下标的确切含义取决于使用场景。下标通常作为访问集合,列表或序列中元素的快捷方式。你可以针对自己特定的类或结构体功能来以最恰当的方式实现下标。
下标的确切含义取决于使用场景。下标通常作为访问集合,列表或序列中元素的快捷方式。你可以针对自己特定的类或结构体功能来自由地以最恰当的方式实现下标。
例如Swift 的 `Dictionary` 类型实现下标用于对实例中储存的值进行存取操作。为字典设值时,在下标中使用和字典的键类型相同的键,并把一个和字典的值类型相同的值赋给这个下标:
例如Swift 的 `Dictionary` 类型实现下标用于对实例中储存的值进行存取操作。为字典设值时,在下标中使用和字典的键类型相同的键,并把一个和字典的值类型相同的值赋给这个下标:
```swift
var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
@ -64,19 +64,17 @@ numberOfLegs["bird"] = 2
上例定义一个名为 `numberOfLegs` 的变量,并用一个包含三对键值的字典字面量初始化它。`numberOfLegs` 字典的类型被推断为 `[String: Int]`。字典创建完成后,该例子通过下标将 `String` 类型的键 `bird``Int` 类型的值 `2` 添加到字典中。
更多关于 `Dictionary` 下标的信息请参考 [读取和修改字典](./04_Collection_Types.md#accessing-and-modifying-a-dictionary)。
更多关于 `Dictionary` 下标的信息请参考 [读取和修改字典](./04_Collection_Types.md#accessing_and_modifying_a_dictionary)。
> 注意
>
> Swift 的 `Dictionary` 类型的下标接受并返回_可选_类型的值。上例中的 `numberOfLegs` 字典通过下标返回的是一个 `Int?` 或者说“可选的 int”。`Dictionary` 类型之所以如此实现下标,是因为不是每个键都有对应的值,同时这也提供了一种通过键删除对应值的方式,只需将键对应的值赋值为 `nil` 即可。
> Swift 的 `Dictionary` 类型的下标接受并返回可选类型的值。上例中的 `numberOfLegs` 字典通过下标返回的是一个 `Int?` 或者说“可选的 int”。`Dictionary` 类型之所以如此实现下标,是因为不是每个键都有对应的值,同时这也提供了一种通过键删除对应值的方式,只需将键对应的值赋值为 `nil` 即可。
## 下标选项 {#subscript-options}
下标可以接受任意数量的入参,并且这些入参可以是任类型。下标的返回值也可以是任意类型。
下标可以接受任意数量的入参,并且这些入参可以是任类型。下标的返回值也可以是任意类型。下标可以使用可变参数,并且可以提供默认参数数值,但是不能使用输入输出参数。
与函数一样,下标可以接受不同数量的参数,并且为这些参数提供默认值,如在[可变参数](./06_Functions.md#variadic-parameters) 和 [默认参数值](./06_Functions.md#default-parameter-values) 中所述。但是,与函数不同的是,下标不能使用 in-out 参数
一个类或结构体可以根据自身需要提供多个下标实现,使用下标时将通过入参的数量和类型进行区分,自动匹配合适的下标。它通常被称为*下标的重载*。
一个类或结构体可以根据自身需要提供多个下标实现,使用下标时将通过入参的数量和类型进行区分,自动匹配合适的下标,这就是*下标的重载*
虽然接受单一入参的下标是最常见的,但也可以根据情况定义接受多个入参的下标。例如下例定义了一个 `Matrix` 结构体,用于表示一个 `Double` 类型的二维矩阵。`Matrix` 结构体的下标接受两个整型参数:
@ -105,17 +103,17 @@ struct Matrix {
}
```
`Matrix` 提供了一个接受两个入参的构造方法,入参分别是 `rows``columns`,创建了一个足够容纳 `rows * columns``Double` 类型的值的数组。通过传入数组长度和初始值 `0.0` 到数组的构造器,将矩阵中每个位置的值初始化为 `0.0`。关于数组的这种构造方法请参考 [创建一个带有默认值的数组](./04_Collection_Types.md#creating-an-array-with-a-default-value)。
`Matrix` 提供了一个接受两个入参的构造方法,入参分别是 `rows``columns`,创建了一个足够容纳 `rows * columns``Double` 类型的值的数组。通过传入数组长度和初始值 `0.0` 到数组的构造器,将矩阵中每个位置的值初始化为 `0.0`。关于数组的这种构造方法请参考 [创建一个带有默认值的数组](./04_Collection_Types.md#creating_an_array_with_a_default_value)。
你可以通过传入合适的 `row``column` 数值来构造一个新的 `Matrix` 实例:
你可以通过传入合适的 `row``column` 的数量来构造一个新的 `Matrix` 实例:
```swift
var matrix = Matrix(rows: 2, columns: 2)
```
上例中创建了一个两行两列的 `Matrix` 实例。该 `Matrix` 实例的 `grid` 数组按照从左上到右下的阅读顺序将矩阵扁平化存储:
上例中创建了一个 `Matrix` 实例来表示两行两列的矩阵。该 `Matrix` 实例的 `grid` 数组按照从左上到右下的阅读顺序将矩阵扁平化存储:
![](https://docs.swift.org/swift-book/images/subscriptMatrix01@2x.png)
![](https://docs.swift.org/swift-book/_images/subscriptMatrix01_2x.png)
`row``column` 的值传入下标来为矩阵设值,下标的入参使用逗号分隔:
@ -126,7 +124,7 @@ matrix[1, 0] = 3.2
上面两条语句分别调用下标的 setter 将矩阵右上角位置(即 `row``0``column``1` 的位置)的值设置为 `1.5`,将矩阵左下角位置(即 `row``1``column``0` 的位置)的值设置为 `3.2`
![](https://docs.swift.org/swift-book/images/subscriptMatrix02@2x.png)
![](https://docs.swift.org/swift-book/_images/subscriptMatrix02_2x.png)
`Matrix` 下标的 getter 和 setter 中都含有断言,用来检查下标入参 `row``column` 的值是否有效。为了方便进行断言,`Matrix` 包含了一个名为 `indexIsValid(row:column:)` 的便利方法,用来检查入参 `row``column` 的值是否在矩阵范围内:
@ -141,18 +139,18 @@ func indexIsValid(row: Int, column: Int) -> Bool {
```swift
let someValue = matrix[2, 2]
// 断言将会触发,因为 [2, 2] 已经超过了 matrix 的范围
```
## 类型下标 {#type-subscripts}
正如上节所述,实例下标是在特定类型的一个实例上调用的下标。你也可以定义一种在这个类型身上调用的下标。这种下标被称作_类型下标_。你可以通过在 `subscript` 关键字之前写下 `static` 关键字的方式来表示一个类型下标。类可以使用 `class` 关键字来代替 `static`,它允许子类重写父类中对那个下标的实现。下面的例子展示了如何定义和调用一个类型下标:
```
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
static subscript(n: Int) -> Planet {
return Planet(rawValue: n)!
}
}
let mars = Planet[4]
print(mars)
```
```
## 类型下标{#type-subscripts}
正如上节所述,实例下标是在特定类型的一个实例上调用的下标。你也可以定义一种在这个类型身上调用的下标。这种下标的类型被称作类型下标。你可以通过在 `subscript` 关键字之前写下 `static` 关键字的方式来表示一个类型下标。类可以使用 `class` 关键字来允许子类重写父类中对那个下标的实现。下面的例子展示了如何定义和调用一个类型下标:
```
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
static subscript(n: Int) -> Planet {
return Planet(rawValue: n)!
}
}
let mars = Planet[4]
print(mars)
```

View File

@ -57,7 +57,7 @@ class SomeClass: SomeSuperclass {
}
```
下一个例子,定义了一个叫 `Bicycle` 的子类,继承自`Vehicle`
下一个例子,定义了一个叫 `Bicycle` 的子类,继承自`Vehicle`
```swift
class Bicycle: Vehicle {
@ -69,7 +69,7 @@ class Bicycle: Vehicle {
除了所继承的特性,`Bicycle` 类还定义了一个默认值为 `false` 的存储型属性 `hasBasket`(属性推断为 `Bool`)。
默认情况下,你创建的所有新的 `Bicycle` 实例不会有一个篮子(即 `hasBasket` 属性默认为 `false`)。创建该实例之后,你可以为 `Bicycle` 实例设置 `hasBasket` 属性为 `true`
默认情况下,你创建的所有新的 `Bicycle` 实例不会有一个篮子(即 `hasBasket` 属性默认为 `false`)。创建该实例之后,你可以为 `Bicycle` 实例设置 `hasBasket` 属性为 `ture`
```swift
let bicycle = Bicycle()
@ -111,7 +111,7 @@ print("Tandem: \(tandem.description)")
如果要重写某个特性,你需要在重写定义的前面加上 `override` 关键字。这么做,就表明了你是想提供一个重写版本,而非错误地提供了一个相同的定义。意外的重写行为可能会导致不可预知的错误,任何缺少 `override` 关键字的重写都会在编译时被认定为错误。
`override` 关键字会提醒 Swift 编译器去检查该类的超类(或其中一个类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。
`override` 关键字会提醒 Swift 编译器去检查该类的超类(或其中一个类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。
### 访问超类的方法,属性及下标 {#accessing-superclass-methods-properties-and-subscripts}
@ -184,7 +184,7 @@ print("Car: \(car.description)")
#### 重写属性观察器 {#overriding-property-observers}
你可以通过重写属性为一个继承来的属性添加属性观察器。这样一来,无论被继承属性原本是如何实现的,当其属性值发生改变时,你就会被通知到。关于属性观察器的更多内容,请看 [属性观察器](../02_language_guide/10_Properties.md#property-observers)。
你可以通过重写属性为一个继承来的属性添加属性观察器。这样一来,无论被继承属性原本是如何实现的,当其属性值发生改变时,你就会被通知到。关于属性观察器的更多内容,请看 [属性观察器](../chapter2/10_Properties.html#property_observers)。
> 注意
>
@ -216,6 +216,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 。这样的类是不可被继承的,试图继承这样的类会导致编译报错。

View File

@ -4,7 +4,7 @@
你要通过定义*构造器*来实现构造过程,它就像用来创建特定类型新实例的特殊方法。与 Objective-C 中的构造器不同Swift 的构造器没有返回值。它们的主要任务是保证某种类型的新实例在第一次使用前完成正确的初始化。
类的实例也可以通过实现*析构器*来执行它释放之前自定义的清理工作。想了解更多关于析构器的内容,请参考 [析构过程](./15_Deinitialization.md)。
类的实例也可以通过实现*析构器*来执行它释放之前自定义的清理工作。想了解更多关于析构器的内容,请参 考 [析构过程](./15_Deinitialization.md)。
## 存储属性的初始赋值 {#setting-initial-values-for-stored-properties}
@ -14,7 +14,7 @@
> 注意
>
> 当你为存储型属性分配默认值或者在构造器中设置初始值时,它们的值是被直接设置的,不会触发任何属性观察者。
> 当你为存储型属性分配默认值或者在构造器中设置初始值时,它们的值是被直接设置的,不会触发任何属性观察者。
### 构造器 {#initializers}
@ -131,7 +131,7 @@ let veryGreen = Color(0.0, 1.0, 0.0)
如果你不希望构造器的某个形参使用实参标签,可以使用下划线(`_`)来代替显式的实参标签来重写默认行为。
下面是之前 [形参的构造过程](#initialization-parameters) 中 `Celsius` 例子的扩展,多了一个用已经的摄氏表示的 `Double` 类型值来创建新的 `Celsius` 实例的额外构造器:
下面是之前 [形参的构造过程](#initialization_parameters) 中 `Celsius` 例子的扩展,多了一个用已经的摄氏表示的 `Double` 类型值来创建新的 `Celsius` 实例的额外构造器:
```swift
struct Celsius {
@ -225,7 +225,7 @@ var item = ShoppingListItem()
### 结构体的逐一成员构造器 {#memberwise-initializers-for-structure-types}
结构体如果没有定义任何自定义构造器,它们将自动获得一个*逐一成员构造器memberwise initializer*。不像默认构造器,即使存储型属性没有默认值,结构体也会获得逐一成员构造器。
结构体如果没有定义任何自定义构造器,它们将自动获得一个*逐一成员构造器memberwise initializer*。不像默认构造器,即使存储型属性没有默认值,结构体也会获得逐一成员构造器。
逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法。新实例的属性初始值可以通过名字传入逐一成员构造器中。
@ -258,7 +258,7 @@ print(zeroByZero.width, zeroByZero.height)
构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为*构造器代理*,它能避免多个构造器间的代码重复。
构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给自己的其它构造器。类则不同,它可以继承自其它类(请参考 [继承](./13_Inheritance.md))。这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节 [类的继承和构造过程](#class-inheritance-and-initialization) 中介绍。
构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给自己的其它构造器。类则不同,它可以继承自其它类(请参考 [继承](./13_Inheritance.md))。这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节 [类的继承和构造过程](#class_inheritance_and_initialization) 中介绍。
对于值类型,你可以使用 `self.init` 在自定义的构造器中引用相同类型中的其它构造器。并且你只能在构造器内部调用 `self.init`
@ -301,7 +301,7 @@ struct Rect {
}
```
第一个 `Rect` 构造器 `init()`,在功能上跟没有自定义构造器时自动获得的默认构造器是一样的。这个构造器函数体是空的,使用一对大括号 `{}` 来表示。调用这个构造器将返回一个 `Rect` 实例,它的 `origin``size` 属性都使用定义时的默认值 `Point(x: 0.0, y: 0.0)``Size(width: 0.0, height: 0.0)`
第一个 `Rect` 构造器 `init()`,在功能上跟没有自定义构造器时自动获得的默认构造器是一样的。这个构造器函数体是空的,使用一对大括号 `{}` 来表示。调用这个构造器将返回一个 `Rect` 实例,它的 `origin``size` 属性都使用定义时的默认值 `Point(x: 0.0, y: 0.0)``Size(width: 0.0, height: 0.0)`
```swift
let basicRect = Rect()
@ -328,7 +328,7 @@ let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
> 注意
>
> 如果你想用另外一种不需要自己定义 `init()``init(origin:size:)` 的方式来实现这个例子,请参考 [扩展](./20_Extensions.md)。
> 如果你想用另外一种不需要自己定义 `init()``init(origin:size:)` 的方式来实现这个例子,请参考 [扩展](./21_Extensions.md)。
## 类的继承和构造过程 {#class-inheritance-and-initialization}
@ -342,7 +342,7 @@ Swift 为类类型提供了两种构造器来确保实例中所有存储型属
类倾向于拥有极少的指定构造器,普遍的是一个类只拥有一个指定构造器。指定构造器像一个个“漏斗”放在构造过程发生的地方,让构造过程沿着父类链继续往上进行。
每一个类都必须至少拥有一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。具体内容请参考后续章节 [构造器的自动继承](#automatic-initializer-inheritance)。
每一个类都必须至少拥有一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。具体内容请参考后续章节 [构造器的自动继承](#automatic_initializer_inheritance)。
*便利构造器*是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为部分形参提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入值的实例。
@ -389,7 +389,7 @@ convenience init(parameters) {
这些规则可以通过下面图例来说明:
![构造器代理图](https://docs.swift.org/swift-book/images/initializerDelegation01@2x.png)
![构造器代理图](https://docs.swift.org/swift-book/_images/initializerDelegation01_2x.png)
如图所示,父类中包含一个指定构造器和两个便利构造器。其中一个便利构造器调用了另外一个便利构造器,而后者又调用了唯一的指定构造器。这满足了上面提到的规则 2 和 3。这个父类没有自己的父类所以规则 1 没有用到。
@ -401,7 +401,7 @@ convenience init(parameters) {
下面图例中展示了一种涉及四个类的更复杂的类层级结构。它演示了指定构造器是如何在类层级中充当“漏斗”的作用,在类的构造器链上简化了类之间的相互关系。
![复杂构造器代理图](https://docs.swift.org/swift-book/images/initializerDelegation02@2x.png)
![复杂构造器代理图](https://docs.swift.org/swift-book/_images/initializerDelegation02_2x.png)
### 两段式构造过程 {#two-phase-initialization}
@ -417,7 +417,7 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
##### 安全检查 1
&nbsp;&nbsp;&nbsp;&nbsp;指定构造器必须保证它所在类的所有属性都初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。
&nbsp;&nbsp;&nbsp;&nbsp;指定构造器必须保证它所在类的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。
如上所述,一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足这一规则,指定构造器必须保证它所在类的属性在它往上代理之前先完成初始化。
@ -453,7 +453,7 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
下图展示了在假定的子类和父类之间的构造阶段 1
![构建过程阶段1](https://docs.swift.org/swift-book/images/twoPhaseInitialization01@2x.png)
![构建过程阶段1](https://docs.swift.org/swift-book/_images/twoPhaseInitialization01_2x.png)
在这个例子中,构造过程从对子类中一个便利构造器的调用开始。这个便利构造器此时还不能修改任何属性,它会代理到该类中的指定构造器。
@ -465,7 +465,7 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
以下展示了相同构造过程的阶段 2
![构建过程阶段2](https://docs.swift.org/swift-book/images/twoPhaseInitialization02@2x.png)
![构建过程阶段2](https://docs.swift.org/swift-book/_images/twoPhaseInitialization02_2x.png)
父类中的指定构造器现在有机会进一步自定义实例(尽管这不是必须的)。
@ -475,23 +475,23 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
### 构造器的继承和重写 {#initializer-inheritance-and-overriding}
跟 Objective-C 中的子类不同Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更精细的子类继承,而在用来创建子类的新实例时没有完全或错误被初始化。
跟 Objective-C 中的子类不同Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更精细的子类继承,而在用来创建子类的新实例时没有完全或错误被初始化。
> 注意
>
> 父类的构造器仅会在安全和适当的某些情况下被继承。具体内容请参考后续章节 [构造器的自动继承](#automatic-initializer-inheritance)。
> 父类的构造器仅会在安全和适当的某些情况下被继承。具体内容请参考后续章节 [构造器的自动继承](#automatic_initializer_inheritance)。
假如你希望自定义的子类中能提供一个或多个跟父类相同的构造器,你可以在子类中提供这些构造器的自定义实现。
当你在编写一个和父类中指定构造器相匹配的子类构造器时,你实际上是在重写父类的这个指定构造器。因此,你必须在定义子类构造器时带上 `override` 修饰符。即使你重写的是系统自动提供的默认构造器,也需要带上 `override` 修饰符,具体内容请参考 [默认构造器](#default-initializers)。
当你在编写一个和父类中指定构造器相匹配的子类构造器时,你实际上是在重写父类的这个指定构造器。因此,你必须在定义子类构造器时带上 `override` 修饰符。即使你重写的是系统自动提供的默认构造器,也需要带上 `override` 修饰符,具体内容请参考 [默认构造器](#default_initializers)。
正如重写属性,方法或者是下标,`override` 修饰符会让编译器去检查父类中是否有相匹配的指定构造器,并验证构造器参数是否按预想中被指定。
正如重写属性,方法或者是下标,`override` 修饰符会让编译器去检查父类中是否有相匹配的指定构造器,并验证构造器参数是否按预想中被指定。
> 注意
>
> 当你重写一个父类的指定构造器时,你总是需要写 `override` 修饰符,即使是为了实现子类的便利构造器。
相反,如果你编写了一个和父类便利构造器相匹配的子类构造器,由于子类不能直接调用父类的便利构造器(每个规则都在上文 [类的构造器代理规则](#initializer-delegation-for-class-types) 有所描述),因此,严格意义上来讲,你的子类并未对一个父类构造器提供重写。最后的结果就是,你在子类中“重写”一个父类便利构造器时,不需要加 `override` 修饰符。
相反,如果你编写了一个和父类便利构造器相匹配的子类构造器,由于子类不能直接调用父类的便利构造器(每个规则都在上文 [类的构造器代理规则](#initializer_delegation_for_class_types) 有所描述),因此,严格意义上来讲,你的子类并未对一个父类构造器提供重写。最后的结果就是,你在子类中“重写”一个父类便利构造器时,不需要加 `override` 修饰符。
在下面的例子中定义了一个叫 `Vehicle` 的基类。基类中声明了一个存储型属性 `numberOfWheels`,它是默认值为 `Int` 类型的 `0``numberOfWheels` 属性用在一个描述车辆特征 `String` 类型为 `descrpiption` 的计算型属性中:
@ -504,7 +504,7 @@ class Vehicle {
}
```
`Vehicle` 类只为存储型属性提供默认值,也没有提供自定义构造器。因此,它会自动获得一个默认构造器,具体内容请参考 [默认构造器](#default-initializers)。默认构造器(如果有的话)总是类中的指定构造器,可以用于创建 `numberOfWheels``0``Vehicle` 实例:
`Vehicle` 类只为存储型属性提供默认值,也没有提供自定义构造器。因此,它会自动获得一个默认构造器,具体内容请参考 [默认构造器](#default_initializers)。默认构造器(如果有的话)总是类中的指定构造器,可以用于创建 `numberOfWheels``0``Vehicle` 实例:
```swift
let vehicle = Vehicle()
@ -535,9 +535,9 @@ print("Bicycle: \(bicycle.description)")
// 打印“Bicycle: 2 wheel(s)”
```
如果子类的构造器没有在阶段 2 过程中做自定义操作,并且父类有一个同步、无参数的指定构造器,你可以在所有子类的存储属性赋值之后省略 `super.init()` 的调用。若父类有一个异步的构造器,你就需要明确地写入 `await super.init()`
如果子类的构造器没有在阶段 2 过程中做自定义操作,并且父类有一个无参数的指定构造器,你可以在所有子类的存储属性赋值之后省略 `super.init()` 的调用。
这个例子定义了另一个 `Vehicle` 的子类 `Hoverboard` ,只设置它的 `color` 属性。这个构造器依赖隐式调用父类的构造器来完成,而不是显调用 `super.init()`
这个例子定义了另一个 `Vehicle` 的子类 `Hoverboard` ,只设置它的 `color` 属性。这个构造器依赖隐式调用父类的构造器来完成,而不是显调用 `super.init()`
```swift
class Hoverboard: Vehicle {
@ -605,7 +605,7 @@ class Food {
下图中展示了 `Food` 的构造器链:
![Food 构造器链](https://docs.swift.org/swift-book/images/initializersExample01@2x.png)
![Food 构造器链](https://docs.swift.org/swift-book/_images/initializersExample01_2x.png)
类类型没有默认的逐一成员构造器,所以 `Food` 类提供了一个接受单一参数 `name` 的指定构造器。这个构造器可以使用一个特定的名字来创建新的 `Food` 实例:
@ -640,13 +640,13 @@ class RecipeIngredient: Food {
下图中展示了 `RecipeIngredient` 类的构造器链:
![RecipeIngredient 构造器](https://docs.swift.org/swift-book/images/initializersExample02@2x.png)
![RecipeIngredient 构造器](https://docs.swift.org/swift-book/_images/initializersExample02_2x.png)
`RecipeIngredient` 类拥有一个指定构造器 `init(name: String, quantity: Int)`,它可以用来填充 `RecipeIngredient` 实例的所有属性值。这个构造器一开始先将传入的 `quantity` 实参赋值给 `quantity` 属性,这个属性也是唯一在 `RecipeIngredient` 中新引入的属性。随后,构造器向上代理到父类 `Food``init(name: String)`。这个过程满足 [两段式构造过程](#two-phase-initialization) 中的安全检查 1。
`RecipeIngredient` 类拥有一个指定构造器 `init(name: String, quantity: Int)`,它可以用来填充 `RecipeIngredient` 实例的所有属性值。这个构造器一开始先将传入的 `quantity` 实参赋值给 `quantity` 属性,这个属性也是唯一在 `RecipeIngredient` 中新引入的属性。随后,构造器向上代理到父类 `Food``init(name: String)`。这个过程满足 [两段式构造过程](#two_phase_initialization) 中的安全检查 1。
`RecipeIngredient` 也定义了一个便利构造器 `init(name: String)`,它只通过 `name` 来创建 `RecipeIngredient` 的实例。这个便利构造器假设任意 `RecipeIngredient` 实例的 `quantity``1`,所以不需要显式的质量即可创建出实例。这个便利构造器的定义可以更加方便和快捷地创建实例,并且避免了创建多个 `quantity``1``RecipeIngredient` 实例时的代码重复。这个便利构造器只是简单地横向代理到类中的指定构造器,并为 `quantity` 参数传递 `1`
`RecipeIngredient` 的便利构造器 `init(name: String)` 使用了跟 `Food` 中指定构造器 `init(name: String)` 相同的形参。由于这个便利构造器重写了父类的指定构造器 `init(name: String)`,因此必须在前面使用 `override` 修饰符(参见 [构造器的继承和重写](#initializer-inheritance-and-overriding))。
`RecipeIngredient` 的便利构造器 `init(name: String)` 使用了跟 `Food` 中指定构造器 `init(name: String)` 相同的形参。由于这个便利构造器重写了父类的指定构造器 `init(name: String)`,因此必须在前面使用 `override` 修饰符(参见 [构造器的继承和重写](#initializer_inheritance_and_overriding))。
尽管 `RecipeIngredient` 将父类的指定构造器重写为了便利构造器,但是它依然提供了父类的所有指定构造器的实现。因此,`RecipeIngredient` 会自动继承父类的所有便利构造器。
@ -683,7 +683,7 @@ class ShoppingListItem: RecipeIngredient {
下图展示了这三个类的构造器链:
![三类构造器图](https://docs.swift.org/swift-book/images/initializersExample03@2x.png)
![三类构造器图](https://docs.swift.org/swift-book/_images/initializersExample03_2x.png)
你可以使用三个继承来的构造器来创建 `ShoppingListItem` 的新实例:
@ -1027,7 +1027,7 @@ class SomeClass {
下面例子中定义了一个结构体 `Chessboard`,它构建了西洋跳棋游戏的棋盘,西洋跳棋游戏在一副黑白格交替的 8 x 8 的棋盘中进行的:
![西洋跳棋棋盘](https://docs.swift.org/swift-book/images/chessBoard@2x.png)
![西洋跳棋棋盘](https://docs.swift.org/swift-book/_images/chessBoard_2x.png)
为了呈现这副游戏棋盘,`Chessboard` 结构体定义了一个属性 `boardColors`,它是一个包含 `64``Bool` 值的数组。在数组中,值为 `true` 的元素表示一个黑格,值为 `false` 的元素表示一个白格。数组中第一个元素代表棋盘上左上角的格子,最后一个元素代表棋盘上右下角的格子。
@ -1036,7 +1036,7 @@ class SomeClass {
```swift
struct Chessboard {
let boardColors: [Bool] = {
var temporaryBoard: [Bool] = []
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...8 {
for j in 1...8 {

View File

@ -4,7 +4,7 @@
## 析构过程原理 {#how-deinitialization-works}
Swift 会自动释放不再需要的实例以释放资源。如 [自动引用计数](./24_Automatic_Reference_Counting.md) 章节中所讲述Swift 通过*自动引用计数ARC)* 处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件。
Swift 会自动释放不再需要的实例以释放资源。如 [自动引用计数](./23_Automatic_Reference_Counting.md) 章节中所讲述Swift 通过*自动引用计数ARC)* 处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件。
在类的定义中,每个类最多只能有一个析构器,而且析构器不带任何参数和圆括号,如下所示:
@ -16,7 +16,7 @@ deinit {
析构器是在实例释放发生前被自动调用的。你不能主动调用析构器。子类继承了父类的析构器,并且在子类析构器实现的最后,父类的析构器会被自动调用。即使子类没有提供自己的析构器,父类的析构器也同样会被调用。
因为直到实例的析构器被调用后,实例才会被释放,所以析构器可以访问实例的所有属性,并且可以根据那些属性修改它的行为(比如查找一个需要被关闭的文件)。
因为直到实例的析构器被调用后,实例才会被释放,所以析构器可以访问实例的所有属性,并且可以根据那些属性可以修改它的行为(比如查找一个需要被关闭的文件)。
## 析构器实践 {#deinitializers-in-action}

View File

@ -6,15 +6,15 @@
>
> Swift 的可选链式调用和 Objective-C 中向 `nil` 发送消息有些相像,但是 Swift 的可选链式调用可以应用于任意类型,并且能检查调用是否成功。
## 使用可选链式调用代替强制解包 {#optional-chaining-as-an-alternative-to-forced-unwrapping}
## 使用可选链式调用代替强制展开 {#optional-chaining-as-an-alternative-to-forced-unwrapping}
通过在想调用的属性、方法,或下标的可选值后面放一个问号(`?`),可以定义一个可选链。这一点很像在可选值后面放一个叹号(`!`)来强制解包它的值。它们的主要区别在于当可选值为空时可选链式调用只会调用失败,然而强制解包将会触发运行时错误。
通过在想调用的属性、方法,或下标的可选值后面放一个问号(`?`),可以定义一个可选链。这一点很像在可选值后面放一个叹号(`!`)来强制展开它的值。它们的主要区别在于当可选值为空时可选链式调用只会调用失败,然而强制展开将会触发运行时错误。
为了反映可选链式调用可以在空值(`nil`)上调用的事实,不论这个调用的属性、方法及下标返回的值是不是可选值,它的返回结果都是一个可选值。你可以利用这个返回值来判断你的可选链式调用是否调用成功,如果调用有返回值则说明调用成功,返回 `nil` 则说明调用失败。
这里需要特别指出,可选链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了一个可选值。例如,使用可选链式调用访问属性,当可选链式调用成功时,如果属性原本的返回结果是 `Int` 类型,则会变为 `Int?` 类型。
下面几段代码将解释可选链式调用和强制解包的不同。
下面几段代码将解释可选链式调用和强制展开的不同。
首先定义两个类 `Person``Residence`
@ -36,7 +36,7 @@ class Residence {
let john = Person()
```
如果使用叹号(`!`)强制解包获得这个 `john``residence` 属性中的 `numberOfRooms` 值,会触发运行时错误,因为这时 `residence` 没有可以解包的值:
如果使用叹号(`!`)强制展开获得这个 `john``residence` 属性中的 `numberOfRooms` 值,会触发运行时错误,因为这时 `residence` 没有可以展开的值:
```swift
let roomCount = john.residence!.numberOfRooms
@ -58,7 +58,7 @@ if let roomCount = john.residence?.numberOfRooms {
`residence` 后面添加问号之后Swift 就会在 `residence` 不为 `nil` 的情况下访问 `numberOfRooms`
因为访问 `numberOfRooms` 有可能失败,可选链式调用会返回 `Int?` 类型,或称为“可选的 `Int`”。如上例所示,当 `residence``nil` 的时候,可选的 `Int` 将会为 `nil`,表明无法访问 `numberOfRooms`。访问成功时,可选的 `Int` 值会通过可选绑定解包,并赋值给非可选类型的 `roomCount` 常量。
因为访问 `numberOfRooms` 有可能失败,可选链式调用会返回 `Int?` 类型,或称为“可选的 `Int`”。如上例所示,当 `residence``nil` 的时候,可选的 `Int` 将会为 `nil`,表明无法访问 `numberOfRooms`。访问成功时,可选的 `Int` 值会通过可选绑定展开,并赋值给非可选类型的 `roomCount` 常量。
要注意的是,即使 `numberOfRooms` 是非可选的 `Int` 时,这一点也成立。只要使用可选链式调用就意味着 `numberOfRooms` 会返回一个 `Int?` 而不是 `Int`
@ -97,7 +97,7 @@ class Person {
```swift
class Residence {
var rooms: [Room] = []
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
@ -156,7 +156,7 @@ class Address {
## 通过可选链式调用访问属性 {#accessing-properties-through-optional-chaining}
正如 [使用可选链式调用代替强制解包](#optional-chaining-as-an-alternative-to-forced-unwrapping) 中所述,可以通过可选链式调用在一个可选值上访问它的属性,并判断访问是否成功。
正如 [使用可选链式调用代替强制展开](#optional_chaining_as_an_alternative_to_forced_unwrapping) 中所述,可以通过可选链式调用在一个可选值上访问它的属性,并判断访问是否成功。
使用前面定义过的类,创建一个 `Person` 实例,然后像之前一样,尝试访问 `numberOfRooms` 属性:
@ -212,7 +212,7 @@ func printNumberOfRooms() {
}
```
这个方法没有返回值。然而,没有返回值的方法具有隐式的返回类型 `Void`,如 [无返回值函数](./06_Functions.md#functions-without-return-values) 中所述。这意味着没有返回值的方法也会返回 `()`,或者说空的元组。
这个方法没有返回值。然而,没有返回值的方法具有隐式的返回类型 `Void`,如 [无返回值函数](./06_Functions.md#functions_without_return_values) 中所述。这意味着没有返回值的方法也会返回 `()`,或者说空的元组。
如果在可选值上通过可选链式调用来调用这个方法,该方法的返回类型会是 `Void?`,而不是 `Void`,因为通过可选链式调用得到的返回值都是可选的。这样我们就可以使用 `if` 语句来判断能否成功调用 `printNumberOfRooms()` 方法,即使方法本身没有定义返回值。通过判断返回值是否为 `nil` 可以判断调用是否成功:
@ -225,7 +225,7 @@ if john.residence?.printNumberOfRooms() != nil {
// 打印“It was not possible to print the number of rooms.”
```
同样的,可以据此判断通过可选链式调用为属性赋值是否成功。在上面的 [通过可选链式调用访问属性](#accessing-properties-through-optional-chaining) 的例子中,我们尝试给 `john.residence` 中的 `address` 属性赋值,即使 `residence``nil`。通过可选链式调用给属性赋值会返回 `Void?`,通过判断返回值是否为 `nil` 就可以知道赋值是否成功:
同样的,可以据此判断通过可选链式调用为属性赋值是否成功。在上面的 [通过可选链式调用访问属性](#accessing_properties_through_optional_chaining) 的例子中,我们尝试给 `john.residence` 中的 `address` 属性赋值,即使 `residence``nil`。通过可选链式调用给属性赋值会返回 `Void?`,通过判断返回值是否为 `nil` 就可以知道赋值是否成功:
```swift
if (john.residence?.address = someAddress) != nil {

View File

@ -142,14 +142,12 @@ do {
statements
} catch pattern 2 where condition {
statements
} catch pattern 3, pattern 4 where condition {
statements
} catch {
statements
}
```
`catch` 后面写一个匹配模式来表明这个子句能处理什么样的错误。如果一条 `catch` 子句没有指定匹配模式,那么这条子句可以匹配任何错误,并且把错误绑定到一个名字为 `error` 的局部常量。关于模式匹配的更多信息请参考 [模式](../03_language_reference/08_Patterns.md)。
`catch` 后面写一个匹配模式来表明这个子句能处理什么样的错误。如果一条 `catch` 子句没有指定匹配模式,那么这条子句可以匹配任何错误,并且把错误绑定到一个名字为 `error` 的局部常量。关于模式匹配的更多信息请参考 [模式](../chapter3/07_Patterns.html)。
举例来说,下面的代码处理了 `VendingMachineError` 枚举类型的全部三种情况:
@ -175,14 +173,14 @@ do {
`catch` 子句不必将 `do` 子句中的代码所抛出的每一个可能的错误都作处理。如果所有 `catch` 子句都未处理错误,错误就会传递到周围的作用域。然而,错误还是必须要被某个周围的作用域处理的。在不会抛出错误的函数中,必须用 `do-catch` 语句处理错误。而能够抛出错误的函数既可以使用 `do-catch` 语句处理,也可以让调用方来处理错误。如果错误传递到了顶层作用域却依然没有被处理,你会得到一个运行时错误。
以下面的代码为例,不是 `VendingMachineError`明的错误会在调用函数的地方被捕获:
以下面的代码为例,不是 `VendingMachineError`明的错误会在调用函数的地方被捕获:
```swift
func nourish(with item: String) throws {
do {
try vendingMachine.vend(itemNamed: item)
} catch is VendingMachineError {
print("Couldn't buy that from the vending machine.")
print("Invalid selection, out of stock, or not enough money.")
}
}
@ -191,28 +189,12 @@ do {
} catch {
print("Unexpected non-vending-machine-related error: \(error)")
}
// 打印“Couldn't buy that from the vending machine.”
// 打印“Invalid selection, out of stock, or not enough money.”
```
如果 `vend(itemNamed:)` 抛出的是一个 `VendingMachineError` 类型的错误,`nourish(with:)` 会打印一条消息,否则 `nourish(with:)` 会将错误抛给它的调用方。这个错误之后会被通用的 `catch` 语句捕获。
另一种捕获多个相关错误的方式是将它们放在 `catch` 后,通过逗号分隔。
例如:
```swift
func eat(item: String) throws {
do {
try vendingMachine.vend(itemNamed: item)
} catch VendingMachineError.invalidSelection, VendingMachineError.insufficientFunds, VendingMachineError.outOfStock {
print("Invalid selection, out of stock, or not enough money.")
}
}
```
`eat(item:)` 函数捕获了列出来的 `VendingMachine` 错误,且它的错误文本和列表的错误相关。如果列出来的三个错误中任意一个抛出,这个 `catch` 代码块就会打印信息。其他错误会传递到外面的作用域,包括以后可能添加的其他 `VendingMachine` 错误。
### 将错误转换成可选值 {#converting-errors-to-optional-values}
### 将错误转换成可选值 {#converting_errors_to_optional_values}
可以使用 `try?` 通过将错误转换成一个可选值来处理错误。如果是在计算 `try?` 表达式时抛出错误,该表达式的结果就为 `nil`。例如,在下面的代码中,`x``y` 有着相同的数值和等价的含义:
@ -243,7 +225,7 @@ func fetchData() -> Data? {
}
```
### 禁用错误传递 {#disabling-error-propagation}
### 禁用错误传递 {#disabling_error_propagation}
有时你知道某个 `throwing` 函数实际上在运行时是不会抛出错误的,在这种情况下,你可以在表达式前面写 `try!` 来禁用错误传递,这会把调用包装在一个不会有错误抛出的运行时断言中。如果真的抛出了错误,你会得到一个运行时错误。

View File

@ -4,7 +4,7 @@
类型转换在 Swift 中使用 `is``as` 操作符实现。这两个操作符分别提供了一种简单达意的方式去检查值的类型或者转换它的类型。
你也可以用它来检查一个类型是否遵循了某个协议,就像在 [检验协议遵循](./21_Protocols.md#checking-for-protocol-conformance) 部分讲述的一样。
你也可以用它来检查一个类型是否遵循了某个协议,就像在 [检验协议遵循](./21_Protocols.md#checking_for_protocol_conformance) 部分讲述的一样。
## 为类型转换定义类层次 {#defining-a-class-hierarchy-for-type-casting}
@ -141,7 +141,7 @@ Swift 为不确定类型提供了两种特殊的类型别名:
这里有个示例,使用 `Any` 类型来和混合的不同类型一起工作,包括函数类型和非类类型。它创建了一个可以存储 `Any` 类型的数组 `things`
```swift
var things: [Any] = []
var things = [Any]()
things.append(0)
things.append(0.0)

View File

@ -63,7 +63,7 @@ struct BlackjackCard {
`BlackjackCard` 结构体拥有两个属性——`rank``suit`。它也同样定义了一个计算型属性 `description``description` 属性用 `rank``suit` 中的内容来构建对扑克牌名字和数值的描述。该属性使用可选绑定来检查可选类型 `second` 是否有值,若有值,则在原有的描述中增加对 `second` 的描述。
因为 `BlackjackCard` 是一个没有自定义构造器的结构体,在 [结构体的逐一成员构造器](./14_Initialization.md#memberwise-initializers-for-structure-types) 中可知,结构体有默认的成员构造器,所以你可以用默认的构造器去初始化新常量 `theAceOfSpades`
因为 `BlackjackCard` 是一个没有自定义构造器的结构体,在 [结构体的逐一成员构造器](./14_Initialization.md#memberwise_initializers_for_structure_types) 中可知,结构体有默认的成员构造器,所以你可以用默认的构造器去初始化新常量 `theAceOfSpades`
```swift
let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades)

View File

@ -11,7 +11,7 @@ Swift 中的扩展可以:
- 定义和使用新的嵌套类型
- 使已经存在的类型遵循conform一个协议
在 Swift 中,你甚至可以扩展协议以提供其需要的实现,或者添加额外功能给遵循的类型所使用。你可以从 [协议扩展](./21_Protocols.md#protocol-extensions) 获取更多细节。
在 Swift 中,你甚至可以扩展协议以提供其需要的实现,或者添加额外功能给遵循的类型所使用。你可以从 [协议扩展](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID521) 获取更多细节。
> 注意
>
@ -35,9 +35,9 @@ extension SomeType: SomeProtocol, AnotherProtocol {
}
```
这种遵循协议的方式在 [在扩展里添加协议遵循](./21_Protocols.md#adding-protocol-conformance-with-an-extension) 中有描述。
这种遵循协议的方式在 [使用扩展遵循协议](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID277) 中有描述。
扩展可以使用在现有型类型上,就像 [泛型扩展](./22_Generics.md#extending-a-generic-type) 中描述的一样。你还可以使用扩展给泛型类型有条件的添加功能,就像 [具有泛型 Where 句的扩展](./22_Generics.md#extensions-with-a-generic-where-clause) 中描述的一样。
扩展可以使用在现有型类型上,就像 [扩展范型类型](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID185) 中描述的一样。你还可以使用扩展给泛型类型有条件的添加功能,就像 [扩展一个带有 Where 句的范型](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID553) 中描述的一样。
> 注意
>
@ -87,7 +87,7 @@ print("A marathon is \(aMarathon) meters long")
扩展可以给一个类添加新的便利构造器,但是它们不能给类添加新的指定构造器或者析构器。指定构造器和析构器必须始终由类的原始实现提供。
如果你使用扩展给一个值类型添加构造器,而这个值类型已经为所有存储属性提供默认值,且没有定义任何自定义构造器,那么你可以在该值类型扩展的构造器中使用默认构造器和成员构造器。如果你已经将构造器写值类型的原始实现中,则不适用于这种情况,如同 [值类型的构造器代理](./14_Initialization.md#initializer-delegation-for-value-types) 中所描述的那样
如果你使用扩展给一个值类型添加构造器只是用于给所有存储属性提供默认值,且没有定义任何自定义构造器,那么你可以在该值类型扩展的构造器中使用默认构造器和成员构造器。如果你构造器写到了值类型的原始实现中,就像 [值类型的构造器委托](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID215) 中所描述的,那么就不属于在扩展中添加构造器
如果你使用扩展给另一个模块中定义的结构体添加构造器,那么新的构造器直到定义模块中使用一个构造器之前,不能访问 `self`
@ -106,7 +106,7 @@ struct Rect {
}
```
因为 `Rect` 结构体给所有的属性都提供了默认值,所以它自动获得了一个默认构造器和一个成员构造器,就像 [默认构造器](./14_Initialization.md#default-initializers) 中描述的一样。这些构造器可以用来创建新的 `Rect` 实例:
因为 `Rect` 结构体给所有的属性都提供了默认值,所以它自动获得了一个默认构造器和一个成员构造器,就像 [默认构造器](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID213) 中描述的一样。这些构造器可以用来创建新的 `Rect` 实例:
```swift
let defaultRect = Rect()
@ -114,7 +114,7 @@ let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
```
你可以通过扩展 `Rect` 结构体来提供一个允许指定 center 和 size 的构造器:
你可以通过扩展 `Rect` 结构体来提供一个允许指定 point 和 size 的构造器:
```swift
extension Rect {
@ -184,7 +184,7 @@ someInt.square()
## 下标 {#subscripts}
扩展可以给现有的类型添加新的下标。下面的例子中,对 Swift 的 `Int` 类型添加了一个整数类型的下标。下标 `[n]` 返回从数字右侧开始的第 `n`数字
扩展可以给现有的类型添加新的下标。下面的例子中,对 Swift 的 `Int` 类型添加了一个整数类型的下标。下标 `[n]` 从数字右侧开始,返回小数点后的第 `n` 位:
- `123456789[0]` 返回 `9`
- `123456789[1]` 返回 `8`
@ -210,7 +210,7 @@ extension Int {
// 返回 7
```
如果操作的 `Int` 值没有足够的位数满足所请求的下标,那么下标的现将返回 `0`好像在数字的左边补上了 0
如果操作的 `Int` 值没有足够的位数满足所请求的下标,那么下标的现将返回 `0`好像在数字的左边补上了 0
```swift
746381295[9]
@ -268,4 +268,4 @@ printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
> 注意
>
> `number.kind` 已经被认为是 `Int.Kind` 类型。所以,在 `switch` 语句中所有的 `Int.Kind` case 分支可以被缩写,例如使用 `.negative` 替代 `Int.Kind.negative.`
> `number.kind` 已经被认为是 `Int.Kind` 类型。所以,在 `switch` 语句中所有的 `Int.Kind` case 分支可以被缩写,就像使用 `.negative` 替代 `Int.Kind.negative.`

View File

@ -22,7 +22,7 @@ struct SomeStructure: FirstProtocol, AnotherProtocol {
}
```
一个拥有父类,应该将父类名放在遵循的协议名之前,以逗号分隔:
若一个拥有父类的类在遵循协议时,应该将父类名放在协议名之前,以逗号分隔:
```swift
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
@ -75,7 +75,7 @@ let john = Person(fullName: "John Appleseed")
这个例子中定义了一个叫做 `Person` 的结构体,用来表示一个具有名字的人。从第一行代码可以看出,它遵循了 `FullyNamed` 协议。
`Person` 结构体的每一个实例都有一个 `String` 类型的存储型属性 `fullName`。这正好满足了 `FullyNamed` 协议的要求,也就意味着 `Person` 结构体正确地遵循了协议。(如果协议要求未被完全满足,在编译时会报错。)
`Person` 结构体的每一个实例都有一个 `String` 类型的存储型属性 `fullName`。这正好满足了 `FullyNamed` 协议的要求,也就意味着 `Person` 结构体正确地符合了协议。(如果协议要求未被完全满足,在编译时会报错。)
下面是一个更为复杂的类,它采纳并遵循了 `FullyNamed` 协议:
@ -105,7 +105,7 @@ var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
```swift
protocol SomeProtocol {
static func someTypeMethod()
static func someTypeMethod()
}
```
@ -143,7 +143,7 @@ print("And another one: \(generator.random())")
## 异变方法要求 {#mutating-method-requirements}
有时需要在方法中改变(或*异变*)方法所属的实例。例如,在值类型(即结构体和枚举)的实例方法中,将 `mutating` 关键字作为方法的前缀,写在 `func` 关键字之前,表示可以在该方法中修改它所属的实例以及实例的任意属性的值。这一过程在 [在实例方法中修改值类型](./11_Methods.md#modifying-value-types-from-within-instance-methods) 章节中有详细描述。
有时需要在方法中改变(或*异变*)方法所属的实例。例如,在值类型(即结构体和枚举)的实例方法中,将 `mutating` 关键字作为方法的前缀,写在 `func` 关键字之前,表示可以在该方法中修改它所属的实例以及实例的任意属性的值。这一过程在 [在实例方法中修改值类型](./11_Methods.md#modifying_value_types_from_within_instance_methods) 章节中有详细描述。
如果你在协议中定义了一个实例方法,该方法会改变遵循该协议的类型的实例,那么在定义协议时需要在方法前加 `mutating` 关键字。这使得结构体和枚举能够遵循此协议并满足此方法要求。
@ -204,13 +204,13 @@ class SomeClass: SomeProtocol {
}
```
使用 `required` 修饰符可以确保所有子类也必须提供此构造器实现,从而也能遵循协议。
使用 `required` 修饰符可以确保所有子类也必须提供此构造器实现,从而也能符合协议。
关于 `required` 构造器的更多内容,请参考 [必要构造器](./14_Initialization.md#required-initializers)。
关于 `required` 构造器的更多内容,请参考 [必要构造器](./14_Initialization.md#required_initializers)。
> 注意
>
> 如果类已经被标记为 `final`,那么不需要在协议构造器的实现中使用 `required` 修饰符,因为 `final` 类不能有子类。关于 `final` 修饰符的更多内容,请参见 [防止重写](./13_Inheritance.md#preventing-overrides)。
> 如果类已经被标记为 `final`,那么不需要在协议构造器的实现中使用 `required` 修饰符,因为 `final` 类不能有子类。关于 `final` 修饰符的更多内容,请参见 [防止重写](./13_Inheritance.md#preventing_overrides)。
如果一个子类重写了父类的指定构造器,并且该构造器满足了某个协议的要求,那么该构造器的实现需要同时标注 `required``override` 修饰符:
@ -236,7 +236,7 @@ class SomeSubClass: SomeSuperClass, SomeProtocol {
### 可失败构造器要求 {#failable-initializer-requirements}
协议还可以为遵循协议的类型定义可失败构造器要求,详见 [可失败构造器](./14_Initialization.md#failable-initializers)。
协议还可以为遵循协议的类型定义可失败构造器要求,详见 [可失败构造器](./14_Initialization.md#failable_initializers)。
遵循协议的类型可以通过可失败构造器(`init?`)或非可失败构造器(`init`)来满足协议中定义的可失败构造器要求。协议中定义的非可失败构造器要求可以通过非可失败构造器(`init`)或隐式解包可失败构造器(`init!`)来满足。
@ -272,7 +272,7 @@ class Dice {
例子中定义了一个 `Dice` 类,用来代表桌游中拥有 N 个面的骰子。`Dice` 的实例含有 `sides``generator` 两个属性,前者是整型,用来表示骰子有几个面,后者为骰子提供一个随机数生成器,从而生成随机点数。
`generator` 属性的类型为 `RandomNumberGenerator`,因此任何遵循了 `RandomNumberGenerator` 协议的类型的实例都可以赋值给 `generator`,除此之外并无其他要求。并且由于其类型是 `RandomNumberGenerator`所以`Dice` 类中与 `generator` 交互的代码,必须适用于所有遵循该协议的 `generator` 实例。这意味着不能使用由 `generator` 底层类型定义的任何方法或属性。但是你可以从协议类型转换成底层实现类型,就像从父类向下转型为子类一样。请参考 [向下转型](./18_Type_Casting.md#downcasting)。
`generator` 属性的类型为 `RandomNumberGenerator`,因此任何遵循了 `RandomNumberGenerator` 协议的类型的实例都可以赋值给 `generator`,除此之外并无其他要求。并且由于其类型是 `RandomNumberGenerator`,在 `Dice` 类中与 `generator` 交互的代码,必须适用于所有 `generator` 实例都遵循的方法。这句话的意思是不能使用由 `generator` 底层类型提供的任何方法或属性。但是你可以通过向下转型,从协议类型转换成底层实现类型,比如从父类向下转型为子类。请参考 [向下转型](./18_Type_Casting#downcasting)。
`Dice` 类还有一个构造器,用来设置初始状态。构造器有一个名为 `generator`,类型为 `RandomNumberGenerator` 的形参。在调用构造方法创建 `Dice` 的实例时,可以传入任何遵循 `RandomNumberGenerator` 协议的实例给 `generator`
@ -312,7 +312,7 @@ protocol DiceGameDelegate {
`DiceGame` 协议可以被任意涉及骰子的游戏遵循。
`DiceGameDelegate` 协议可以被任意类型遵循,用来追踪 `DiceGame` 的游戏过程。为了防止强引用导致的循环引用问题,可以把协议声明为弱引用,更多相关的知识请看 [类实例之间的循环强引用](./24_Automatic_Reference_Counting.md#strong-reference-cycles-between-class-instances),当协议标记为类专属可以使 `SnakesAndLadders` 类在声明协议时强制要使用弱引用。若要声明类专属的协议就必须继承于 `AnyObject` ,更多请看 [类专属的协议](#class-only-protocol)。
`DiceGameDelegate` 协议可以被任意类型遵循,用来追踪 `DiceGame` 的游戏过程。为了防止强引用导致的循环引用问题,可以把协议声明为弱引用,更多相关的知识请看 [类实例之间的循环强引用](./23_Automatic_Reference_Counting.md#strong_reference_cycles_between_class_instances),当协议标记为类专属可以使 `SnakesAndLadders` 类在声明协议时强制要使用弱引用。若要声明类专属的协议就必须继承于 `AnyObject` ,更多请看 [类专属的协议](#class_only_protocol)。
如下所示,`SnakesAndLadders` 是 [控制流](./05_Control_Flow.md) 章节引入的蛇梯棋游戏的新版本。新版本使用 `Dice` 实例作为骰子,并且实现了 `DiceGame``DiceGameDelegate` 协议,后者用来记录游戏的过程:
@ -355,7 +355,7 @@ class SnakesAndLadders: DiceGame {
游戏使用 `SnakesAndLadders` 类的 `init()` 构造器来初始化游戏。所有的游戏逻辑被转移到了协议中的 `play()` 方法,`play()` 方法使用协议要求的 `dice` 属性提供骰子摇出的值。
注意,`delegate` 并不是游戏的必备条件,因此 `delegate` 被定义为 `DiceGameDelegate` 类型的可选属性。因为 `delegate` 是可选值,因此会被自动赋予初始值 `nil`。随后,可以在游戏中为 `delegate` 设置适当的值。因为 `DiceGameDelegate` 协议是类专属的,可以将 `delegate` 声明为 `weak`,从而避免循环引用。
注意,`delegate` 并不是游戏的必备条件,因此 `delegate` 被定义为 `DiceGameDelegate` 类型的可选属性。因为 `delegate` 是可选值,因此会被自动赋予初始值 `nil`。随后,可以在游戏中为 `delegate` 设置适当的值。
`DicegameDelegate` 协议提供了三个方法用来追踪游戏过程。这三个方法被放置于游戏的逻辑中,即 `play()` 方法内。分别在游戏开始时,新一轮开始时,以及游戏结束时被调用。
@ -387,7 +387,7 @@ class DiceGameTracker: DiceGameDelegate {
`gameDidStart(_:)` 方法从 `game` 参数获取游戏信息并打印。`game` 参数是 `DiceGame` 类型而不是 `SnakeAndLadders` 类型,所以在 `gameDidStart(_:)` 方法中只能访问 `DiceGame` 协议中的内容。当然了,`SnakeAndLadders` 的方法也可以在类型转换之后调用。在上例代码中,通过 `is` 操作符检查 `game` 是否为 `SnakesAndLadders` 类型的实例,如果是,则打印出相应的消息。
无论当前进行的是何种游戏,由于 `game` 遵循 `DiceGame` 协议,可以确保 `game` 含有 `dice` 属性。因此在 `gameDidStart(_:)` 方法中可以通过传入的 `game` 参数来访问 `dice` 属性,进而打印出 `dice``sides` 属性的值。
无论当前进行的是何种游戏,由于 `game` 符合 `DiceGame` 协议,可以确保 `game` 含有 `dice` 属性。因此在 `gameDidStart(_:)` 方法中可以通过传入的 `game` 参数来访问 `dice` 属性,进而打印出 `dice``sides` 属性的值。
`DiceGameTracker` 的运行情况如下所示:
@ -455,7 +455,7 @@ print(game.textualDescription)
## 有条件地遵循协议 {#Conditionally-Conforming-to-a-Protocol}
泛型类型可能只在某些情况下满足一个协议的要求,比如当类型的泛型形式参数遵循对应协议时。你可以通过在扩展类型时列出限制让泛型类型有条件地遵循某协议。在你采纳协议的名字后面写泛型 `where` 分句。更多关于泛型 `where` 分句,见 [泛型 Where 分句](./22_Generics.md##where-clauses)。
泛型类型可能只在某些情况下满足一个协议的要求,比如当类型的泛型形式参数遵循对应协议时。你可以通过在扩展类型时列出限制让泛型类型有条件地遵循某协议。在你采纳协议的名字后面写泛型 `where` 分句。更多关于泛型 `where` 分句,见 [泛型 Where 分句](./22_Generics.md##where_clauses)。
下面的扩展让 `Array` 类型只要在存储遵循 `TextRepresentable` 协议的元素时就遵循 `TextRepresentable` 协议。
@ -473,7 +473,7 @@ print(myDice.textualDescription)
## 在扩展里声明采纳协议 {#declaring-protocol-adoption-with-an-extension}
当一个类型已经遵循了某个协议中的所有要求,却还没有声明采纳该协议时,可以通过空的扩展来让它采纳该协议:
当一个类型已经符合了某个协议中的所有要求,却还没有声明采纳该协议时,可以通过空的扩展来让它采纳该协议:
```swift
struct Hamster {
@ -498,65 +498,9 @@ print(somethingTextRepresentable.textualDescription)
>
> 即使满足了协议的所有要求,类型也不会自动遵循协议,必须显式地遵循协议。
## 使用合成实现来采纳协议 {#adopting-a-protocol-using-a-synthesized-implementation}
Swift 可以自动提供一些简单场景下遵循 `Equatable``Hashable``Comparable` 协议的实现。在使用这些合成实现之后,无需再编写重复的代码来实现这些协议所要求的方法。
Swift 为以下几种自定义类型提供了 `Equatable` 协议的合成实现:
- 遵循 `Equatable` 协议且只有存储属性的结构体。
- 遵循 `Equatable` 协议且只有关联类型的枚举
- 没有任何关联类型的枚举
在包含类型原始声明的文件中声明对 `Equatable` 协议的遵循,可以得到 `==` 操作符的合成实现,且无需自己编写任何关于 `==` 的实现代码。`Equatable` 协议同时包含 `!=` 操作符的默认实现。
下面的例子中定义了一个 `Vector3D` 结构体来表示一个类似 `Vector2D` 的三维向量 `(x, y, z)`。由于 `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.")
}
// 打印 "These two vectors are also equivalent."
```
Swift 为以下几种自定义类型提供了 `Hashable` 协议的合成实现:
- 遵循 `Hashable` 协议且只有存储属性的结构体。
- 遵循 `Hashable` 协议且只有关联类型的枚举
- 没有任何关联类型的枚举
在包含类型原始声明的文件中声明对 `Hashable` 协议的遵循,可以得到 `hash(into:)` 的合成实现,且无需自己编写任何关于 `hash(into:)` 的实现代码。
Swift 为没有原始值的枚举类型提供了 `Comparable` 协议的合成实现。如果枚举类型包含关联类型,那这些关联类型也必须同时遵循 `Comparable` 协议。在包含原始枚举类型声明的文件中声明其对 `Comparable` 协议的遵循,可以得到 `<` 操作符的合成实现,且无需自己编写任何关于 `<` 的实现代码。`Comparable` 协议同时包含 `<=``>``>=` 操作符的默认实现。
下面的例子中定义了 `SkillLevel` 枚举类型其中定义了初学者beginner、中级intermediate和专家expert三种等级专家等级会由额外的星级stars来进行排名。
```swift
enum SkillLevel: Comparable {
case beginner
case intermediate
case expert(stars: Int)
}
var levels = [SkillLevel.intermediate, SkillLevel.beginner,
SkillLevel.expert(stars: 5), SkillLevel.expert(stars: 3)]
for level in levels.sorted() {
print(level)
}
// 打印 "beginner"
// 打印 "intermediate"
// 打印 "expert(stars: 3)"
// 打印 "expert(stars: 5)"
```
## 协议类型的集合 {#collections-of-protocol-types}
协议类型可以在数组或者字典这样的集合中使用,在 [协议类型](./21_Protocols.md##protocols-as-types) 提到了这样的用法。下面的例子创建了一个元素类型为 `TextRepresentable` 的数组:
协议类型可以在数组或者字典这样的集合中使用,在 [协议类型](./21_Protocols.md##protocols_as_types) 提到了这样的用法。下面的例子创建了一个元素类型为 `TextRepresentable` 的数组:
```swift
let things: [TextRepresentable] = [game, d12, simonTheHamster]
@ -632,7 +576,7 @@ print(game.prettyTextualDescription)
## 类专属的协议 {#class-only-protocol}
你通过添加 `AnyObject` 关键字到协议的继承列表,就可以限制协议只能被类类型遵循(而不能是结构体类型或者枚举类型)。
你通过添加 `AnyObject` 关键字到协议的继承列表,就可以限制协议只能被类类型采纳(以及非结构体或者枚举类型)。
```swift
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
@ -644,7 +588,7 @@ protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
> 注意
>
> 当协议定义的要求需要遵循协议的类型必须是引用语义而非值语义时,应该采用类类型专属协议。关于引用语义和值语义的更多内容,请查看 [结构体和枚举是值类型](./09_Structures_And_Classes.md#structures-and-enumerations-are-value-types) 和 [类是引用类型](./09_Structures_And_Classes.md#classes-are-reference-types)。
> 当协议定义的要求需要遵循协议的类型必须是引用语义而非值语义时,应该采用类类型专属协议。关于引用语义和值语义的更多内容,请查看 [结构体和枚举是值类型](./09_Classes_and_Structures.md#structures_and_enumerations_are_value_types) 和 [类是引用类型](./09_Classes_and_Structures.md#classes_are_reference_types)。
## 协议合成 {#protocol-composition}
@ -675,9 +619,9 @@ wishHappyBirthday(to: birthdayPerson)
`Named` 协议包含 `String` 类型的 `name` 属性。`Aged` 协议包含 `Int` 类型的 `age` 属性。`Person` 结构体采纳了这两个协议。
`wishHappyBirthday(to:)` 函数的参数 `celebrator` 的类型为 `Named & Aged` 这意味着“任何同时遵循 Named 和 Aged 协议的类型”。它不关心参数的具体类型,只要参数遵循这两个协议即可。
`wishHappyBirthday(to:)` 函数的参数 `celebrator` 的类型为 `Named & Aged` 这意味着“任何同时遵循 Named 和 Aged 协议”。它不关心参数的具体类型,只要参数符合这两个协议即可。
上面的例子创建了一个名为 `birthdayPerson``Person` 的实例,作为参数传递给了 `wishHappyBirthday(to:)` 函数。因为 `Person` 同时遵循这两个协议,所以这个参数合法,函数将打印生日问候语。
上面的例子创建了一个名为 `birthdayPerson``Person` 的实例,作为参数传递给了 `wishHappyBirthday(to:)` 函数。因为 `Person` 同时符合这两个协议,所以这个参数合法,函数将打印生日问候语。
这里有一个例子:将 Location 类和前面的 Named 协议进行组合:
@ -706,16 +650,16 @@ beginConcert(in: seattle)
// 打印 "Hello, Seattle!"
```
`beginConcert(in:)` 函数接受一个类型为 `Location & Named` 的参数,这意味着“任何 Location 的子类,并且遵循 Named 协议”。在这个例子中City 就满足这样的条件。
`beginConcert(in:)` 函数接受一个类型为 `Location & Named` 的参数,这意味着“任何 Location 的子类,并且遵循 Named 协议”。例如City 就满足这样的条件。
将 birthdayPerson 传入 `beginConcert(in:)` 函数是不合法的,因为 Person 不是 Location 的子类。同理,如果你新建一个类继承于 Location但是没有遵循 Named 协议,而用这个类的实例去调用 `beginConcert(in:)` 函数也是非法的。
## 检查协议一致性 {#checking-for-protocol-conformance}
你可以使用 [类型转换](./18_Type_Casting.md) 中描述的 `is``as` 操作符来检查协议一致性,即是否遵循某协议,并且可以转换到指定的协议类型。检查和转换协议的语法与检查和转换类型是完全一样的:
你可以使用 [类型转换](./18_Type_Casting.md) 中描述的 `is``as` 操作符来检查协议一致性,即是否符合某协议,并且可以转换到指定的协议类型。检查和转换协议的语法与检查和转换类型是完全一样的:
* `is` 用来检查实例是否遵循某个协议,若遵循则返回 `true`,否则返回 `false`
* `as?` 返回一个可选值,当实例遵循某个协议时,返回类型为协议类型的可选值,否则返回 `nil`
* `is` 用来检查实例是否符合某个协议,若符合则返回 `true`,否则返回 `false`
* `as?` 返回一个可选值,当实例符合某个协议时,返回类型为协议类型的可选值,否则返回 `nil`
* `as!` 将实例强制向下转换到某个协议类型,如果强转失败,将触发运行时错误。
下面的例子定义了一个 `HasArea` 协议,该协议定义了一个 `Double` 类型的可读属性 `area`
@ -764,7 +708,7 @@ let objects: [AnyObject] = [
`objects` 数组使用字面量初始化,数组包含一个 `radius``2``Circle` 的实例,一个保存了英国国土面积的 `Country` 实例和一个 `legs``4``Animal` 实例。
如下所示,`objects` 数组可以被迭代,并对迭代出的每一个元素进行检查,看它是否遵循 `HasArea` 协议:
如下所示,`objects` 数组可以被迭代,并对迭代出的每一个元素进行检查,看它是否符合 `HasArea` 协议:
```swift
for object in objects {
@ -779,7 +723,7 @@ for object in objects {
// Something that doesn't have an area
```
当迭代出的元素遵循 `HasArea` 协议时,将 `as?` 操作符返回的可选值通过可选绑定,绑定到 `objectWithArea` 常量上。`objectWithArea``HasArea` 协议类型的实例,因此 `area` 属性可以被访问和打印。
当迭代出的元素符合 `HasArea` 协议时,将 `as?` 操作符返回的可选值通过可选绑定,绑定到 `objectWithArea` 常量上。`objectWithArea``HasArea` 协议类型的实例,因此 `area` 属性可以被访问和打印。
`objects` 数组中的元素的类型并不会因为强转而丢失类型信息,它们仍然是 `Circle``Country``Animal` 类型。然而,当它们被赋值给 `objectWithArea` 常量时,只被视为 `HasArea` 类型,因此只有 `area` 属性能够被访问。
@ -826,9 +770,9 @@ class Counter {
`increment()` 方法首先试图使用 `increment(forCount:)` 方法来得到每次的增量。`increment()` 方法使用可选链式调用来尝试调用 `increment(forCount:)`,并将当前的 `count` 值作为参数传入。
这里使用了两层可选链式调用。首先,由于 `dataSource` 可能为 `nil`,因此在 `dataSource` 后边加上了 `?`,以此表明只在 `dataSource` 非空时才去调用 `increment(forCount:)` 方法。其次,即使 `dataSource` 存在,也无法保证其是否实现了 `increment(forCount:)` 方法,因为这个方法是可选的。因此,`increment(forCount:)` 方法同样使用可选链式调用进行调用,只有在该方法被实现的情况下才能调用它,所以在 `increment(forCount:)` 方法名称后边也加上了 `?`
这里使用了两层可选链式调用。首先,由于 `dataSource` 可能为 `nil`,因此在 `dataSource` 后边加上了 `?`,以此表明只在 `dataSource` 非空时才去调用 `increment(forCount:)` 方法。其次,即使 `dataSource` 存在,也无法保证其是否实现了 `increment(forCount:)` 方法,因为这个方法是可选的。因此,`increment(forCount:)` 方法同样使用可选链式调用进行调用,只有在该方法被实现的情况下才能调用它,所以在 `increment(forCount:)` 方法后边也加上了 `?`
调用 `increment(forCount:)` 方法在上述两种情形下都有可能失败,所以返回值为 `Int?` 类型。虽然在 `CounterDataSource` 协议中,`increment(forCount:)` 的返回值类型是非可选 `Int`。另外,即使这里使用了两层可选链式调用,最后的返回结果依旧是单层的可选类型。关于这一点的更多信息,请查阅 [连接多层可选链式调用](./16_Optional_Chaining.md)。
调用 `increment(forCount:)` 方法在上述两种情形下都有可能失败,所以返回值为 `Int?` 类型。虽然在 `CounterDataSource` 协议中,`increment(forCount:)` 的返回值类型是非可选 `Int`。另外,即使这里使用了两层可选链式调用,最后的返回结果依旧是单层的可选类型。关于这一点的更多信息,请查阅 [连接多层可选链式调用](./16_Optional_Chaining)。
在调用 `increment(forCount:)` 方法后,`Int?` 型的返回值通过可选绑定解包并赋值给常量 `amount`。如果可选值确实包含一个数值,也就是说,数据源和方法都存在,数据源方法返回了一个有效值。之后便将解包后的 `amount` 加到 `count` 上,增量操作完成。
@ -857,7 +801,7 @@ for _ in 1...4 {
// 12
```
上述代码新建了一个 `Counter` 实例,并将它的数据源设置为一个 `ThreeSource` 的实例,然后调用 `increment()` 方法 `4` 次。正如预期一样,每次调用都会将 `count` 的值增加 `3`.
上述代码新建了一个 `Counter` 实例,并将它的数据源设置为一个 `ThreeSource` 的实例,然后调用 `increment()` 方法 `4` 次。按照预期预期一样,每次调用都会将 `count` 的值增加 `3`.
下面是一个更为复杂的数据源 `TowardsZeroSource`,它将使得最后的值变为 `0`
@ -917,11 +861,9 @@ print("And here's a random Boolean: \(generator.randomBool())")
// 打印 “And here's a random Boolean: true”
```
协议扩展可以为遵循协议的类型增加实现,但不能声明该协议继承自另一个协议。协议的继承只能在协议声明处进行指定。
### 提供默认实现 {#providing-default-implementations}
可以通过协议扩展来为协议要求的方法、计算属性提供默认的实现。如果遵循协议的类型为这些要求提供了自己的实现,那么这些自定义实现将会替代扩展中的默认实现被使用。
可以通过协议扩展来为协议要求的属性、方法以及下标提供默认的实现。如果遵循协议的类型为这些要求提供了自己的实现,那么这些自定义实现将会替代扩展中的默认实现被使用。
> 注意
>
@ -939,9 +881,9 @@ extension PrettyTextRepresentable {
### 为协议扩展添加限制条件 {#adding-constraints-to-protocol-extensions}
在扩展协议的时候,可以指定一些限制条件,只有遵循协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如 [泛型 Where 子句](./22_Generics.md#where-clauses) 中所描述的。
在扩展协议的时候,可以指定一些限制条件,只有遵循协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如 [泛型 Where 子句](./22_Generics.md#where_clauses) 中所描述的。
例如,你可以扩展 `Collection` 协议,适用于集合中的元素遵循了 `Equatable` 协议的情况。通过限制集合元素遵 `Equatable` 协议, 作为标准库的一部分, 你可以使用 `==``!=` 操作符来检查两个元素的等价性和非等价性。
例如,你可以扩展 `Collection` 协议,适用于集合中的元素遵循了 `Equatable` 协议的情况。通过限制集合元素遵 `Equatable` 协议, 作为标准库的一部分, 你可以使用 `==``!=` 操作符来检查两个元素的等价性和非等价性。
```swift
extension Collection where Element: Equatable {

View File

@ -16,7 +16,7 @@ func swapTwoInts(_ a: inout Int, _ b: inout Int) {
}
```
这个函数使用输入输出参数(`inout`)来交换 `a``b` 的值,具体请参考 [输入输出参数](./06_Functions.md#in-out-parameters)。
这个函数使用输入输出参数(`inout`)来交换 `a``b` 的值,具体请参考 [输入输出参数](./06_Functions.md#in_out_parameters)。
`swapTwoInts(_:_:)` 函数将 `b` 的原始值换成了 `a`,将 `a` 的原始值换成了 `b`,你可以调用这个函数来交换两个 `Int` 类型变量:
@ -44,7 +44,7 @@ func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
}
```
你可能注意到了,`swapTwoInts(_:_:)``swapTwoStrings(_:_:)``swapTwoDoubles(_:_:)` 函数体是一样的,唯一的区别是它们接受的参数类型(`Int``String``Double`)。
你可能注意到了,`swapTwoInts(_:_:)``swapTwoStrings(_:_:)``swapTwoDoubles(_:_:)` 函数体是一样的,唯一的区别是它们接受的参数类型(`Int``String``Double`)。
在实际应用中,通常需要一个更实用更灵活的函数来交换两个任意类型的值,幸运的是,泛型代码帮你解决了这种问题。(这些函数的泛型版本已经在下面定义好了。)
@ -123,7 +123,7 @@ swapTwoValues(&someString, &anotherString)
下图展示了入栈push和出栈pop的行为
![](https://docs.swift.org/swift-book/images/stackPushPop~dark@2x.png)
![](https://docs.swift.org/swift-book/_images/stackPushPop_2x.png)
1. 现在有三个值在栈中。
2. 第四个值被压入到栈的顶部。
@ -135,7 +135,7 @@ swapTwoValues(&someString, &anotherString)
```swift
struct IntStack {
var items: [Int] = []
var items = [Int]()
mutating func push(_ item: Int) {
items.append(item)
}
@ -153,7 +153,7 @@ struct IntStack {
```swift
struct Stack<Element> {
var items: [Element] = []
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
@ -186,7 +186,7 @@ stackOfStrings.push("cuatro")
下图展示了 `stackOfStrings` 如何将这四个值压栈:
![](https://docs.swift.org/swift-book/images/stackPushedFourStrings~dark@2x.png)
![](https://docs.swift.org/swift-book/_images/stackPushedFourStrings_2x.png)
移除并返回栈顶部的值“cuatro”即出栈
@ -197,7 +197,7 @@ let fromTheTop = stackOfStrings.pop()
下图展示了如何将顶部的值出栈:
![](https://docs.swift.org/swift-book/images/stackPoppedOneString~dark@2x.png)
![](https://docs.swift.org/swift-book/_images/stackPoppedOneString_2x.png)
## 泛型扩展 {#extending-a-generic-type}
@ -226,7 +226,7 @@ if let topItem = stackOfStrings.topItem {
// 打印“The top item on the stack is tres.”
```
泛型类型的扩展,还可以包括类型扩展需要额外满足的条件,从而对类型添加新功能,这一部分将在 [具有泛型 Where 子句的扩展](#extensions-with-a-generic-where-clause) 中进行讨论。
泛型类型的扩展,还可以包括类型扩展需要额外满足的条件,从而对类型添加新功能,这一部分将在[具有泛型 Where 子句的扩展](#extensions-with-a-generic-where-clause)中进行讨论。
## 类型约束 {#type-constraints}
@ -234,7 +234,7 @@ if let topItem = stackOfStrings.topItem {
例如Swift 的 `Dictionary` 类型对字典的键的类型做了些限制。在 [字典的描述](./04_Collection_Types.md#dictionaries) 中字典键的类型必须是可哈希hashable的。也就是说必须有一种方法能够唯一地表示它。字典键之所以要是可哈希的是为了便于检查字典中是否已经包含某个特定键的值。若没有这个要求字典将无法判断是否可以插入或替换某个指定键的值也不能查找到已经存储在字典中的指定键的值。
这个要求通过 `Dictionary` 键类型上的类型约束实现,它指明了键必须遵循 Swift 标准库中定义的 `Hashable` 协议。所有 Swift 的基本类型(例如 `String``Int``Double``Bool`)默认都是可哈希的。如何让自定义类型遵循 `Hashable` 协议,可以查看文档 [遵循 Hashable 协议](https://developer.apple.com/documentation/swift/hashable#2849490)。
这个要求通过 `Dictionary` 键类型上的类型约束实现,它指明了键必须遵循 Swift 标准库中定义的 `Hashable` 协议。所有 Swift 的基本类型(例如 `String``Int``Double``Bool`)默认都是可哈希的。
当自定义泛型类型时,你可以定义你自己的类型约束,这些约束将提供更为强大的泛型编程能力。像 `可哈希hashable` 这种抽象概念根据它们的概念特征来描述类型,而不是它们的具体类型。
@ -354,7 +354,7 @@ protocol Container {
```swift
struct IntStack: Container {
// IntStack 的原始实现部分
var items: [Int] = []
var items = [Int]()
mutating func push(_ item: Int) {
items.append(item)
}
@ -386,7 +386,7 @@ struct IntStack: Container {
```swift
struct Stack<Element>: Container {
// Stack<Element> 的原始实现部分
var items: [Element] = []
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
@ -410,7 +410,7 @@ struct Stack<Element>: Container {
### 扩展现有类型来指定关联类型 {#extending-an-existing-type-to-specify-an-associated-type}
[在扩展添加协议一致性](./21_Protocols.md#adding-protocol-conformance-with-an-extension) 中描述了如何利用扩展让一个已存在的类型遵循一个协议,这包括使用了关联类型协议。
[在扩展添加协议一致性](./21_Protocols.md#adding_protocol_conformance_with_an_extension) 中描述了如何利用扩展让一个已存在的类型遵循一个协议,这包括使用了关联类型协议。
Swift 的 `Array` 类型已经提供 `append(_:)` 方法,`count` 属性,以及带有 `Int` 索引的下标来检索其元素。这三个功能都符合 `Container` 协议的要求,也就意味着你只需声明 `Array` 遵循`Container` 协议,就可以扩展 Array使其遵从 Container 协议。你可以通过一个空扩展来实现这点,正如通过扩展采纳协议中的描述:
@ -446,7 +446,7 @@ protocol SuffixableContainer: Container {
}
```
在这个协议里,`Suffix` 是一个关联类型,就像上边例子中 `Container``Item` 类型一样。`Suffix` 拥有两个约束:它必须遵循 `SuffixableContainer` 协议(就是当前定义的协议),以及它的 `Item` 类型必须是和容器里的 `Item` 类型相同。`Item` 的约束是一个 `where` 分句,它在下面 [具有泛型 Where 子句的扩展](#extensions-with-a-generic-where-clause) 中有讨论。
在这个协议里,`Suffix` 是一个关联类型,就像上边例子中 `Container``Item` 类型一样。`Suffix` 拥有两个约束:它必须遵循 `SuffixableContainer` 协议(就是当前定义的协议),以及它的 `Item` 类型必须是和容器里的 `Item` 类型相同。`Item` 的约束是一个 `where` 分句,它在下面[具有泛型 Where 子句的扩展](#extensions-with-a-generic-where-clause)中有讨论。
这是上面 [泛型类型](#generic-types) 中 `Stack` 类型的扩展,它遵循了 SuffixableContainer 协议:
@ -486,7 +486,7 @@ extension IntStack: SuffixableContainer {
## 泛型 Where 语句 {#where-clauses}
[类型约束](#type-constraints) 让你能够为泛型函数、下标、类型的类型参数定义一些强制要求。
[类型约束](#type_constraints) 让你能够为泛型函数、下标、类型的类型参数定义一些强制要求。
对关联类型添加约束通常是非常有用的。你可以通过定义一个泛型 `where` 子句来实现。通过泛型 `where` 子句让关联类型遵从某个特定的协议,以及某个特定的类型参数和关联类型必须类型相同。你可以通过将 `where` 关键字紧跟在类型参数列表后面来定义 `where` 子句,`where` 子句后跟一个或者多个针对关联类型的约束,以及一个或多个类型参数和关联类型间的相等关系。你可以在函数体或者类型的大括号之前添加 `where` 子句。
@ -643,55 +643,9 @@ print([1260.0, 1200.0, 98.6, 37.0].average())
就像可以在其他地方写泛型 `where` 子句一样,你可以在一个泛型 `where` 子句中包含多个条件作为扩展的一部分。用逗号分隔列表中的每个条件。
## 包含上下文关系的 where 分句 {#contextual-where-clauses}
当你使用泛型时,可以为没有独立类型约束的声明添加 `where` 分句。例如,你可以使用 `where` 分句为泛型添加下标,或为扩展方法添加泛型约束。`Container` 结构体是个泛型,下面的例子通过 `where` 分句让新的方法声明其调用所需要满足的类型约束。
```swift
extension Container {
func average() -> Double where Item == Int {
var sum = 0.0
for index in 0..<count {
sum += Double(self[index])
}
return sum / Double(count)
}
func endsWith(_ item: Item) -> Bool where Item: Equatable {
return count >= 1 && self[count-1] == item
}
}
let numbers = [1260, 1200, 98, 37]
print(numbers.average())
// 输出 "648.75"
print(numbers.endsWith(37))
// 输出 "true"
```
例子中,当 `Item` 是整型时为 `Container` 添加 `average()` 方法,当 `Item` 遵循 `Equatable` 时添加 `endsWith(_:)` 方法。两个方法都通过 `where` 分句对 `Container` 中定义的泛型 `Item` 进行了约束。
如果不使用包含上下文关系的 `where` 分句,需要写两个扩展,并为每个扩展分别加上 `where` 分句。下面的例子和上面的具有相同效果。
```swift
extension Container where Item == Int {
func average() -> Double {
var sum = 0.0
for index in 0..<count {
sum += Double(self[index])
}
return sum / Double(count)
}
}
extension Container where Item: Equatable {
func endsWith(_ item: Item) -> Bool {
return count >= 1 && self[count-1] == item
}
}
```
在包含上下文关系的 `where` 分句的例子中,由于每个方法的 `where` 分句各自声明了需要满足的条件,因此 `average()``endsWith(_:)` 的实现能放在同一个扩展里。而将 `where` 分句放在扩展进行声明也能起到同样的效果,但每一个扩展只能有一个必备条件。
## 具有泛型 Where 子句的关联类型 {#associated-types-with-a-generic-where-clause}
你可以在关联类型后面加上具有泛型 `where`句。例如,建立一个包含迭代器(`Iterator`)的容器,就像是标准库中使用的 `Sequence` 协议那样。你应该这么写:
你可以在关联类型后面加上具有泛型 `where`句。例如,建立一个包含迭代器(`Iterator`)的容器,就像是标准库中使用的 `Sequence` 协议那样。你应该这么写:
```swift
protocol Container {
@ -721,7 +675,7 @@ protocol ComparableContainer: Container where Item: Comparable { }
extension Container {
subscript<Indices: Sequence>(indices: Indices) -> [Item]
where Indices.Iterator.Element == Int {
var result: [Item] = []
var result = [Item]()
for index in indices {
result.append(self[index])
}

View File

@ -1,8 +1,8 @@
# 自动引用计数
Swift 使用*自动引用计数ARC* 机制来跟踪和管理你的应用程序的内存。通常情况下Swift 内存管理机制会一直起作用你无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时,自动释放其占用的内存。
Swift 使用*自动引用计数ARC*机制来跟踪和管理你的应用程序的内存。通常情况下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)。
然而在少数情况下为了能帮助你管理内存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)。
> 注意
>
@ -10,11 +10,11 @@ Swift 使用*自动引用计数ARC* 机制来跟踪和管理你的应用
## 自动引用计数的工作机制 {#how-arc-works}
当你创建一个新的实例ARC 会分配一块内存来储存该实例信息。内存中会包含实例的类型信息,以及这个实例所关联的任何存储属性的值。
当你每次创建一个类的新的实例的时候ARC 会分配一块内存来储存该实例信息。内存中会包含实例的类型信息,以及这个实例所有相关的存储属性的值。
此外当实例不再被使用时ARC 释放实例所占用的内存,并让释放的内存能挪作他用。这确保了不再被使用的实例,不会一直占用内存空间。
然而,当 ARC 回收并释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你试图访问这个实例,你的应用程序很可能会崩溃。
然而,当 ARC 收回和释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你试图访问这个实例,你的应用程序很可能会崩溃。
为了确保使用中的实例不会被销毁ARC 会跟踪和计算每一个实例正在被多少属性,常量和变量所引用。哪怕实例的引用数为 1ARC 都不会销毁这个实例。
@ -37,9 +37,9 @@ class Person {
}
```
`Person` 类有一个构造器,此构造器实例的 `name` 属性赋值,并打印一条消息以表明初始化过程生效。`Person` 类也拥有一个析构器,这个析构器会在实例被销毁时打印一条消息。
`Person` 类有一个构造器,此构造器实例的 `name` 属性赋值,并打印一条消息以表明初始化过程生效。`Person` 类也拥有一个析构器,这个析构器会在实例被销毁时打印一条消息。
接下来的代码片段定义了三个类型为 `Person?` 的变量,按照代码片段中的顺序,为新的 `Person` 实例建立多个引用。由于这些变量是被定义为可选类型(`Person?`,而不是 `Person`),它们的值会被自动初始化为 `nil`,目前还不会引用到 `Person` 类的实例。
接下来的代码片段定义了三个类型为 `Person?` 的变量,用来按照代码片段中的顺序,为新的 `Person` 实例建立多个引用。由于这些变量是被定义为可选类型(`Person?`,而不是 `Person`),它们的值会被自动初始化为 `nil`,目前还不会引用到 `Person` 类的实例。
```swift
var reference1: Person?
@ -87,7 +87,7 @@ reference3 = nil
然而,我们可能会写出一个类实例的强引用数*永远不能*变成 `0` 的代码。如果两个类实例互相持有对方的强引用,因而每个实例都让对方一直存在,就是这种情况。这就是所谓的*循环强引用*。
你可以通过定义类之间的关系为弱引用或无主引用,替代强引用,从而解决循环强引用的问题。具体的过程在 [解决类实例之间的循环强引用](#resolving-strong-reference-cycles-between-class-instances) 中有描述。不管怎样,在你学习怎样解决循环强引用之前,很有必要了解一下它是怎样产生的。
你可以通过定义类之间的关系为弱引用或无主引用,替代强引用,从而解决循环强引用的问题。具体的过程在 [解决类实例之间的循环强引用](#resolving_strong_reference_cycles_between_class_instances) 中有描述。不管怎样,在你学习怎样解决循环强引用之前,很有必要了解一下它是怎样产生的。
下面展示了一个不经意产生循环强引用的例子。例子定义了两个类:`Person``Apartment`,用来建模公寓和它其中的居民:
@ -111,9 +111,9 @@ class Apartment {
类似的,每个 `Apartment` 实例有一个叫 `unit`,类型为 `String` 的属性,并有一个可选的初始化为 `nil``tenant` 属性。`tenant` 属性是可选的,因为一栋公寓并不总是有居民。
这两个类都定义了析构器,在类实例被析构的时候输出信息。这让你能够知晓 `Person``Apartment` 的实例是否像预期的那样被销毁。
这两个类都定义了析构器,用以在类实例被析构的时候输出信息。这让你能够知晓 `Person``Apartment` 的实例是否像预期的那样被销毁。
接下来的代码片段定义了两个可选类型的变量 `john``unit4A`,并分别被设为下面的 `Apartment``Person` 的实例。这两个变量都被初始化为 `nil`,这正是可选类型的优点:
接下来的代码片段定义了两个可选类型的变量 `john``unit4A`,并分别被设为下面的 `Apartment``Person` 的实例。这两个变量都被初始化为 `nil`,这正是可选类型的优点:
```swift
var john: Person?
@ -129,9 +129,9 @@ unit4A = Apartment(unit: "4A")
在两个实例被创建和赋值后,下图表现了强引用的关系。变量 `john` 现在有一个指向 `Person` 实例的强引用,而变量 `unit4A` 有一个指向 `Apartment` 实例的强引用:
![](https://docs.swift.org/swift-book/images/referenceCycle01@2x.png)
![](https://docs.swift.org/swift-book/_images/referenceCycle01_2x.png)
现在你能够将这两个实例关联在一起,这样人就能有公寓住了,而公寓也有了房客。注意感叹号是用来解包和访问可选变量 `john``unit4A` 中的实例,这样实例的属性才能被赋值:
现在你能够将这两个实例关联在一起,这样人就能有公寓住了,而公寓也有了房客。注意感叹号是用来展开和访问可选变量 `john``unit4A` 中的实例,这样实例的属性才能被赋值:
```swift
john!.apartment = unit4A
@ -140,7 +140,7 @@ unit4A!.tenant = john
在将两个实例联系在一起之后,强引用的关系如图所示:
![](https://docs.swift.org/swift-book/images/referenceCycle02@2x.png)
![](https://docs.swift.org/swift-book/_images/referenceCycle02_2x.png)
不幸的是,这两个实例关联后会产生一个循环强引用。`Person` 实例现在有了一个指向 `Apartment` 实例的强引用,而 `Apartment` 实例也有了一个指向 `Person` 实例的强引用。因此,当你断开 `john``unit4A` 变量所持有的强引用时,引用计数并不会降为 `0`,实例也不会被 ARC 销毁:
@ -153,7 +153,7 @@ unit4A = nil
在你将 `john``unit4A` 赋值为 `nil` 后,强引用关系如下图:
![](https://docs.swift.org/swift-book/images/referenceCycle03@2x.png)
![](https://docs.swift.org/swift-book/_images/referenceCycle03_2x.png)
`Person``Apartment` 实例之间的强引用关系保留了下来并且不会被断开。
@ -171,7 +171,7 @@ Swift 提供了两种办法用来解决你在使用类的属性时所遇到的
因为弱引用不会保持所引用的实例即使引用存在实例也有可能被销毁。因此ARC 会在引用的实例被销毁后自动将其弱引用赋值为 `nil`。并且因为弱引用需要在运行时允许被赋值为 `nil`,所以它们会被定义为可选类型变量,而不是常量。
你可以像其他可选值一样,检查弱引用的值是否存在,这样可以避免访问已销毁的实例的引用。
你可以像其他可选值一样,检查弱引用的值是否存在,你将永远不会访问已销毁的实例的引用。
> 注意
>
@ -210,7 +210,7 @@ unit4A!.tenant = john
现在,两个关联在一起的实例的引用关系如下图所示:
![](https://docs.swift.org/swift-book/images/weakReference01@2x.png)
![](https://docs.swift.org/swift-book/_images/weakReference01_2x.png)
`Person` 实例依然保持对 `Apartment` 实例的强引用,但是 `Apartment` 实例只持有对 `Person` 实例的弱引用。这意味着当你通过把 `john` 变量赋值为 `nil` 而断开其所保持的强引用时,再也没有指向 `Person` 实例的强引用了:
@ -221,7 +221,7 @@ john = nil
由于再也没有指向 `Person` 实例的强引用,该实例会被销毁,且 `tenant` 属性会被赋值为 `nil`
![](https://docs.swift.org/swift-book/images/weakReference02@2x.png)
![](https://docs.swift.org/swift-book/_images/weakReference02_2x.png)
唯一剩下的指向 `Apartment` 实例的强引用来自于变量 `unit4A`。如果你断开这个强引用,再也没有指向 `Apartment` 实例的强引用了:
@ -230,9 +230,9 @@ unit4A = nil
// 打印“Apartment 4A is being deinitialized”
```
由于再也没有指向 `Apartment` 实例的强引用,该实例会被销毁:
由于再也没有指向 `Person` 实例的强引用,该实例会被销毁:
![](https://docs.swift.org/swift-book/images/weakReference03@2x.png)
![](https://docs.swift.org/swift-book/_images/weakReference03_2x.png)
> 注意
>
@ -242,7 +242,7 @@ unit4A = nil
和弱引用类似,*无主引用*不会牢牢保持住引用的实例。和弱引用不同的是,无主引用在其他实例有相同或者更长的生命周期时使用。你可以在声明属性或者变量时,在前面加上关键字 `unowned` 表示这是一个无主引用。
但和弱引用不同,无主引用通常都被期望拥有值。所以将值标记为无主引用不会将它变为可选类型ARC 也不会将无主引用的值设置`nil`
无主引用通常都被期望拥有值。不过 ARC 无法在实例被销毁后将无主引用设为 `nil`,因为非可选类型的变量不允许被赋值`nil`
> 重点
>
@ -298,13 +298,13 @@ john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
在你关联两个实例后,它们的引用关系如下图所示:
![](https://docs.swift.org/swift-book/images/unownedReference01@2x.png)
![](https://docs.swift.org/swift-book/_images/unownedReference01_2x.png)
`Customer` 实例持有对 `CreditCard` 实例的强引用,而 `CreditCard` 实例持有对 `Customer` 实例的无主引用。
由于 `customer` 的无主引用,当你断开 `john` 变量持有的强引用时,再也没有指向 `Customer` 实例的强引用了:
![](https://docs.swift.org/swift-book/images/unownedReference02@2x.png)
![](https://docs.swift.org/swift-book/_images/unownedReference02_2x.png)
由于再也没有指向 `Customer` 实例的强引用,该实例被销毁了。其后,再也没有指向 `CreditCard` 实例的强引用,该实例也随之被销毁了:
@ -321,64 +321,6 @@ john = nil
> 上面的例子展示了如何使用安全的无主引用。对于需要禁用运行时的安全检查的情况例如出于性能方面的原因Swift 还提供了不安全的无主引用。与所有不安全的操作一样,你需要负责检查代码以确保其安全性。
> 你可以通过 `unowned(unsafe)` 来声明不安全无主引用。如果你试图在实例被销毁后,访问该实例的不安全无主引用,你的程序会尝试访问该实例之前所在的内存地址,这是一个不安全的操作。
### 无主可选引用 {#unowned-optional-references}
可以将类的可选引用标记为无主引用。按照 ARC 的所有权模型,无主可选引用和弱引用都可以在同一上下文里使用。区别是使用无主可选引用时,需要保证引用的对象总是合法的,或者将它设置为 `nil`
这里有一个追踪学校特定系科所提供课程的示例:
```swift
class Department {
var name: String
var courses: [Course]
init(name: String) {
self.name = name
self.courses = []
}
}
class Course {
var name: String
unowned var department: Department
unowned var nextCourse: Course?
init(name: String, in department: Department) {
self.name = name
self.department = department
self.nextCourse = nil
}
}
```
`Department` 维持着系科对其提供的每个课程的强引用。在 ARC 的所有权模型中,系科持有它的课程。`Course` 持有两个无主引用,一个是它对应的系科,另一个是学生应该修的后续课程,但课程不应该持有这两个对象。每个课程都有对应的系科,所以 `department` 属性不是可选的。然而某些课程没有推荐的后续课程,所以 `nextCourse` 属性是可选的。
下面是使用这些类的示例:
```swift
let department = Department(name: "Horticulture")
let intro = Course(name: "Survey of Plants", in: department)
let intermediate = Course(name: "Growing Common Herbs", in: department)
let advanced = Course(name: "Caring for Tropical Plants", in: department)
intro.nextCourse = intermediate
intermediate.nextCourse = advanced
department.courses = [intro, intermediate, advanced]
```
上面的代码创建系科和它的三个课程。intro 和 intermediate 课程都将建议的后续课程存储在它们的 `nextCourse` 属性中,通过无主可选引用关联学生完成该课程后的应修课程。
![](https://docs.swift.org/swift-book/images/unownedOptionalReference@2x.png)
无主可选引用不会保持对包装类实例的强持有,所以它不会影响 ARC 对该实例的析构。在 ARC 下,无主可选引用除了可以为 `nil` 外,其他表现和无主引用一致。
类似不可选无主引用,需要确保 `nextCourse` 总是引用一个还没被析构的课程。在这个案例中,假如需要从 `department.courses` 里删除一个课程,同时也需要在其他可能引用它的课程里移除它。
> 注意
>
> 可选值的底层类型是 `Optional`,是 Swift 标准库里的枚举。然而,可选是值类型不能被标记为 `unowned` 的唯一例外。
>
> 可选值包装的类不使用引用计数,所以不需要维持对可选值的强引用。
### 无主引用和隐式解包可选值属性 {#unowned-references-and-implicitly-unwrapped-optional-properties}
上面弱引用和无主引用的例子涵盖了两种常用的需要打破循环强引用的场景。
@ -389,7 +331,7 @@ department.courses = [intro, intermediate, advanced]
然而,存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后永远不会为 `nil`。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解包可选值属性。
这使两个属性在初始化完成后能被直接访问(不需要可选解包),同时避免了循环引用。这一节将为你展示如何建立这种关系。
这使两个属性在初始化完成后能被直接访问(不需要可选展开),同时避免了循环引用。这一节将为你展示如何建立这种关系。
下面的例子定义了两个类,`Country``City`,每个类将另外一个类的实例保存为属性。在这个模型中,每个国家必须有首都,每个城市必须属于一个国家。为了实现这种关系,`Country` 类拥有一个 `capitalCity` 属性,而 `City` 类有一个 `country` 属性:
@ -415,13 +357,13 @@ class City {
为了建立两个类的依赖关系,`City` 的构造器接受一个 `Country` 实例作为参数,并且将实例保存到 `country` 属性。
`Country` 的构造器调用了 `City` 的构造器。然而,只有 `Country` 的实例完全初始化后,`Country` 的构造器才能把 `self` 传给 `City` 的构造器。在 [两段式构造过程](./14_Initialization.md#two-phase-initialization) 中有具体描述。
`Country` 的构造器调用了 `City` 的构造器。然而,只有 `Country` 的实例完全初始化后,`Country` 的构造器才能把 `self` 传给 `City` 的构造器。在 [两段式构造过程](./14_Initialization.md#two_phase_initialization) 中有具体描述。
为了满足这种需求,通过在类型结尾处加上感叹号(`City!`)的方式,将 `Country``capitalCity` 属性声明为隐式解包可选值类型的属性。这意味着像其他可选类型一样,`capitalCity` 属性的默认值为 `nil`,但是不需要解包它的值就能访问它。在 [隐式解包可选值](./01_The_Basics.md#implicityly-unwrapped-optionals) 中有描述。
为了满足这种需求,通过在类型结尾处加上感叹号(`City!`)的方式,将 `Country``capitalCity` 属性声明为隐式解包可选值类型的属性。这意味着像其他可选类型一样,`capitalCity` 属性的默认值为 `nil`,但是不需要展开它的值就能访问它。在 [隐式解包可选值](./01_The_Basics.md#implicityly_unwrapped_optionals) 中有描述。
由于 `capitalCity` 默认值为 `nil`,一旦 `Country` 的实例在构造器中给 `name` 属性赋值后,整个初始化过程就完成了。这意味着一旦 `name` 属性被赋值后,`Country` 的构造器就能引用并传递隐式的 `self``Country` 的构造器在赋值 `capitalCity` 时,就能将 `self` 作为参数传递给 `City` 的构造器。
的意义在于你可以通过一条语句同时创建 `Country``City` 的实例,而不产生循环强引用,并且 `capitalCity` 的属性能被直接访问,而不需要通过感叹号来解包它的可选值:
上的意义在于你可以通过一条语句同时创建 `Country``City` 的实例,而不产生循环强引用,并且 `capitalCity` 的属性能被直接访问,而不需要通过感叹号来展开它的可选值:
```swift
var country = Country(name: "Canada", capitalName: "Ottawa")
@ -429,7 +371,7 @@ print("\(country.name)'s capital city is called \(country.capitalCity.name)")
// 打印“Canada's capital city is called Ottawa”
```
在上面的例子中,使用隐式解包可选值意味着满足了类的构造器的两个构造阶段的要求。`capitalCity` 属性在初始化完成后,能像非可选值一样使用和存取,同时还避免了循环强引用。
在上面的例子中,使用隐式解包可选值意味着满足了类的构造器的两个构造阶段的要求。`capitalCity` 属性在初始化完成后,能像非可选值一样使用和存取,同时还避免了循环强引用。
## 闭包的循环强引用 {#strong-reference-cycles-for-closures}
@ -509,9 +451,9 @@ print(paragraph!.asHTML())
不幸的是,上面写的 `HTMLElement` 类产生了类实例和作为 `asHTML` 默认值的闭包之间的循环强引用。循环强引用如下图所示:
![](https://docs.swift.org/swift-book/images/closureReferenceCycle01@2x.png)
![](https://docs.swift.org/swift-book/_images/closureReferenceCycle01_2x.png)
实例的 `asHTML` 属性持有闭包的强引用。但是,闭包在其闭包体内使用了 `self`(引用了 `self.name``self.text`),因此闭包捕获了 `self`,这意味着闭包又反过来持有了 `HTMLElement` 实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考 [值捕获](./07_Closures.md#capturing-values))。
实例的 `asHTML` 属性持有闭包的强引用。但是,闭包在其闭包体内使用了 `self`(引用了 `self.name``self.text`),因此闭包捕获了 `self`,这意味着闭包又反过来持有了 `HTMLElement` 实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考 [值捕获](./07_Closures.md#capturing_values))。
> 注意
>
@ -535,14 +477,13 @@ paragraph = nil
### 定义捕获列表 {#defining-a-capture-list}
捕获列表中的每一项都由一对元素组成,一个元素是 `weak``unowned` 关键字,另一个元素是类实例的引用(例如 `self`)或初始化过的变量(如 `delegate = self.delegate`)。这些项在方括号中用逗号分开。
捕获列表中的每一项都由一对元素组成,一个元素是 `weak``unowned` 关键字,另一个元素是类实例的引用(例如 `self`)或初始化过的变量(如 `delegate = self.delegate!`)。这些项在方括号中用逗号分开。
如果闭包有参数列表和返回类型,把捕获列表放在它们前面:
```swift
lazy var someClosure = {
[unowned self, weak delegate = self.delegate]
(index: Int, stringToProcess: String) -> String in
lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// 这里是闭包的函数体
}
```
@ -550,8 +491,8 @@ lazy var someClosure = {
如果闭包没有指明参数列表或者返回类型,它们会通过上下文推断,那么可以把捕获列表和关键字 `in` 放在闭包最开始的地方:
```swift
lazy var someClosure = {
[unowned self, weak delegate = self.delegate] in
lazy var someClosure: () -> String = {
[unowned self, weak delegate = self.delegate!] in
// 这里是闭包的函数体
}
```
@ -607,7 +548,7 @@ print(paragraph!.asHTML())
使用捕获列表后引用关系如下图所示:
![](https://docs.swift.org/swift-book/images/closureReferenceCycle02@2x.png)
![](https://docs.swift.org/swift-book/_images/closureReferenceCycle02_2x.png)
这一次,闭包以无主引用的形式捕获 `self`,并不会持有 `HTMLElement` 实例的强引用。如果将 `paragraph` 赋值为 `nil``HTMLElement` 实例将会被销毁,并能看到它的析构器打印出的消息:
@ -616,4 +557,4 @@ paragraph = nil
// 打印“p is being deinitialized”
```
你可以查看 [捕获列表](../03_language_reference/04_Expressions.md) 章节,获取更多关于捕获列表的信息。
你可以查看 [捕获列表](../chapter3/04_Expressions.html) 章节,获取更多关于捕获列表的信息。

View File

@ -20,7 +20,7 @@ print("We're number \(one)!")
你可以思考一下预算表更新的过程,会看到同样的问题。更新预算表总共有两步:首先你把预算项的名字和费用加上,然后再更新总数来反映预算表的现况。在更新之前和之后,你都可以从预算表里读取任何信息并获得正确的答案,就像下面展示的那样。
![](https://docs.swift.org/swift-book/images/memory_shopping@2x.png)
![](https://docs.swift.org/swift-book/_images/memory_shopping_2x.png)
而当你添加预算项进入表里的时候,它只是在一个临时的,错误的状态,因为总数还没有被更新。在添加数据的过程中读取总数就会读取到错误的信息。
@ -30,7 +30,7 @@ print("We're number \(one)!")
>
> 如果你写过并发和多线程的代码,内存访问冲突也许是同样的问题。然而,这里访问冲突的讨论是在单线程的情境下讨论的,并没有使用并发或者多线程。
>
> 如果你曾经在单线程代码里有访问冲突Swift 可以保证你在编译或者运行时会得到错误。对于多线程的代码,可以使用 [Thread Sanitizer](https://developer.apple.com/documentation/xcode/diagnosing_memory_thread_and_crash_issues_early) 去帮助检测多线程的冲突。
> 如果你曾经在单线程代码里有访问冲突Swift 可以保证你在编译或者运行时会得到错误。对于多线程的代码,可以使用 [Thread Sanitizer](https://developer.apple.com/documentation/code_diagnostics/thread_sanitizer) 去帮助检测多线程的冲突。
### 内存访问性质 {#characteristics-of-memory-access}
@ -61,9 +61,9 @@ print(myNumber)
## In-Out 参数的访问冲突 {#conflicting-access-to-in-out-parameters}
一个函数会对它所有的 in-out 参数保持长期写访问。in-out 参数的写访问会在所有非 in-out 参数处理完之后开始,直到函数执行完毕为止。如果有多个 in-out 参数,则写访问开始的顺序与参数的顺序一致。
一个函数会对它所有的 in-out 参数进行长期写访问。in-out 参数的写访问会在所有非 in-out 参数处理完之后开始,直到函数执行完毕为止。如果有多个 in-out 参数,则写访问开始的顺序与参数的顺序一致。
这种长期保持的写访问带来的问题是,你不能访问以 in-out 形式传入的原变量,即使作用域原则和访问权限允许——任何访问原变量的行为都会造成冲突。例如:
长期访问的存在会造成一个结果,你不能访问以 in-out 形式传入的原变量,即使作用域原则和访问权限允许——任何访问原变量的行为都会造成冲突。例如:
```swift
var stepSize = 1
@ -78,9 +78,9 @@ increment(&stepSize)
在上面的代码里,`stepSize` 是一个全局变量,并且它可以在 `increment(_:)` 里正常访问。然而,对于 `stepSize` 的读访问与 `number` 的写访问重叠了。就像下面展示的那样,`number``stepSize` 都指向了同一个存储地址。同一块内存的读和写访问重叠了,就此产生了冲突。
![](https://docs.swift.org/swift-book/images/memory_increment@2x.png)
![](https://docs.swift.org/swift-book/_images/memory_increment_2x.png)
解决这个冲突的一种方式,是显拷贝一份 `stepSize`
解决这个冲突的一种方式,是显拷贝一份 `stepSize`
```swift
// 显式拷贝
@ -148,7 +148,7 @@ oscar.shareHealth(with: &maria) // 正常
上面的例子里,调用 `shareHealth(with:)` 方法去把 `oscar` 玩家的血量分享给 `maria` 玩家并不会造成冲突。在方法调用期间会对 `oscar` 发起写访问,因为在 mutating 方法里 `self` 就是 `oscar`,同时对于 `maria` 也会发起写访问,因为 `maria` 作为 in-out 参数传入。过程如下,它们会访问内存的不同位置。即使两个写访问重叠了,它们也不会冲突。
![](https://docs.swift.org/swift-book/images/memory_share_health_maria@2x.png)
![](https://docs.swift.org/swift-book/_images/memory_share_health_maria_2x.png)
当然,如果你将 `oscar` 作为参数传入 `shareHealth(with:)` 里,就会产生冲突:
@ -159,11 +159,11 @@ oscar.shareHealth(with: &oscar)
mutating 方法在调用期间需要对 `self` 发起写访问,而同时 in-out 参数也需要写访问。在方法里,`self``teammate` 都指向了同一个存储地址——就像下面展示的那样。对于同一块内存同时进行两个写访问,并且它们重叠了,就此产生了冲突。
![](https://docs.swift.org/swift-book/images/memory_share_health_oscar@2x.png)
![](https://docs.swift.org/swift-book/_images/memory_share_health_oscar_2x.png)
## 属性的访问冲突 {#conflicting-access-to-properties}
如结构体,元组和枚举的类型都是由多个独立的值组成的,例如结构体的属性或元组的元素。因为它们都是值类型,修改值的任何一部分都是对于整个值的修改,意味着其中一个属性的读或写访问都需要访问整个值。例如,元组元素的写访问重叠会产生冲突:
如结构体,元组和枚举的类型都是由多个独立的值组成的,例如结构体的属性或元组的元素。因为它们都是值类型,修改值的任何一部分都是对于整个值的修改,意味着其中一个属性的读或写访问都需要访问整个值。例如,元组元素的写访问重叠会产生冲突:
```swift
var playerInformation = (health: 10, energy: 20)
@ -180,7 +180,7 @@ var holly = Player(name: "Holly", health: 10, energy: 10)
balance(&holly.health, &holly.energy) // 错误
```
在实践中,大多数对于结构体属性的访问都会安全的重叠。例如,将上面例子里的变量 `holly` 改为本地变量而非全局变量,编译器就可以保证这个重叠访问是安全的:
在实践中,大多数对于结构体属性的访问都会安全的重叠。例如,将上面例子里的变量 `holly` 改为本地变量而非全局变量,编译器就可以保证这个重叠访问是安全的:
```swift
func someFunction() {

View File

@ -1,14 +1,14 @@
# 访问控制
*访问控制*可以限定其它源文件或模块对你的代码的访问。这个特性可以让隐藏代码的实现细节,并且能提供一个接口来让别人访问和使用的代码。
*访问控制*可以限定其它源文件或模块中的代码对你的代码的访问级别。这个特性可以让我们隐藏代码的一些实现细节,并且可以为其他人可以访问和使用的代码提供接口
你可以明确地给单个类型(类、结构体、枚举)设置访问级别,也可以给这些类型的属性、方法、构造器、下标等设置访问级别。协议也可以被限定在一定访问级别的范围内使用,包括协议里的全局常量、变量和函数。
你可以明确地给单个类型(类、结构体、枚举)设置访问级别,也可以给这些类型的属性、方法、构造器、下标等设置访问级别。协议也可以被限定在一定的范围内使用,包括协议里的全局常量、变量和函数。
Swift 不仅提供了多种不同的访问级别,还为某些典型场景提供了默认的访问级别,这样就不需要我们在每段代码中都显式声明访问级别。如果只是开发一个单 target 的应用程序,完全可以不用显式声明代码的访问级别。
Swift 不仅提供了多种不同的访问级别,还为某些典型场景提供了默认的访问级别,这样就不需要我们在每段代码中都申明显式访问级别。其实,如果只是开发一个单 target 的应用程序,我们完全可以不用显式声明代码的访问级别。
> 注意
>
> 为了简单起见,对于代码中可以设置访问级别的特性(属性、基本类型、函数等),在下面的章节中我们会统一称之为“实体”。
> 为了简单起见,对于代码中可以设置访问级别的特性(属性、基本类型、函数等),在下面的章节中我们会称之为“实体”。
## 模块和源文件 {#modules-and-source-files}
@ -16,22 +16,29 @@ Swift 中的访问控制模型基于模块和源文件这两个概念。
*模块*指的是独立的代码单元,框架或应用程序会作为一个独立的模块来构建和发布。在 Swift 中,一个模块可以使用 `import` 关键字导入另外一个模块。
在 Swift 中Xcode 的每个 target例如框架或应用程序都被当作独立的模块处理。如果你是为了实现某个通用的功能或者是为了封装一些常用方法而将代码打包成独立的框架这个框架就是 Swift 中的一个模块。当它被导入到某个应用程序或者其他框架时,框架内容都将属于这个独立的模块。
在 Swift 中Xcode 的每个 target例如框架或应用程序都被当作独立的模块处理。如果你是为了实现某个通用的功能或者是为了封装一些常用方法而将代码打包成独立的框架这个框架就是 Swift 中的一个模块。当它被导入到某个应用程序或者其他框架时,框架内容都将属于这个独立的模块。
*源文件* 就是 Swift 模块中的源代码文件(实际上,源文件属于一个应用程序或框架。尽管我们一般会将不同的类型分别定义在不同的源文件中,但是同一个源文件也可以包含多个类型、函数的定义。
*源文件*就是 Swift 中的源代码文件,它通常属于一个模块,即一个应用程序或框架。尽管我们一般会将不同的类型分别定义在不同的源文件中,但是同一个源文件也可以包含多个类型、函数之类的定义。
## 访问级别 {#access-levels}
Swift 为代码中的实体提供了五种不同的*访问级别*。这些访问级别不仅与源文件中定义的实体相关,同时也与源文件所属的模块相关。
- *open**public* 级别可以让实体被同一模块源文件中的所有实体访问,在模块外也可以通过导入该模块来访问源文件里的所有实体。通常情况下,你会使用 open 或 public 级别来指定框架的外部接口。open 和 public 的区别在后面会提到。
- *internal* 级别让实体被同一模块源文件中的任何实体访问,但是不能被模块外的实体访问。通常情况下,如果某个接口只在应用程序或框架内部使用,就可以将其设置为 internal 级别。
- *fileprivate* 限制实体只能在其定义的文件内部访问。如果功能的部分实现细节只需要在文件内使用时,可以使用 fileprivate 来将其隐藏。
- *private* 限制实体只能在其定义的作用域,以及同一文件内的 extension 访问。如果功能的部分细节只需要在当前作用域内使用时,可以使用 private 来将其隐藏。
- *Open**Public* 级别可以让实体被同一模块源文件中的所有实体访问,在模块外也可以通过导入该模块来访问源文件里的所有实体。通常情况下,你会使用 Open 或 Public 级别来指定框架的外部接口。Open 和 Public 的区别在后面会提到。
- *Internal* 级别让实体被同一模块源文件中的任何实体访问,但是不能被模块外的实体访问。通常情况下,如果某个接口只在应用程序或框架内部使用,就可以将其设置为 Internal 级别。
- *File-private* 限制实体只能在其定义的文件内部访问。如果功能的部分细节只需要在文件内使用时,可以使用 File-private 来将其隐藏。
- *Private* 限制实体只能在其定义的作用域,以及同一文件内的 extension 访问。如果功能的部分细节只需要在当前作用域内使用时,可以使用 Private 来将其隐藏。
open 为最高访问级别(限制最少),private 为最低访问级别(限制最多)。
Open 为最高访问级别(限制最少),Private 为最低访问级别(限制最多)。
open 只能作用于类和类的成员,它和 public 的区别主要在于 open 限定的类和成员能够在模块外被继承和重写,在下面的 [子类](#subclassing) 这一节中有详解。将类的访问级别显式指定为 `open` 表明你已经设计好了类的代码,并且充分考虑过这个类在其他模块中用作父类时的影响。
Open 只能作用于类和类的成员,它和 Public 的区别如下:
* Public 或者其它更严访问级别的类,只能在其定义的模块内部被继承。
* Public 或者其它更严访问级别的类成员,只能在其定义的模块内部的子类中重写。
* Open 的类,可以在其定义的模块中被继承,也可以在引用它的模块中被继承。
* Open 的类成员,可以在其定义的模块中子类中重写,也可以在引用它的模块中的子类重写。
把一个类标记为 `open`,明确的表示你已经充分考虑过外部模块使用此类作为父类的影响,并且设计好了你的类的代码了。
### 访问级别基本原则 {#guiding-principle-of-access-levels}
@ -39,22 +46,22 @@ Swift 中的访问级别遵循一个基本原则:*实体不能定义在具有
例如:
- 一个 public 的变量,其类型的访问级别不能是 internalfileprivate 或是 private。因为无法保证变量的类型在使用变量的地方也具有访问权限。
- 一个 Public 的变量,其类型的访问级别不能是 InternalFile-private 或是 Private。因为无法保证变量的类型在使用变量的地方也具有访问权限。
- 函数的访问级别不能高于它的参数类型和返回类型的访问级别。因为这样就会出现函数可以在任何地方被访问,但是它的参数类型和返回类型却不可以的情况。
关于此原则在各种情况下的具体表现,将在下文有所体现。
### 默认访问级别 {#default-access-levels}
代码中所有的实体,如果你不显式指定它们的访问级别,那么它们将都有一个 `internal` 的默认访问级别(有一些例外情况,本文稍后会说明)。因此,多数情况下不需要显式指定实体的访问级别。
如果你没有为代码中的实体显式指定访问级别,那么它们默认为 `internal` 级别(有一些例外情况,稍后会进行说明)。因此,在大多数情况下,我们不需要显式指定实体的访问级别。
### 单 target 应用程序的访问级别 {#access-levels-for-single-target-apps}
当你编写一个单 target 应用程序时,应用的所有功能都是为该应用服务,而不需要提供给其他应用或者模块使用,所以不需要明确设置访问级别,使用默认的访问级别 internal 即可。但是,你也可以使用 `fileprivate``private` 访问级别,用于隐藏一些功能的实现细节。
当你编写一个单目标应用程序时,应用的所有功能都是为该应用服务,而不需要提供给其他应用或者模块使用,所以我们不需要明确设置访问级别,使用默认的访问级别 Internal 即可。但是,你也可以使用 `fileprivate` 访问`private` 访问级别,用于隐藏一些功能的实现细节。
### 框架的访问级别 {#access-levels-for-frameworks}
当你开发框架时,就需要把一些对外的接口定义为 open 或 public 访问级别,以便使用者导入该框架后可以正常使用其功能。这些被你定义为对外的接口,就是这个框架的 API。
当你开发框架时,就需要把一些对外的接口定义为 Open 或 Public以便使用者导入该框架后可以正常使用其功能。这些被你定义为对外的接口就是这个框架的 API。
> 注意
>
@ -80,7 +87,7 @@ fileprivate func someFilePrivateFunction() {}
private func somePrivateFunction() {}
```
除非专门指定,否则实体默认的访问级别为 `internal`,可以查阅 [默认访问级别](#default-access-levels) 这一节。这意味着在不使用修饰符显式声明访问级别的情况下,`SomeInternalClass``someInternalConstant`访问级别是 `internal`
除非专门指定,否则实体默认的访问级别为 `internal`,可以查阅 [默认访问级别](#default_access_levels) 这一节。这意味着在不使用修饰符显式声明访问级别的情况下,`SomeInternalClass``someInternalConstant` 仍然拥有隐式`internal`
```swift
class SomeInternalClass {} // 隐式 internal
@ -89,7 +96,7 @@ var someInternalConstant = 0 // 隐式 internal
## 自定义类型 {#custom-types}
如果想为一个自定义类型指定访问级别,在定义类型时进行指定即可。新类型只能在它的访问级别限制范围内使用。例如,你定义了一个 `fileprivate` 级别的类,那这个类就只能在定义它的源文件中使用,可以作为属性类型、函数参数类型或者返回类型等等。
如果想为一个自定义类型指定访问级别,在定义类型时进行指定即可。新类型只能在它的访问级别限制范围内使用。例如,你定义了一个 `fileprivate` 级别的类,那这个类就只能在定义它的源文件中使用,可以作为属性类型、函数参数类型或者返回类型等等。
一个类型的访问级别也会影响到类型*成员*(属性、方法、构造器、下标)的默认访问级别。如果你将类型指定为 `private` 或者 `fileprivate` 级别,那么该类型的所有成员的默认访问级别也会变成 `private` 或者 `fileprivate` 级别。如果你将类型指定为 `internal``public`(或者不明确指定访问级别,而使用默认的 `internal` ),那么该类型的所有成员的默认访问级别将是 `internal`
@ -120,15 +127,13 @@ private class SomePrivateClass { // 显式 private 类
func somePrivateMethod() {} // 隐式 private 类成员
}
```
### 元组类型 {#tuple-types}
元组的访问级别将由元组中访问级别最严格的类型来决定。例如,如果你构建了一个包含两种不同类型的元组,其中一个类型为 `internal`,另一个类型为 `private`,那么这个元组的访问级别为 `private`
> 注意
>
> 元组不同于类、结构体、枚举、函数那样有单独的定义。一个元组的访问级别是由元组中元素的访问级别来决定的,不能被显式指定。
> 元组不同于类、结构体、枚举、函数那样有单独的定义。元组的访问级别是在它被使用时自动推断出的,而无法明确指定。
### 函数类型 {#function-types}
@ -142,9 +147,9 @@ func someFunction() -> (SomeInternalClass, SomePrivateClass) {
}
```
我们可以看到,这个函数的返回类型是一个元组,该元组中包含两个自定义的类(可查阅 [自定义类型](#custom-types))。其中一个类的访问级别是 `internal`,另一个的访问级别是 `private`,所以根据元组访问级别的原则,该元组的访问级别是 `private`(元组的访问级别与元组中访问级别最低的类型一致)。
我们可以看到,这个函数的返回类型是一个元组,该元组中包含两个自定义的类(可查阅 [自定义类型](#custom_types))。其中一个类的访问级别是 `internal`,另一个的访问级别是 `private`,所以根据元组访问级别的原则,该元组的访问级别是 `private`(元组的访问级别与元组中访问级别最低的类型一致)。
因为该函数返回类型的访问级别是 `private`,所以你必须使用 `private` 修饰符明确指定该函数的访问级别:
因为该函数返回类型的访问级别是 `private`,所以你必须使用 `private` 修饰符明确指定该函数的访问级别:
```swift
private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
@ -158,7 +163,7 @@ private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
枚举成员的访问级别和该枚举类型相同,你不能为枚举成员单独指定不同的访问级别。
比如下面的例子,枚举 `CompassPoint` 被明确指定为 `public`,那么它的成员 `north``south``east``west` 的访问级别同样也是 `public`
比如下面的例子,枚举 `CompassPoint` 被明确指定为 `public`,那么它的成员 `North``South``East``West` 的访问级别同样也是 `public`
```swift
public enum CompassPoint {
@ -175,15 +180,15 @@ public enum CompassPoint {
### 嵌套类型 {#nested-types}
嵌套类型的访问级别和包含它的类型的访问级别相同,嵌套类型是 public 的情况除外。在一个 public 的类型中定义嵌套类型,那么嵌套类型自动拥有 `internal` 访问级别。如果想让嵌套类型拥有 `public` 访问级别,那么必须显式指定该嵌套类型的访问级别为 public
如果在 `private` 的类型中定义嵌套类型,那么该嵌套类型就自动拥有 `private` 访问级别。如果在 `public` 或者 `internal` 级别的类型中定义嵌套类型,那么嵌套类型自动拥有 `internal` 访问级别。如果想让嵌套类型拥有 `public` 访问级别,那么需要明确指定该嵌套类型的访问级别。
## 子类 {#subclassing}
你可以继承同一模块中的所有有访问权限的类,也可以继承不同模块中被 open 修饰的类。一个子类的访问级别不得高于父类的访问级别。例如,父类的访问级别是 `internal`,子类的访问级别就不能是 `public`
子类的访问级别不得高于父类的访问级别。例如,父类的访问级别是 `internal`,子类的访问级别就不能是 `public`
此外,在同一模块中,你可以在符合当前访问级别的条件下重写任意类成员(方法、属性、构造器、下标等)。在不同模块中,你可以重写类中被 open 修饰的成员。
此外,你可以在符合当前访问级别的条件下重写任意类成员(方法、属性、构造器、下标等)。
可以通过重写给所继承类的成员提供更高的访问级别。下面的例子中,类 `A` 的访问级别是 `public`,它包含一个方法 `someMethod()`,访问级别为 `fileprivate`。类 `B` 继承自类 `A`,访问级别为 `internal`,但是在类 `B` 中重写了类 `A` 中访问级别为 `fileprivate` 的方法 `someMethod()`,并重新指定为 `internal` 级别。通过这种方式,我们就可以将某类中 `fileprivate` 级别的类成员重新指定为更高的访问级别,以便其他人使用:
可以通过重写为继承来的类成员提供更高的访问级别。下面的例子中,类 `A` 的访问级别是 `public`,它包含一个方法 `someMethod()`,访问级别为 `fileprivate`。类 `B` 继承自类 `A`,访问级别为 `internal`,但是在类 `B` 中重写了类 `A` 中访问级别为 `fileprivate` 的方法 `someMethod()`,并重新指定为 `internal` 级别。通过这种方式,我们就可以将某类中 `fileprivate` 级别的类成员重新指定为更高的访问级别,以便其他人使用:
```swift
public class A {
@ -229,7 +234,7 @@ private var privateInstance = SomePrivateClass()
> 注意
>
> 这个规则同时适用于存储型属性和计算型属性。即使你不明确指定存储型属性的 `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` 属性被修改的次数:
@ -277,13 +282,13 @@ public struct TrackedString {
## 构造器 {#initializers}
自定义构造器的访问级别可以低于或等于其所属类型的访问级别。唯一的例外是 [必要构造器](./14_Initialization.md#required-initializers),它的访问级别必须和所属类型的访问级别相同。
自定义构造器的访问级别可以低于或等于其所属类型的访问级别。唯一的例外是 [必要构造器](./14_Initialization.md#required_initializers),它的访问级别必须和所属类型的访问级别相同。
如同函数或方法的参数,构造器参数的访问级别也不能低于构造器本身的访问级别。
### 默认构造器 {#default-initializers}
如 [默认构造器](./14_Initialization.md#default-initializers) 所述Swift 会为结构体和类提供一个默认的无参数的构造器,只要它们为所有存储型属性设置了默认初始值,并且未提供自定义的构造器。
如 [默认构造器](./14_Initialization.md#default_initializers) 所述Swift 会为结构体和类提供一个默认的无参数的构造器,只要它们为所有存储型属性设置了默认初始值,并且未提供自定义的构造器。
默认构造器的访问级别与所属类型的访问级别相同,除非类型的访问级别是 `public`。如果一个类型被指定为 `public` 级别,那么默认构造器的访问级别将为 `internal`。如果你希望一个 `public` 级别的类型也能在其他模块中使用这种无参数的默认构造器,你只能自己提供一个 `public` 访问级别的无参数构造器。
@ -295,17 +300,17 @@ public struct TrackedString {
## 协议 {#protocols}
如果想为一个协议类型明确地指定访问级别,在声明协议时指定即可。这将限制该协议只能在适当的访问级别范围内被遵循。
如果想为一个协议类型明确地指定访问级别,在定义协议时指定即可。这将限制该协议只能在适当的访问级别范围内被遵循。
协议中的每个方法或属性都必须具有和该协议相同的访问级别。你不能将协议中的方法或属性设置为其他访问级别。这样才能确保该协议的所有方法或属性对于任意遵循者都可用。
协议中的每一个要求都具有和该协议相同的访问级别。你不能将协议中的要求设置为其他访问级别。这样才能确保该协议的所有要求对于任意遵循者都可用。
> 注意
>
> 如果你定义了一个 `public` 访问级别的协议,那么该协议的所有实现也会是 `public` 访问级别。这一点不同于其他类型,例如,类型是 `public` 访问级别时,其成员的访问级别却只是 `internal`
> 如果你定义了一个 `public` 访问级别的协议,那么该协议的所有实现也会是 `public` 访问级别。这一点不同于其他类型,例如,类型是 `public` 访问级别时,其成员的访问级别却只是 `internal`
### 协议继承 {#protocol-inheritance}
如果定义了一个继承自其他协议的新协议,那么新协议拥有的访问级别最高也只能和被继承协议的访问级别相同。例如,你不能将继承自 `internal` 协议的新协议访问级别指定为 `public`
如果定义了一个继承自其他协议的新协议,那么新协议拥有的访问级别最高也只能和被继承协议的访问级别相同。例如,你不能将继承自 `internal` 协议的新协议定`public` 协议
### 协议遵循 {#protocol-conformance}
@ -321,9 +326,9 @@ public struct TrackedString {
## Extension {#extensions}
Extension 可以在访问级别允许的情况下对类、结构体、枚举进行扩展。Extension 的新增成员具有和原始类型成员一致的访问级别。例如,你使用 extension 扩展了一个 `public` 或者 `internal` 类型,extension 中的成员就默认使用 `internal` 访问级别。如果你使用 extension 扩展一个 `fileprivate` 类型,则 extension 中的成员默认使用 `fileprivate` 访问级别。如果你使用 extension 扩展了一个 `private` 类型,则 extension 的成员默认使用 `private` 访问级别。
Extension 可以在访问级别允许的情况下对类、结构体、枚举进行扩展。Extension 的成员具有和原始类型成员一致的访问级别。例如,你使用 extension 扩展了一个 `public` 或者 `internal` 类型extension 中的成员就默认使用 `internal` 访问级别,和原始类型中的成员一致。如果你使用 extension 扩展了一个 `private` 类型,则 extension 的成员默认使用 `private` 访问级别。
或者,你可以通过修饰语重新指定 extension 的默认访问级别(例如,`private`),从而给该 extension 中的所有成员指定一个新的默认访问级别。这个新的默认访问级别仍然可以被单独成员指定的访问级别所覆盖。
或者,你可以明确指定 extension 的访问级别(例如,`private extension`),从而给该 extension 中的所有成员指定一个新的默认访问级别。这个新的默认访问级别仍然可以被单独指定的访问级别所覆盖。
如果你使用 extension 来遵循协议的话,就不能显式地声明 extension 的访问级别。extension 每个 protocol 要求的实现都默认使用 protocol 的访问级别。
@ -335,7 +340,7 @@ Extension 可以在访问级别允许的情况下对类、结构体、枚举进
- 在 extension 里声明一个私有成员,在同一文件的另一个 extension 里访问。
- 在 extension 里声明一个私有成员,在同一文件的类型声明里访问。
这意味着你可以使用 extension 来组织你的代码,而且不受私有成员的影响。例如,给定下面这样一个简单的协议:
这意味着你可以像组织的代码去使用 extension而且不受私有成员的影响。例如给定下面这样一个简单的协议
```swift
protocol SomeProtocol {
@ -363,7 +368,7 @@ extension SomeStruct: SomeProtocol {
## 类型别名 {#type-aliases}
你定义的任何类型别名都会被当作不同的类型,以便于进行访问控制。类型别名的访问级别不可高于其表示的类型的访问级别。例如,`private` 级别的类型别名可以作为 `private``fileprivate``internal``public` 或者 `open` 类型的别名,但是 `public` 级别的类型别名只能作为 `public` 类型的别名,不能作为 `internal``fileprivate``private` 类型的别名。
你定义的任何类型别名都会被当作不同的类型,以便于进行访问控制。类型别名的访问级别不可高于其表示的类型的访问级别。例如,`private` 级别的类型别名可以作为 `private``file-private``internal``public` 或者 `open` 类型的别名,但是 `public` 级别的类型别名只能作为 `public` 类型的别名,不能作为 `internal``file-private``private` 类型的别名。
> 注意
>

View File

@ -16,9 +16,9 @@ Swift 支持 C 语言中的全部位运算符,接下来会一一介绍。
### Bitwise NOT Operator按位取反运算符 {#bitwise-not-operator}
*按位取反运算符(`~`* 对一个数值的全部比特位进行取反:
*按位取反运算符(`~`*对一个数值的全部比特位进行取反:
![Art/bitwiseNOT_2x.png](https://docs.swift.org/swift-book/images/bitwiseNOT@2x.png)
![Art/bitwiseNOT_2x.png](https://docs.swift.org/swift-book/_images/bitwiseNOT_2x.png)
按位取反运算符是一个前缀运算符,直接放在运算数之前,并且它们之间不能添加任何空格:
@ -35,7 +35,7 @@ let invertedBits = ~initialBits // 等于 0b11110000
*按位与运算符(`&`* 对两个数的比特位进行合并。它返回一个新的数,只有当两个数的对应位*都*为 `1` 的时候,新数的对应位才为 `1`
![Art/bitwiseAND_2x.png](https://docs.swift.org/swift-book/images/bitwiseAND@2x.png)
![Art/bitwiseAND_2x.png](https://docs.swift.org/swift-book/_images/bitwiseAND_2x.png)
在下面的示例当中,`firstSixBits``lastSixBits` 中间 4 个位的值都为 `1`。使用按位与运算符之后,得到二进制数值 `00111100`,等价于无符号十进制数的 `60`
@ -49,7 +49,7 @@ let middleFourBits = firstSixBits & lastSixBits // 等于 00111100
*按位或运算符(`|`*可以对两个数的比特位进行比较。它返回一个新的数,只要两个数的对应位中有*任意一个*为 `1` 时,新数的对应位就为 `1`
![Art/bitwiseOR_2x.png](https://docs.swift.org/swift-book/images/bitwiseOR@2x.png)
![Art/bitwiseOR_2x.png](https://docs.swift.org/swift-book/_images/bitwiseOR_2x.png)
在下面的示例中,`someBits``moreBits` 存在不同的位被设置为 `1`。使用按位或运算符之后,得到二进制数值 `11111110`,等价于无符号十进制数的 `254`
@ -63,7 +63,7 @@ let combinedbits = someBits | moreBits // 等于 11111110
*按位异或运算符*,或称“排外的或运算符”(`^`),可以对两个数的比特位进行比较。它返回一个新的数,当两个数的对应位不相同时,新数的对应位就为 `1`,并且对应位相同时则为 `0`
![Art/bitwiseXOR_2x.png](https://docs.swift.org/swift-book/images/bitwiseXOR@2x.png)
![Art/bitwiseXOR_2x.png](https://docs.swift.org/swift-book/_images/bitwiseXOR_2x.png)
在下面的示例当中,`firstBits``otherBits` 都有一个自己为 `1`,而对方为 `0` 的位。按位异或运算符将新数的这两个位都设置为 `1`。在其余的位上 `firstBits``otherBits` 是相同的,所以设置为 `0`
@ -75,7 +75,7 @@ let outputBits = firstBits ^ otherBits // 等于 00010001
### Bitwise Left and Right Shift Operators按位左移、右移运算符 {#bitwise-left-and-right-shift-operators}
*按位左移运算符(`<<`* 和 *按位右移运算符(`>>`* 可以对一个数的所有位进行指定位数的左移和右移,但是需要遵守下面定义的规则。
*按位左移运算符(`<<`* 和 *按位右移运算符(`>>`*可以对一个数的所有位进行指定位数的左移和右移,但是需要遵守下面定义的规则。
对一个数进行按位左移或按位右移,相当于对这个数进行乘以 2 或除以 2 的运算。将一个整数左移一位,等价于将这个数乘以 2同样地将一个整数右移一位等价于将这个数除以 2。
@ -91,7 +91,7 @@ let outputBits = firstBits ^ otherBits // 等于 00010001
以下这张图展示了 `11111111 << 1`(即把 `11111111` 向左移动 `1` 位),和 `11111111 >> 1`(即把 `11111111` 向右移动 `1` 位)的结果。蓝色的数字是被移位的,灰色的数字是被抛弃的,橙色的 `0` 则是被填充进来的:
![Art/bitshiftUnsigned_2x.png](https://docs.swift.org/swift-book/images/bitshiftUnsigned@2x.png)
![Art/bitshiftUnsigned_2x.png](https://docs.swift.org/swift-book/_images/bitshiftUnsigned_2x.png)
下面的代码演示了 Swift 中的移位运算:
@ -121,7 +121,7 @@ let blueComponent = pink & 0x0000FF // blueComponent 是 0x99即 153
同样的,绿色部分通过对 `0xCC6699``0x00FF00` 进行按位与运算得到 `0x006600`。然后将这个数向右移动 8 位,得到 `0x66`,也就是十进制数值的 `102`
最后,蓝色部分通过对 `0xCC6699``0x0000FF` 进行按位与运算得到 `0x000099`因为 `0x000099` 已经等于 `0x99` ,也就是十进制数值的 `153`,所以这个值就不需要再向右移位了
最后,蓝色部分通过对 `0xCC6699``0x0000FF` 进行按位与运算得到 `0x000099`这里不需要再向右移位,而 `0x000099` 也就是 `0x99` ,也就是十进制数值的 `153`
#### 有符号整数的移位运算 {#shifting-behavior-for-signed-integers}
@ -131,7 +131,7 @@ let blueComponent = pink & 0x0000FF // blueComponent 是 0x99即 153
其余的比特位(通常被称为*数值位*)存储了实际的值。有符号正整数和无符号数的存储方式是一样的,都是从 `0` 开始算起。这是值为 `4``Int8` 型整数的二进制位表现形式:
![Art/bitshiftSignedFour_2x.png](https://docs.swift.org/swift-book/images/bitshiftSignedFour@2x.png)
![Art/bitshiftSignedFour_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedFour_2x.png)
符号位为 `0`(代表这是一个“正数”),另外 7 位则代表了十进制数值 `4` 的二进制表示。
@ -139,21 +139,21 @@ let blueComponent = pink & 0x0000FF // blueComponent 是 0x99即 153
这是值为 `-4``Int8` 型整数的二进制表现形式:
![Art/bitshiftSignedMinusFour_2x.png](https://docs.swift.org/swift-book/images/bitshiftSignedMinusFour@2x.png)
![Art/bitshiftSignedMinusFour_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedMinusFour_2x.png)
这次的符号位为 `1`,说明这是一个负数,另外 7 个位则代表了数值 `124`(即 `128 - 4`)的二进制表示:
![Art/bitshiftSignedMinusFourValue_2x.png](https://docs.swift.org/swift-book/images/bitshiftSignedMinusFourValue@2x.png)
![Art/bitshiftSignedMinusFourValue_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedMinusFourValue_2x.png)
负数的表示通常被称为*二进制补码*。用这种方法来表示负数乍看起来有点奇怪,但它有几个优点。
首先,如果想对 `-1``-4` 进行加法运算,我们只需要对这两个数的全部 8 个比特位执行标准的二进制相加(包括符号位),并且将计算结果中超出 8 位的数值丢弃:
![Art/bitshiftSignedAddition_2x.png](https://docs.swift.org/swift-book/images/bitshiftSignedAddition@2x.png)
![Art/bitshiftSignedAddition_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedAddition_2x.png)
其次,使用二进制补码可以使负数的按位左移和右移运算得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 2每向右一位就将自身的数值除以 2。要达到此目的对有符号整数的右移有一个额外的规则当对有符号整数进行按位右移运算时遵循与无符号整数相同的规则但是对于移位产生的空白位使用*符号位*进行填充,而不是用 `0`
其次,使用二进制补码可以使负数的按位左移和右移运算得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 2每向右一位就将自身的数值除以 2。要达到此目的对有符号整数的右移有一个额外的规则当对有符号整数进行按位右移运算时遵循与无符号整数相同的规则但是对于移位产生的空白位使用*符号位*进行填充,而不是用 `0`
![Art/bitshiftSigned_2x.png](https://docs.swift.org/swift-book/images/bitshiftSigned@2x.png)
![Art/bitshiftSigned_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSigned_2x.png)
这个行为可以确保有符号整数的符号位不会因为右移运算而改变,这通常被称为*算术移位*。
@ -195,7 +195,7 @@ unsignedOverflow = unsignedOverflow &+ 1
`unsignedOverflow` 被初始化为 `UInt8` 所能容纳的最大整数(`255`,以二进制表示即 `11111111`)。然后使用溢出加法运算符(`&+`)对其进行加 `1` 运算。这使得它的二进制表示正好超出 `UInt8` 所能容纳的位数,也就导致了数值的溢出,如下图所示。数值溢出后,仍然留在 `UInt8` 边界内的值是 `00000000`,也就是十进制数值的 `0`
![Art/overflowAddition_2x.png](https://docs.swift.org/swift-book/images/overflowAddition@2x.png)
![Art/overflowAddition_2x.png](https://docs.swift.org/swift-book/_images/overflowAddition_2x.png)
当允许对一个无符号整数进行下溢运算时也会产生类似的情况。这里有一个使用溢出减法运算符(`&-`)的例子:
@ -208,9 +208,9 @@ unsignedOverflow = unsignedOverflow &- 1
`UInt8` 型整数能容纳的最小值是 `0`,以二进制表示即 `00000000`。当使用溢出减法运算符对其进行减 `1` 运算时,数值会产生下溢并被截断为 `11111111` 也就是十进制数值的 `255`
![Art/overflowUnsignedSubtraction_2x.png](https://docs.swift.org/swift-book/images/overflowUnsignedSubtraction@2x.png)
![Art/overflowUnsignedSubtraction_2x.png](https://docs.swift.org/swift-book/_images/overflowUnsignedSubtraction_2x.png)
溢出也会发生在有符号整型上。针对有符号整型的所有溢出加法或者减法运算都是按位运算的方式执行的,符号位也需要参与计算,正如 [按位左移、右移运算符](#bitwise-left-and-right-shift-operators) 所描述的。
溢出也会发生在有符号整型上。针对有符号整型的所有溢出加法或者减法运算都是按位运算的方式执行的,符号位也需要参与计算,正如 [按位左移、右移运算符](#bitwise_left_and_right_shift_operators) 所描述的。
```Swift
var signedOverflow = Int8.min
@ -219,9 +219,9 @@ signedOverflow = signedOverflow &- 1
// 此时 signedOverflow 等于 127
```
`Int8` 型整数能容纳的最小值是 `-128`,以二进制表示即 `10000000`。当使用溢出减法运算符对其进行减 `1` 运算时,符号位被翻转,得到二进制数值 `01111111`,也就是十进制数值的 `127`,这个值也是 `Int8` 型整所能容纳的最大值。
`Int8` 型整数能容纳的最小值是 `-128`,以二进制表示即 `10000000`。当使用溢出减法运算符对其进行减 `1` 运算时,符号位被翻转,得到二进制数值 `01111111`,也就是十进制数值的 `127`,这个值也是 `Int8` 型整所能容纳的最大值。
![Art/overflowSignedSubtraction_2x.png](https://docs.swift.org/swift-book/images/overflowSignedSubtraction@2x.png)
![Art/overflowSignedSubtraction_2x.png](https://docs.swift.org/swift-book/_images/overflowSignedSubtraction_2x.png)
对于无符号与有符号整型数值来说,当出现上溢时,它们会从数值所能容纳的最大数变成最小数。同样地,当发生下溢时,它们会从所能容纳的最小数变成最大数。
@ -266,7 +266,7 @@ signedOverflow = signedOverflow &- 1
因此计算结果为 `17`
有关 Swift 标准库提供的操作符信息,包括操作符优先级组和结性设置的完整列表,请参见 [操作符声明](https://developer.apple.com/documentation/swift/operator_declarations)。
有关 Swift 标准库提供的操作符信息,包括操作符优先级组和结性设置的完整列表,请参见 [操作符声明](https://developer.apple.com/documentation/swift/operator_declarations)。
> 注意
>
@ -276,7 +276,7 @@ signedOverflow = signedOverflow &- 1
类和结构体可以为现有的运算符提供自定义的实现。这通常被称为运算符*重载*。
下面的例子展示了如何让自定义的结构体支持加法运算符(`+`)。算术加法运算符是一个二元运算符,因为它是对两个值进行运算,同时它还可以称为中缀运算符,因为它出现在两个值中间。
下面的例子展示了如何让自定义的结构体支持加法运算符(`+`)。算术加法运算符是一个*二元运算符*,因为它是对两个值进行运算,同时它还可以称为*中缀*运算符,因为它出现在两个值中间。
例子中定义了一个名为 `Vector2D` 的结构体用来表示二维坐标向量 `(x, y)`,紧接着定义了一个可以将两个 `Vector2D` 结构体实例进行相加的*运算符函数*
@ -307,7 +307,7 @@ let combinedVector = vector + anotherVector
这个例子实现两个向量 `(3.01.0)``(2.04.0)` 的相加,并得到新的向量 `(5.05.0)`。这个过程如下图示:
![Art/vectorAddition_2x.png](https://docs.swift.org/swift-book/images/vectorAddition@2x.png)
![Art/vectorAddition_2x.png](https://docs.swift.org/swift-book/_images/vectorAddition_2x.png)
### 前缀和后缀运算符 {#prefix-and-postfix-operators}
@ -389,11 +389,32 @@ if twoThree == anotherTwoThree {
// 打印“These two vectors are equivalent.”
```
多数简单情况下,可以 Swift 合成等价运算符的实现,详见 [使用合成实现来采纳协议](./21_Protocols.md#adopting-a-protocol-using-a-synthesized-implementation)。
多数简单情况下,可以使用 Swift 为您提供的等价运算符默认实现。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.")
}
// 打印“These two vectors are also equivalent.”
```
## 自定义运算符 {#custom-operators}
除了实现标准运算符,在 Swift 中还可以声明和实现*自定义运算符*。可以用来自定义运算符的字符列表请参考 [运算符](../03_language_reference/02_Lexical_Structure.md#operators)。
除了实现标准运算符,在 Swift 中还可以声明和实现*自定义运算符*。可以用来自定义运算符的字符列表请参考 [运算符](../chapter3/02_Lexical_Structure.html#operators)。
新的运算符要使用 `operator` 关键字在全局作用域内进行定义,同时还要指定 `prefix``infix` 或者 `postfix` 修饰符:
@ -419,7 +440,7 @@ let afterDoubling = +++toBeDoubled
### 自定义中缀运算符的优先级 {#precedence-and-associativity-for-custom-infix-operators}
每个自定义中缀运算符都属于某个优先级组。优先级组指定了这个运算符相对于其他中缀运算符的优先级和结合性。[优先级和结合性](#precedence-and-associativity) 中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。
每个自定义中缀运算符都属于某个优先级组。优先级组指定了这个运算符相对于其他中缀运算符的优先级和结合性。[优先级和结合性](#precedence_and_associativity) 中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。
而没有明确放入某个优先级组的自定义中缀运算符将会被放到一个默认的优先级组内,其优先级高于三元运算符。
@ -438,155 +459,9 @@ let plusMinusVector = firstVector +- secondVector
// plusMinusVector 是一个 Vector2D 实例,并且它的值为 (4.0, -2.0)
```
这个运算符把两个向量的 `x` 值相加,同时从第一个向量的 `y` 中减去第二个向量的 `y` 。因为它本质上是属于“相加型”运算符,所以将它放置在 `+``-` 等默认中缀“相加型”运算符相同的优先级组中。关于 Swift 标准库提供的运算符,以及完整的运算符优先级组和结合性设置,请参考 [运算符声明](https://developer.apple.com/documentation/swift/operator_declarations)。而更多关于优先级组以及自定义操作符和优先级组的语法,请参考 [运算符声明](../03_language_reference/06_Declarations.md#operator-declaration)。
这个运算符把两个向量的 `x` 值相加,同时从第一个向量的 `y` 中减去第二个向量的 `y` 。因为它本质上是属于“相加型”运算符,所以将它放置在 `+``-` 等默认中缀“相加型”运算符相同的优先级组中。关于 Swift 标准库提供的运算符,以及完整的运算符优先级组和结合性设置,请参考 [运算符声明](https://developer.apple.com/documentation/swift/operator_declarations)。而更多关于优先级组以及自定义操作符和优先级组的语法,请参考 [运算符声明](./06_Declarations.md#operator_declaration)。
> 注意
>
> 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。
## 结果构造器 {#result-builder}
*结果构造器*是一种自定义类型,支持添加自然的声明式语法来创建类似列表或者树这样的嵌套数据。使用结果构造器的代码可以包含普通的 Swift 语法,例如用来处理判断条件的 `if`,或者处理重复数据的 `for`
下面的代码定义了一些类型用于绘制星星线段和文字线段。
```swift
protocol Drawable {
func draw() -> String
}
struct Line: Drawable {
var elements: [Drawable]
func draw() -> String {
return elements.map { $0.draw() }.joined(separator: "")
}
}
struct Text: Drawable {
var content: String
init(_ content: String) { self.content = content }
func draw() -> String { return content }
}
struct Space: Drawable {
func draw() -> String { return " " }
}
struct Stars: Drawable {
var length: Int
func draw() -> String { return String(repeating: "*", count: length) }
}
struct AllCaps: Drawable {
var content: Drawable
func draw() -> String { return content.draw().uppercased() }
}
```
`Drawable` 协议定义了绘制所需要遵循的方法,例如线或者形状都需要实现 `draw()` 方法。`Line` 结构体用来表示单行线段绘制,给大多数可绘制的元素提供了顶层容器。绘制 `Line` 时,调用了线段中每个元素的 `draw()`,然后将所有结果字符串连成单个字符串。`Text` 结构体包装了一个字符串作为绘制的一部分。`AllCaps` 结构体包装另一个可绘制元素,并将元素中所有文本转换为大写。
可以组合这些类型的构造器来创建一个可绘制元素。
```swift
let name: String? = "Ravi Patel"
let manualDrawing = Line(elements: [
Stars(length: 3),
Text("Hello"),
Space(),
AllCaps(content: Text((name ?? "World") + "!")),
Stars(length: 2),
])
print(manualDrawing.draw())
// 打印 "***Hello RAVI PATEL!**"
```
代码没问题,但是不够优雅。`AllCaps` 后面的括号嵌套太深,可读性不佳。`name``nil` 时使用 “World” 的兜底逻辑必须要依赖 `??` 操作符,这在逻辑复杂的时候会更难以阅读。如果还需要 `switch` 或者 `for` 循环来构建绘制的一部分,就更难以编写了。使用结果构造器可以将这样的代码重构得更像普通的 Swift 代码。
在类型的定义上加上 `@resultBuilder` 特性来定义一个结果构造器。比如下面的代码定义了允许使用声明式语法来描述绘制的结果构造器 `DrawingBuilder`
```swift
@resultBuilder
struct DrawingBuilder {
static func buildBlock(_ components: Drawable...) -> Drawable {
return Line(elements: components)
}
static func buildEither(first: Drawable) -> Drawable {
return first
}
static func buildEither(second: Drawable) -> Drawable {
return second
}
}
```
`DrawingBuilder` 结构体定义了三个方法来实现部分结果构造器语法。`buildBlock(_:)` 方法添加了在方法块中写多行代码的支持。它将方法块中的多个元素组合成 `Line``buildEither(first:)``buildEither(second:)` 方法添加了对 `if`-`else` 的支持。
可以在函数形参上应用 `@DrawingBuilder` 特性,它会将传递给函数的闭包转换为用结果构造器创建的值。例如:
```swift
func draw(@DrawingBuilder content: () -> Drawable) -> Drawable {
return content()
}
func caps(@DrawingBuilder content: () -> Drawable) -> Drawable {
return AllCaps(content: content())
}
func makeGreeting(for name: String? = nil) -> Drawable {
let greeting = draw {
Stars(length: 3)
Text("Hello")
Space()
caps {
if let name = name {
Text(name + "!")
} else {
Text("World!")
}
}
Stars(length: 2)
}
return greeting
}
let genericGreeting = makeGreeting()
print(genericGreeting.draw())
// 打印 "***Hello WORLD!**"
let personalGreeting = makeGreeting(for: "Ravi Patel")
print(personalGreeting.draw())
// 打印 "***Hello RAVI PATEL!**"
```
`makeGreeting(for:)` 函数将传入的 `name` 形参用于绘制个性化问候。`draw(_:)``caps(_:)` 函数都传入应用 `@DrawingBuilder` 特性的单一闭包实参。当调用这些函数时,要使用 `DrawingBuilder` 定义的特殊语法。Swift 将绘制的声明式描述转换为一系列 `DrawingBuilder` 的方法调用构造成最终传递进函数的实参值。例如Swift 将例子中的 `caps(_:)` 的调用转换为下面的代码:
```swift
let capsDrawing = caps {
let partialDrawing: Drawable
if let name = name {
let text = Text(name + "!")
partialDrawing = DrawingBuilder.buildEither(first: text)
} else {
let text = Text("World!")
partialDrawing = DrawingBuilder.buildEither(second: text)
}
return partialDrawing
}
```
Swift 将 `if-else` 方法块转换成调用 `buildEither(first:)``buildEither(second:)` 方法。虽然不会在自己的代码中调用这些方法,但是转换后的结果可以更清晰的理解在使用 `DrawingBuilder` 语法时 Swift 是如何进行转换的。
为了支持 `for` 循环来满足某些特殊的绘制语法,需要添加 `buildArray(_:)` 方法。
```swift
extension DrawingBuilder {
static func buildArray(_ components: [Drawable]) -> Drawable {
return Line(elements: components)
}
}
let manyStars = draw {
Text("Stars:")
for length in 1...3 {
Space()
Stars(length: length)
}
}
```
上面的代码中,使用 `for` 循环创建了一个绘制数组,`buildArray(_:)` 方法将该数组构建成 `Line`
有关 Swift 如何将构建器语法转换为构建器类型方法的完整信息,查看 [结果构造器](../03_language_reference/07_Attributes.md#resultbuilder)。

View File

@ -14,7 +14,7 @@ protocol Shape {
struct Triangle: Shape {
var size: Int
func draw() -> String {
var result: [String] = []
var result = [String]()
for length in 1...size {
result.append(String(repeating: "*", count: length))
}
@ -207,7 +207,7 @@ protoFlippedTriangle == sameThing // 错误
将协议类型作为函数的返回类型能更加灵活,函数只要返回遵循协议的类型即可。然而,更具灵活性导致牺牲了对返回值执行某些操作的能力。上面的例子就说明了为什么不能使用 == 运算符 —— 它依赖于具体的类型信息,而这正是使用协议类型所无法提供的。
这种方法的另一个问题在于,变换形状的操作不能嵌套。翻转三角形的结果是一个 `Shape` 类型的值,而 `protoFlip(_:)` 方法则将遵循 `Shape` 协议的类型作为形参,然而协议类型的值并不遵循这个协议;`protoFlip(_:)` 的返回值也并不遵循 `Shape` 协议。这就是说 `protoFlip(protoFlip(smallTriangle))` 这样的多重变换操作是非法的,因为经过翻转操作后的结果类型并不能作为 `protoFlip(_:)` 的形参。
这种方法的另一个问题在于,变换形状的操作不能嵌套。翻转三角形的结果是一个 `Shape` 类型的值,而 `protoFlip(_:)` 方法则将遵循 `Shape` 协议的类型作为形参,然而协议类型的值并不遵循这个协议;`protoFlip(_:)` 的返回值也并不遵循 `Shape` 协议。这就是说 `protoFlip(protoFlip(smallTriange))` 这样的多重变换操作是非法的,因为经过翻转操作后的结果类型并不能作为 `protoFlip(_:)` 的形参。
相比之下不透明类型则保留了底层类型的唯一性。Swift 能够推断出关联类型,这个特点使得作为函数返回值,不透明类型比协议类型有更大的使用场景。比如,下面这个例子是 [泛型](./22_Generics.md) 中讲到的 `Container` 协议:
@ -246,4 +246,4 @@ print(type(of: twelve))
// 输出 "Int"
```
`twelve` 的类型可以被推断出为 `Int` 这说明了类型推断适用于不透明类型。在 `makeOpaqueContainer(item:)` 的实现中,底层类型是不透明集合 `[T]`。在上述这种情况下,`T` 就是 `Int` 类型,所以返回值就是整数数组,而关联类型 `Item` 也被推断出为 `Int``Container` 协议中的 `subscript` 方法会返回 `Item`,这也意味着 `twelve` 的类型也被能推断出为 `Int`
`twelve` 的类型可以被推断出为 `Int` 这说明了类型推断适用于不透明类型。在 `makeOpaqueContainer(item:)` 的实现中,底层类型是不透明集合 `[T]`。在上述这种情况下,`T` 就是 `Int` 类型,所以返回值就是整数数组,而关联类型 `Item` 也被推断出为 `Int``Container` 协议中的 `subscipt` 方法会返回 `Item`,这也意味着 `twelve` 的类型也被能推断出为 `Int`

View File

@ -1,3 +1,3 @@
# Swift 语言教程
# Swift 教程
本章介绍了 Swift 的各种特性及其使用方法,是全书的核心部分。

View File

@ -19,14 +19,14 @@ Swift 语言相对较小,这是由于 Swift 代码中常用的类型、函数
> getter-setter 方法块语法
>
> *getter-setter 方法块* → { [getter 子句](./06_Declarations.md#getter-clause) [setter 子句](./06_Declarations.md#setter-clause)<sub>可选</sub> } | { [setter 子句](./06_Declarations.md#setter-clause) [getter 子句](./06_Declarations.md#getter-clause) }
> *getter-setter 方法块* → { [*getter 子句*](./06_Declarations.md#getter-clause) [*setter 子句*](./06_Declarations.md#setter-clause)<sub>可选</sub> } | { [*setter 子句*](./06_Declarations.md#setter-clause) [*getter 子句*](./06_Declarations.md#getter-clause) }
>
这个定义表明,一个 getter-setter 方法块可以由一个 getter 分句后跟一个可选的 setter 分句构成,然后用大括号括起来,或者由一个 setter 分句后跟一个 getter 分句构成,然后用大括号括起来。上述的语法产式等价于下面的两个语法产式,
> getter-setter 方法块语法
>
> getter-setter 方法块 → { [getter 子句](./06_Declarations.md#getter-clause) [setter 子句](./06_Declarations.md#setter-clause)<sub>可选</sub> }
> getter-setter 方法块 → { [*getter 子句*](./06_Declarations.md#getter-clause) [*setter 子句*](./06_Declarations.md#setter-clause)<sub>可选</sub> }
>
> getter-setter 方法块 → { [setter 子句](./06_Declarations.md#setter-clause) [getter 子句](./06_Declarations.md#getter-clause) }
> getter-setter 方法块 → { [*setter 子句*](./06_Declarations.md#setter-clause) [*getter 子句*](./06_Declarations.md#getter-clause) }
>

View File

@ -0,0 +1,630 @@
# 词法结构Lexical Structure
Swift 的*“词法结构lexical structure”* 描述了能构成该语言中有效符号token的字符序列。这些合法符号组成了语言中最底层的构建基块并在之后的章节中用于描述语言的其他部分。一个合法符号由一个标识符identifier、关键字keyword、标点符号punctuation、字面量literal或运算符operator组成。
通常情况下,通过考虑输入文本当中可能的最长子串,并且在随后将介绍的语法约束之下,根据随后将介绍的语法约束生成的,根据 Swift 源文件当中的字符来生成相应的“符号”。这种方法称为*“最长匹配longest match”*,或者*“最大适合maximal munch”*。
## 空白与注释 {#whitespace}
空白whitespace有两个用途分隔源文件中的符号以及帮助区分运算符属于前缀还是后缀参见 [运算符](#operators)在其他情况下空白则会被忽略。以下的字符会被当作空白空格U+0020、换行符U+000A、回车符U+000D、水平制表符U+0009、垂直制表符U+000B、换页符U+000C以及空字符U+0000
注释被编译器当作空白处理。单行注释由 `//` 开始直至遇到换行符U+000A或者回车符U+000D。多行注释由 `/*` 开始,以 `*/` 结束。注释允许嵌套,但注释标记必须匹配。
> 空白语法
>
> *空白* → [*空白项*](#whitespace-item) [*空白*](#whitespace)<sub>可选</sub>
>
#### whitespace-item {#whitespace-item}
>
> *空白项* → [*断行符*](#line-break)
>
> *空白项* → [*注释*](#comment)
>
> *空白项* → [*多行注释*](#multiline-comment)
>
> *空白项* → U+0000U+0009U+000BU+000C 或者 U+0020
>
>
#### line-break {#line-break}
>
> *断行符* → U+000A
>
> *断行符* → U+000D
>
> *断行符* → U+000D 接着是 U+000A
>
>
#### comment {#comment}
>
> *注释* → // [*注释内容*](#comment-text) [断行符*](#line-break)
>
>
#### multiline-comment {#multiline-comment}
>
> *多行注释* → `/*` [*多行注释内容*](#multiline-commnet-text) `*/`
>
>
#### comment-text {#comment-text}
>
> *注释内容* → [*注释内容项*](#comment-text-item) [*注释内容*](#comment-text)<sub>可选</sub>
>
>
#### comment-text-item {#comment-text-item}
>
> *注释内容项* → 任何 Unicode 标量值, 除了 U+000A 或者 U+000D
>
>
#### multiline-commnet-text {#multiline-commnet-text}
>
> *多行注释内容* → [*多行注释内容项*](#multiline-comment-text-item) [*多行注释内容*](#multiline-comment-text)<sub>可选</sub>
>
> *多行注释内容项* → [*多行注释*](#multiline-comment).
>
> *多行注释内容项* → [*注释内容项*](#comment-text-item)
>
> *多行注释内容项* → 任何 Unicode 标量值, 除了 `/*` 或者 `*/`
## 标识符 {#identifiers}
*标识符identifier* 可以由以下的字符开始:大写或小写的字母 `A``Z`、下划线(`_`、基本多文种平面Basic Multilingual Plane中非字符数字组合的 Unicode 字符以及基本多文种平面以外的非个人专用区字符。在首字符之后,允许使用数字和组合 Unicode 字符。
使用保留字作为标识符,需要在其前后增加反引号(`` ` ``)。例如,`class` 不是合法的标识符,但可以使用 `` `class` ``。反引号不属于标识符的一部分,`` `x` `` 和 `x` 表示同一标识符。
闭包中如果没有明确指定参数名称,参数将被隐式命名为 `$0`、`$1`、`$2` 等等。这些命名在闭包作用域范围内是合法的标识符。
> 标识符语法
>
> *标识符* → [*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)<sub>可选</sub>
>
> *标识符* → \`[*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)<sub>可选</sub>\`
>
> *标识符* → [*隐式参数名*](#implicit-parameter-name)
>
> *标识符列表* → [*标识符*](#identifier) | [*标识符*](#identifier) **,** [*标识符列表*](#identifier)
>
>
#### identifier-head {#identifier-head}
>
> *头部标识符* → 大写或小写字母 A - Z
>
> *头部标识符* → _
>
> *头部标识符* → U+00A8U+00AAU+00ADU+00AFU+00B2U+00B5或者 U+00B7U+00BA
>
> *头部标识符* → U+00BCU+00BEU+00C0U+00D6U+00D8U+00F6或者 U+00F8U+00FF
>
> *头部标识符* → U+0100U+02FFU+0370U+167FU+1681U+180D或者 U+180FU+1DBF
>
> *头部标识符* → U+1E00U+1FFF
>
> *头部标识符* → U+200BU+200DU+202AU+202EU+203FU+2040U+2054或者 U+2060U+206F
>
> *头部标识符* → U+2070U+20CFU+2100U+218FU+2460U+24FF或者 U+2776U+2793
>
> *头部标识符* → U+2C00U+2DFF 或者 U+2E80U+2FFF
>
> *头部标识符* → U+3004U+3007U+3021U+302FU+3031U+303F或者 U+3040U+D7FF
>
> *头部标识符* → U+F900U+FD3DU+FD40U+FDCFU+FDF0U+FE1F或者 U+FE30U+FE44
>
> *头部标识符* → U+FE47U+FFFD
>
> *头部标识符* → U+10000U+1FFFDU+20000U+2FFFDU+30000U+3FFFD或者 U+40000U+4FFFD
>
> *头部标识符* → U+50000U+5FFFDU+60000U+6FFFDU+70000U+7FFFD或者 U+80000U+8FFFD
>
> *头部标识符* → U+90000U+9FFFDU+A0000U+AFFFDU+B0000U+BFFFD或者 U+C0000U+CFFFD
>
> *头部标识符* → U+D0000U+DFFFD 或者 U+E0000U+EFFFD
>
> *标识符字符* → 数值 0 - 9
>
>
#### identifier-character {#identifier-character}
>
> *标识符字符* → U+0300U+036FU+1DC0U+1DFFU+20D0U+20FF或者 U+FE20U+FE2F
>
> *标识符字符* → [*头部标识符*](#identifier-head)
>
>
#### identifier-characters {#identifier-characters}
>
> *标识符字符组* → [*标识符字符*](#identifier-character) [*标识符字符组*](#identifier-characters)<sub>可选</sub>
>
>
#### implicit-parameter-name {#implicit-parameter-name}
>
> *隐式参数名* → **$** [*十进制数字列表*](#decimal-digit)
## 关键字和标点符号 {#keywords-and-punctuation}
下面这些被保留的关键字不允许用作标识符,除非使用反引号转义,具体描述请参考 [标识符](#identifiers)。除了 `inout``var` 以及 `let` 之外的关键字可以用作某个函数声明或者函数调用当中的外部参数名,无需添加反引号转义。当一个成员与一个关键字具有相同的名称时,不需要使用反引号来转义对该成员的引用,除非在引用该成员和使用该关键字之间存在歧义 - 例如,`self``Type``Protocol` 在显式的成员表达式中具有特殊的含义,因此它们必须在该上下文中使用反引号进行转义。
* 用在声明中的关键字: `associatedtype``class``deinit``enum``extension``fileprivate ``func``import``init``inout``internal``let``open``operator``private``protocol``public``static``struct``subscript``typealias` 以及 `var`
* 用在语句中的关键字:`break``case``continue``default``defer``do``else``fallthrough``for``guard``if``in``repeat``return``switch``where` 以及 `while`
* 用在表达式和类型中的关键字:`as``Any``catch``false``is``nil``rethrows``super``self``Self``throw``throws``true` 以及 `try `
* 用在模式中的关键字:`_`
* 以井字号(`#`)开头的关键字:`#available``#colorLiteral``#column``#else``#elseif``#endif``#error``#file``#fileLiteral``#function``#if``#imageLiteral ``#line``#selector``#sourceLocation`以及 `#warning`
* 特定上下文中被保留的关键字: `associativity``convenience``dynamic``didSet``final``get``infix``indirect``lazy``left``mutating``none``nonmutating``optional``override``postfix``precedence``prefix``Protocol``required``right``set``Type``unowned``weak` 以及 `willSet`。这些关键字在特定上下文之外可以被用做标识符。
以下符号被当作保留符号,不能用于自定义运算符: `(``)``{``}``[``]``.``,``:``;``=``@``#``&`(作为前缀运算符)、`->`、`` ` ``、`?`、`!`(作为后缀运算符)。
## 字面量 {#literal}
*字面量literal* 用来表示源码中某种特定类型的值,比如一个数字或字符串。
下面是字面量的一些示例:
```swift
42 // 整数字面量
3.14159 // 浮点数字面量
"Hello, world!" // 字符串字面量
true // 布尔值字面量
```
字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中Swift 使用了显式类型注解(`: Int8`)来推导出 `42` 这个整数字面量的类型是 `Int8`。如果没有可用的类型信息, Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整数字面量的默认类型是 `Int`,浮点数字面量的默认类型是 `Double`,字符串字面量的默认类型是 `String`,布尔值字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"` 的默认推导类型就是 `String`。
当为一个字面量值指定了类型注解的时候,这个标注的类型必须能通过这个字面量值实例化。也就是说,这个类型必须符合这些 Swift 标准库协议中的一个:整数字面量的 `IntegerLiteralConvertible` 协议、浮点数字面量的 `FloatingPointLiteralConvertible` 协议、字符串字面量的 `StringLiteralConvertible` 协议以及布尔值字面量的 `BooleanLiteralConvertible` 协议。比如,`Int8` 符合 `IntegerLiteralConvertible` 协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整数字面量 `42` 的类型注解。
> 字面量语法
>
> *字面量* → [*数值字面量*](#integer-literal) | [*字符串字面量*](#string-literal) | [*布尔值字面量*](#integer-literal) | [*nil 字面量*](#integer-literal)
>
> *数值字面量* → **-**<sub>可选</sub> [*整数字面量*](#integer-literal) | **-**<sub>可选</sub> [*浮点数字面量*](#floating-point-literal)
>
> *布尔值字面量* → **true** | **false**
>
> *nil 字面量* → **nil**
### 整数字面量{#integer-literal}
*整数字面量Integer Literals* 表示未指定精度整数的值。整数字面量默认用十进制表示,可以加前缀来指定其他的进制。二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。
十进制字面量包含数字 `0` 至 `9`。二进制字面量只包含 `0` 或 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F`(大小写均可)。
负整数的字面量在整数字面量前加负号 `-`,比如 `-42`。
整型字面面可以使用下划线(`_`)来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,这同样也会被系统所忽略,并不会影响字面量的值。
除非特别指定,整数字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数](../chapter2/01_The_Basics.md#integers)。
> 整数字面量语法
>
>
#### integer-literal {#integer-literal}
>
> *整数字面量* → [*二进制字面量*](#binary-literal)
>
> *整数字面量* → [*八进制字面量*](#octal-literal)
>
> *整数字面量* → [*十进制字面量*](#decimal-literal)
>
> *整数字面量* → [*十六进制字面量*](#hexadecimal-literal)
>
>
#### binary-literal {#binary-literal}
>
> *二进制字面量* → **0b** [*二进制数字*](#binary-digit) [*二进制字面量字符组*](#binary-literal-characters)<sub>可选</sub>
>
>
#### binary-digit {#binary-digit}
>
> *二进制数字* → 数值 0 到 1
>
> *二进制字面量字符* → [*二进制数字*](#binary-digit) | _
>
>
#### binary-literal-characters {#binary-literal-characters}
>
> *二进制字面量字符组* → [*二进制字面量字符*](#binary-literal-character) [*二进制字面量字符组*](#binary-literal-characters)<sub>可选</sub>
>
>
#### octal-literal {#octal-literal}
>
> *八进制字面量* → **0o** [*八进字数字*](#octal-digit) [*八进制字符组*](#octal-literal-characters)<sub>可选</sub>
>
>
#### octal-digit {#octal-digit}
>
> *八进字数字* → 数值 0 到 7
>
> *八进制字符* → [*八进字数字*](#octal-digit) | _
>
>
#### octal-literal-characters {#octal-literal-characters}
>
> *八进制字符组* → [*八进制字符*](#octal-literal-character) [*八进制字符组*](#octal-literal-characters)<sub>可选</sub>
>
>
#### decimal-literal {#decimal-literal}
>
> *十进制字面量* → [*十进制数字*](#decimal-digit) [*十进制字符组*](#decimal-literal-characters)<sub>可选</sub>
>
>
#### decimal-digit {#decimal-digit}
>
> *十进制数字* → 数值 0 到 9
>
>
#### decimal-literal-characters {#decimal-literal-characters}
>
> *十进制数字组* → [*十进制数字*](#decimal-digit) [*十进制数字组*](#decimal-literal-characters)<sub>可选</sub>
>
> *十进制字符* → [*十进制数字*](#decimal-digit) | _
>
> *十进制字符组* → [*十进制字符*](#decimal-literal-characters) [*十进制字符组*](#decimal-literal-characters)<sub>可选</sub>
>
>
#### hexadecimal-literal {#hexadecimal-literal}
>
> *十六进制字面量* → **0x** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
>
>
#### hexadecimal-digit {#hexadecimal-digit}
>
> *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F
>
> *十六进制字符* → [*十六进制数字*](#hexadecimal-digit) | _
>
>
#### hexadecimal-literal-characters {#hexadecimal-literal-characters}
>
> *十六进制字面量字符组* → [*十六进制字符*](#hexadecimal-literal-characters) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
### 浮点数字面量{#floating-point-literal}
*浮点数字面量Floating-point literals* 表示未指定精度浮点数的值。
浮点数字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。
十进制浮点数字面量由十进制数字串后跟小数部分或指数部分(或两者皆有)组成。十进制小数部分由小数点(`.`)后跟十进制数字串组成。指数部分由大写或小写字母 `e` 为前缀后跟十进制数字串组成,这串数字表示 `e` 之前的数量乘以 10 的几次方。例如:`1.25e2` 表示 1.25 x 10²也就是 `125.0`;同样,`1.25e2` 表示 1.25 x 10¯²也就是 `0.0125`。
十六进制浮点数字面量由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 为前缀后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 15 x 2²也就是 `60`;同样,`0xFp-2` 表示 15 x 2¯²也就是 `3.75`。
负数的浮点数字面量由负号(`-`)和浮点数字面量组成,例如 `-42.5`。
浮点数字面量允许使用下划线(`_`)来增强数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。
除非特别指定,浮点数字面量的默认推导类型为 Swift 标准库类型中的 `Double`,表示 64 位浮点数。Swift 标准库也定义了 `Float` 类型,表示 32 位浮点数。
> 浮点数字面量语法
>
>
#### floating-point-literal {#floating-point-literal}
>
> *浮点数字面量* → [*十进制字面量*](#decimal-literal) [*十进制分数*](#decimal-fraction)<sub>可选</sub> [*十进制指数*](#decimal-exponent)<sub>可选</sub>
>
> *浮点数字面量* → [*十六进制字面量*](#hexadecimal-literal) [*十六进制分数*](#hexadecimal-fraction)<sub>可选</sub> [*十六进制指数*](#hexadecimal-exponent)
>
>
#### decimal-fraction {#decimal-fraction}
>
> *十进制分数* → **.** [*十进制字面量*](#decimal-literal)
>
>
#### decimal-exponent {#decimal-exponent}
>
> *十进制指数* → [*十进制指数 e*](#floating-point-e) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal-literal)
>
>
#### hexadecimal-fraction {#hexadecimal-fraction}
>
> *十六进制分数* → **.** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
>
>
#### hexadecimal-exponent {#hexadecimal-exponent}
>
> *十六进制指数* → [*十六进制指数 p*](#floating-point-p) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal-literal)
>
>
#### floating-point-e {#floating-point-e}
>
> *十进制指数 e* → **e** | **E**
>
>
#### floating-point-p {#floating-point-p}
>
> *十六进制指数 p* → **p** | **P**
>
>
#### sign {#sign}
>
> *正负号* → **+** | **-**
### 字符串字面量 {#string-literal}
字符串字面量是被引号包括的一串字符组成。 单行字符串字面量被包在双引号中的一串字符组成,形式如下:
> "`字符`"
字符串字面量中不能包含未转义的双引号(`"`)、未转义的反斜线(`\`)、回车符、换行符。
多行字符串字面量被包在三个双引号中的一串字符组成,形式如下:
> """
> `字符`
> """
与单行字符串字面量不同的是,多行字符串字面量可以包含不转义的双引号("),回车以及换行。它不能包含三个未转义的连续双引号。
""" 之后的回车或者换行开始多行字符串字面量,不是字符串的一部分。 """ 之前回车或者换行结束字面量,也不是字符串的一部分。要让多行字符串字面量的开始或结束带有换行,就在第一行或者最后一行写一个空行。
多行字符串字面量可以使用任何空格或制表符组合进行缩进;这些缩进不会包含在字符串中。 """ 的结束符号决定了缩进:字面量中的任何一个非空行必须起始于多行字符串字面量结束符号的前面;空格和制表符不会被转换。你可以包在缩进后含额外的空格和制表符;这些空格和制表符会在字符串中出现。
多行字符串字面量中的一行结束使用规范化的换行符号。尽管你的源代码混用了回车和换行符,字符串中所有的行结束都必须一样.
在多行字符串字面量里, 在行末用反斜线(`\`)可以省略字符串行间中断。 反斜线之间的空白和行间中断也可以省略。 你可以在你的代码里用这种语法硬包裹多行字符串字面量,不需要改变产生的字符串的值。
可以在字符串字面量中使用的转义特殊符号如下:
* 空字符 `\0`
* 反斜线 `\\`
* 水平制表符 `\t`
* 换行符 `\n`
* 回车符 `\r`
* 双引号 `\"`
* 单引号 `\'`
* Unicode 标量 `\u{`n`}`n 为一到八位的十六进制数字
字符串字面量允许在反斜杠(`\`)后的括号 `()` 中插入表达式的值。插入表达式可以包含字符串字面量,但不能包含未转义的反斜线(`\`)、回车符以及换行符。
例如,以下所有字符串字面量的值都是相同的:
```swift
"1 2 3"
"1 2 \("3")"
"1 2 \(3)"
"1 2 \(1 + 2)"
let x = 3; "1 2 \(x)"
```
可以使用一对或多对扩展分隔符(#)包裹字符串进行分隔,被分隔的字符串的形式如下所示:
> \#"`characters`"#
>
> \#"""
>
> `characters`
>
> """#
特殊字符在被分隔符分隔的结果字符串中会展示为普通字符,而不是特殊字符。你可以使用扩展分隔符来创建一些具有特殊效果的字符串。例如,生成字符串插值,启动或终止转义序列(字符串)。
以下所示,由字符串字面量和扩展分隔符所创建的字符串是等价的:
```swift
let string = #"\(x) \ " \u{2603}"#
let escaped = "\\(x) \\ \" \\u{2603}"
print(string)
// Prints "\(x) \ " \u{2603}"
print(string == escaped)
// Prints "true"
```
如果在一个字符串中使用多对扩展分隔符,请不要在分隔符之间使用空格。
```swift
print(###"Line 1\###nLine 2"###) // OK
print(# # #"Line 1\# # #nLine 2"# # #) // Error
```
使用扩展分隔符创建的多行字符串字面量与普通多行字符串字面量具有相同的缩进要求。
字符串字面量的默认推导类型为 `String`。更多有关 `String` 类型的信息请参考 [字符串和字符](../chapter2/03_Strings_and_Characters.md) 以及 [*字符串结构参考*](https://developer.apple.com/documentation/swift/string)。
`` 操作符连接的字符型字面量是在编译时进行连接的。比如下面的 `textA``textB` 是完全一样的,`textA` 没有任何运行时的连接操作。
```swift
let textA = "Hello " + "world"
let textB = "Hello world"
```
> 字符串字面量语法
>
> *字符串字面量* → [*静态字符串字面量*](#static-string-literal) | [*插值字符串字面量*](#interpolated-string-literal)
>
> *字符串开分隔定界符* → [*字符串扩展分隔符*](#extended-string-literal-delimiter) **"**
>
> *字符串闭分隔定界符* → **"** [*字符串扩展分隔符*](#extended-string-literal-delimiter)<sub>可选</sub>
>
>
#### static-string-literal {#static-string-literal}
>
> *静态字符串字面量* → [*字符串开分隔定界符*](#extended-string-literal-delimiter) [*引用文本*](#quoted-text)<sub>可选</sub> [*字符串闭分隔定界符*](#extended-string-literal-delimiter)
>
> *静态字符串字面量* → [*多行字符串开分隔定界符*](#extended-string-literal-delimiter) [*多行引用文本*](#multiline-quoted-text)<sub>可选</sub> [*多行字符串闭分隔定界符*](#extended-string-literal-delimiter)
>
> *多行字符串开分隔定界符* → [*字符串扩展分隔符*](#extended-string-literal-delimiter) **"""**
>
> *多行字符串闭分隔定界符* → **"""** [*字符串扩展分隔符*](#extended-string-literal-delimiter)
>
>
#### extended-string-literal-delimiter {#extended-string-literal-delimiter}
>
> *字符串扩展分隔符* → **#** [*字符串扩展分隔符*](#extended-string-literal-delimiter)<sub>可选</sub>
>
>
#### quoted-text {#quoted-text}
>
> *引用文本* → [*引用文本项*](#quoted-text-item) [*引用文本*](#quoted-text)<sub>可选</sub>
>
>
#### quoted-text-item {#quoted-text-item}
>
> *引用文本项* → [*转义字符*](#escaped-character)
>
> *引用文本项* → 除了 **"**、**\\**、U+000A、U+000D 以外的所有 Unicode 字符
>
>
#### multiline-quoted-text {#multiline-quoted-text}
>
> *多行引用文本* → [*多行引用文本项*](#multiline-quoted-text-item) [*多行引用文本*](#multiline-quoted-text)<sub>可选</sub>
>
>
#### multiline-quoted-text-item {#multiline-quoted-text-item}
>
> *多行引用文本项* [*转义字符*](#escaped-character)<sub>可选</sub>
>
>
#### multiline-quoted-text {#multiline-quoted-text}
>
> *多行引用文本* → 除了 **\** 以外的任何Unicode标量值
>
> *多行引用文本* → [*转义换行*](#escaped-newline)
>
>
#### interpolated-string-literal {#interpolated-string-literal}
>
> *插值字符串字面量* → [*字符串开分隔定界符*](#extended-string-literal-delimiter) [*插值文本*](#interpolated-text)<sub>可选</sub> [*字符串闭分隔定界符*](#extended-string-literal-delimiter)
>
> *插值字符串字面量* → [*多行字符串开分隔定界符*](#extended-string-literal-delimiter) [*插值文本*](#interpolated-text)<sub>可选</sub> [*多行字符串闭分隔定界符*](#extended-string-literal-delimiter)
>
>
#### interpolated-text {#interpolated-text}
>
> *插值文本* → [*插值文本项*](#interpolated-text-item) [*插值文本*](#interpolated-text)<sub>可选</sub>
>
>
#### interpolated-text-item {#interpolated-text-item}
>
> *插值文本项* → **\\****(**[*表达式*](./04_Expressions.md)**)** | [*引用文本项*](#quoted-text-item)
>
> *多行插值文本* → [*多行插值文本项*](#multiline-quoted-text-item) [*多行插值文本*](#multiline-quoted-text)<sub>可选</sub>
>
> *多行插值文本项* → **\\(** [表达式](./04_Expressions.md) **)** | [多行引用文本项](#multiline-quoted-text-item)
>
>
#### escape-sequence {#escape-sequence}
>
> *转义序列* → **\\** [字符串扩展分隔符](#extended-string-literal-delimiter)
>
>
#### escaped-character {#escaped-character}
>
> *转义字符* → [*转义序列*](#escape-sequence) **0** | [*转义序列*](#escape-sequence) **\\** | [*转义序列*](#escape-sequence) **t** | [*转义序列*](#escape-sequence) **n** | [*转义序列*](#escape-sequence) **r** | [*转义序列*](#escape-sequence) **\"** | [*转义序列*](#escape-sequence) **'**
>
> *转义字符* → [*转义序列*](#escape-sequence) **u {** [*unicode 标量数字*](#unicode-scalar-digits) **}**
>
>
#### unicode-scalar-digits {#unicode-scalar-digits}
>
> *unicode 标量数字* → 一到八位的十六进制数字
>
>
#### escaped-newline {#escaped-newline}
>
> *转义换行符* → [*转义序列*](#escape-sequence) [*空白*](#whitespace)<sub>可选</sub> [*断行符*](#line-break)
## 运算符 {#operator}
Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/26_Advanced_Operators.md) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。
自定义运算符可以由以下其中之一的 ASCII 字符 `/``=``-``+``!``*``%``<``>``&``|``^``?` 以及 `~`,或者后面语法中规定的任一个 Unicode 字符(其中包含了*数学运算符*、*零散符号Miscellaneous Symbols* 以及印刷符号Dingbats之类的 Unicode 块)开始。在第一个字符之后,允许使用组合型 Unicode 字符。
您也可以以点号(`.`)开头来定义自定义运算符。这些运算符可以包含额外的点,例如 `.+.`。如果某个运算符不是以点号开头的,那么它就无法再包含另外的点号了。例如,`+.+` 就会被看作为一个 `+` 运算符后面跟着一个 `.+` 运算符。
虽然您可以用问号 `?` 来自定义运算符,但是这个运算符不能只包含单独的一个问号。此外,虽然运算符可以包含一个惊叹号 `!`,但是前缀运算符不能够以问号或者惊叹号开头。
> 注意
>
> 以下这些标记 `=`、`->`、`//`、`/*`、`*/`、`.`、`<`(前缀运算符)、`&`、`?`、`?`(中缀运算符)、`>`(后缀运算符)、`!` 、`?` 是被系统保留的。这些符号不能被重载,也不能用于自定义运算符。
运算符两侧的空白被用来区分该运算符是否为前缀运算符、后缀运算符或二元运算符。规则总结如下:
* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+++b``a +++ b` 当中的 `+++` 运算符会被看作二元运算符。
* 如果运算符只有左侧空白,将被看作一元前缀运算符。例如 `a +++b` 中的 `+++` 运算符会被看做是一元前缀运算符。
* 如果运算符只有右侧空白,将被看作一元后缀运算符。例如 `a+++ b` 中的 `+++` 运算符会被看作是一元后缀运算符。
* 如果运算符左侧没有空白并紧跟 `.`,将被看作一元后缀运算符。例如 `a+++.b` 中的 `+++` 运算符会被视为一元后缀运算符(即上式被视为 `a+++ .b` 而不是 `a +++ .b`)。
鉴于这些规则,运算符前的字符 `(``[``{`,运算符后的字符 `)``]``}`,以及字符 `,``;``:` 都被视为空白。
以上规则需注意一点,如果预定义运算符 `!``?` 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将 `?` 用作可选链式调用运算符,左侧必须无空白。如果用于条件运算符 `? :`,必须两侧都有空白。
在某些特定的设计中 ,以 `<``>` 开头的运算符会被分离成两个或多个符号,剩余部分可能会以同样的方式被再次分离。因此,在 `Dictionary<String, Array<Int>>` 中没有必要添加空白来消除闭合字符 `>` 的歧义。在这个例子中, 闭合字符 `>` 不会被视为单独的符号,因而不会被错误解析为 `>>` 运算符。
要学习如何自定义运算符,请参考 [自定义运算符](../chapter2/26_Advanced_Operators.md#custom_operators) 和 [运算符声明](./06_Declarations.md#operator_declaration)。要学习如何重载运算符,请参考 [运算符函数](../chapter2/26_Advanced_Operators.md#operator_functions)。
> 运算符语法
>
> *运算符* → [*头部运算符*](#operator-head) [*运算符字符组*](#operator-characters)<sub>可选</sub>
>
> *运算符* → [*头部点运算符*](#dot-operator-head) [*点运算符字符组*](#dot-operator-characters)
>
>
#### operator-head {#operator-head}
>
> *头部运算符* → **/** | **=** | **-** | **+** | **!** | __*__ | **%** | **<** | **>** | **&** | **|** | **^** | **~** | **?**
>
> *头部运算符* → U+00A1U+00A7
>
> *头部运算符* → U+00A9 或 U+00AB
>
> *头部运算符* → U+00AC 或 U+00AE
>
> *头部运算符* → U+00B0U+00B1U+00B6U+00BBU+00BFU+00D7或 U+00F7
>
> *头部运算符* → U+2016U+2017 或 U+2020U+2027
>
> *头部运算符* → U+2030U+203E
>
> *头部运算符* → U+2041U+2053
>
> *头部运算符* → U+2055U+205E
>
> *头部运算符* → U+2190U+23FF
>
> *头部运算符* → U+2500U+2775
>
> *头部运算符* → U+2794U+2BFF
>
> *头部运算符* → U+2E00U+2E7F
>
> *头部运算符* → U+3001U+3003
>
> *头部运算符* → U+3008U+3030
>
>
#### operator-character {#operator-character}
>
> *运算符字符* → [*头部运算符*](#operator-head)
>
> *运算符字符* → U+0300U+036F
>
> *运算符字符* → U+1DC0U+1DFF
>
> *运算符字符* → U+20D0U+20FF
>
> *运算符字符* → U+FE00U+FE0F
>
> *运算符字符* → U+FE20U+FE2F
>
> *运算符字符* → U+E0100U+E01EF
>
>
#### operator-characters {#operator-characters}
>
> *运算符字符组* → [*运算符字符*](#operator-character) [*运算符字符组*](#operator-characters)<sub>可选</sub>
>
>
#### dot-operator-head {#dot-operator-head}
>
> *头部点运算符* → **..**
>
>
#### dot-operator-character {#dot-operator-character}
>
> *点运算符字符* → **.** | [*运算符字符*](#operator-character)
>
>
#### dot-operator-characters {#dot-operator-characters}
>
> *点运算符字符组* → [*点运算符字符*](#dot-operator-character) [*点运算符字符组*](#dot-operator-characters)<sub>可选</sub>
>
> *二元运算符* → [*运算符*](#operator)
>
> *前缀运算符* → [*运算符*](#operator)
>
> *后缀运算符* → [*运算符*](#operator)

View File

@ -2,7 +2,7 @@
Swift 语言存在两种类型:命名型类型和复合型类型。*命名型类型*是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义类 `MyClass` 的实例拥有类型 `MyClass`。除了用户定义的命名型类型Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。
那些通常被其它语言认为是基本或原始的数据型类型,比如表示数字、字符和字符串的类型,实际上就是命名型类型,这些类型在 Swift 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照 [扩展](../02_language_guide/20_Extensions.md) 和 [扩展声明](./06_Declarations.md#extension-declaration) 中讨论的那样,声明一个扩展来增加它们的行为以满足你程序的需求。
那些通常被其它语言认为是基本或原始的数据型类型,比如表示数字、字符和字符串的类型,实际上就是命名型类型,这些类型在 Swift 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照 [扩展](../chapter2/20_Extensions.md) 和 [扩展声明](./06_Declarations.md#extension_declaration) 中讨论的那样,声明一个扩展来增加它们的行为以满足你程序的需求。
*复合型类型*是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型 `(Int, (Int, Int))` 包含两个元素:第一个是命名型类型 `Int`,第二个是另一个复合型类型 `(Int, Int)`
@ -41,14 +41,14 @@ Swift 语言存在两种类型:命名型类型和复合型类型。*命名型
> *类型***** [类型](#type) ****
## 类型注解 {#type-annotation-h}
*类型注解*显式地指定一个变量或表达式的类型。类型注解冒号 `:`)开始, 以类型结尾,比如下面两个例子:
*类型注解*显式地指定一个变量或表达式的类型。类型注解始于冒号 `:` 终于类型,比如下面两个例子:
```swift
let someTuple: (Double, Double) = (3.14159, 2.71828)
func someFunction(a: Int) { /* ... */ }
```
在第一个例子中,表达式 `someTuple` 的类型被指定为 `(Double, Double)`。在第二个例子中,函数 `someFunction``a` 的类型被指定为 `Int`
在第一个例子中,表达式 `someTuple` 的类型被指定为 `(Double, Double)`。在第二个例子中,函数 `someFunction` 的参 `a` 的类型被指定为 `Int`
类型注解可以在类型之前包含一个类型特性的可选列表。
@ -56,10 +56,10 @@ func someFunction(a: Int) { /* ... */ }
>
#### type-annotation {#type-annotation}
> *类型注解***:** [特性列表](./07_Attributes.md#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [类型](#type)
> *类型注解***:** [*特性列表*](./07_Attributes.md#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [*类型*](#type)
## 类型标识符 {#type-identifier-h}
*类型标识符*可以引用命名型类型,还可引用命名型或复合型类型的别名。
*类型标识符*引用命名型类型,还可引用命名型或复合型类型的别名。
大多数情况下,类型标识符引用的是与之同名的命名型类型。例如类型标识符 `Int` 引用命名型类型 `Int`,同样,类型标识符 `Dictionary<String, Int>` 引用命名型类型 `Dictionary<String, Int>`
@ -80,15 +80,16 @@ var someValue: ExampleModule.MyType
>
#### type-identifier {#type-identifier}
> *类型标识符* → [类型名称](#type-name) [泛型参子句](./09_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub> | [类型名称](#type-name) [泛型参子句](./09_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub> **.** [类型标识符](#type-identifier)
> *类型标识符* → [*类型名称*](#type-name) [*泛型参子句*](./09_Generic_Parameters_and_Arguments.md#generic_argument_clause)<sub>可选</sub> | [*类型名称*](#type-name) [*泛型参子句*](./09_Generic_Parameters_and_Arguments.md#generic_argument_clause)<sub>可选</sub> **.** [*类型标识符*](#type-identifier)
>
#### type-name {#type-name}
> *类型名称* → [标识符](./02_Lexical_Structure.md#identifier)
> *类型名称* → [*标识符*](./02_Lexical_Structure.md#identifier)
## 元组类型 {#tuple-type-h}
*元组类型*是使用括号括起来的零个或多个类型,类型间用逗号隔开。
你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符紧跟一个冒号 `(:)` 组成。[函数和多返回值](../02_language_guide/06_Functions.md#functions-with-multiple-return-values) 章节里有一个展示上述特性的例子。
你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符紧跟一个冒号 `(:)` 组成。[函数和多返回值](../chapter2/06_Functions.md#functions_with_multiple_return_values) 章节里有一个展示上述特性的例子。
当一个元组类型的元素有名字的时候,这个名字就是类型的一部分。
@ -105,37 +106,37 @@ someTuple = (left: 5, right: 5) // 错误:命名类型不匹配
>
#### tuple-type {#tuple-type}
> *元组类型***(** **)** | **(** [元组类型元素](#tuple-type-element) **,** [元组类型元素列表](#tuple-type-element-list) **)**
> *元组类型***(** **)** | **(** [*元组类型元素*](#tuple-type-element) **,** [*元组类型元素列表*](#tuple-type-element-list) **)**
>
#### tuple-type-element-list {#tuple-type-element-list}
> *元组类型元素列表* → [元组类型元素](#tuple-type-element) | [元组类型元素](#tuple-type-element) **,** [元组类型元素列表](#tuple-type-element-list)
> *元组类型元素列表* → [*元组类型元素*](#tuple-type-element) | [*元组类型元素*](#tuple-type-element) **,** [*元组类型元素列表*](#tuple-type-element-list)
>
#### tuple-type-element {#tuple-type-element}
> *元组类型元素* → [元素名](#element-name) [类型注解](#type-annotation) | [类型](#type)
> *元组类型元素* → [*元素名*](#element-name) [*类型注解*](#type-annotation) | [*类型*](#type)
>
#### element-name {#element-name}
> *元素名* → [标识符](./02_Lexical_Structure.md#identifier)
> *元素名* → [*标识符*](./02_Lexical_Structure.md#identifier)
>
## 函数类型 {#function-type-h}
*函数类型*表示一个函数、方法或闭包的类型,它由参类型和返回值类型组成,中间用箭头(`->`)隔开:
*函数类型*表示一个函数、方法或闭包的类型,它由参类型和返回值类型组成,中间用箭头(`->`)隔开:
> `参类型`->`返回值类型`
> `参类型`->`返回值类型`
*参类型*是由逗号间隔的类型列表。由于*返回值类型*可以是元组类型,所以函数类型支持多返回值的函数与方法。
*参类型*是由逗号间隔的类型列表。由于*返回值类型*可以是元组类型,所以函数类型支持多返回值的函数与方法。
你可以对参类型为 `() -> T`(其中 T 是任何类型)的函数使用 `autoclosure` 特性这会在调用侧隐式创建一个闭包。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被调用。以自动闭包做为参的函数类型的例子详见 [自动闭包](../02_language_guide/07_Closures.md#autoclosures)。
你可以对参类型为 `() -> T`(其中 T 是任何类型)的函数使用 `autoclosure` 特性这会自动将参数表达式转化为闭包,表达式的结果即闭包返回值。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被调用。以自动闭包做为参的函数类型的例子详见 [自动闭包](../chapter2/07_Closures.md#autoclosures)。
函数类型可以拥有个可变参数在*形参类型*中。从语法角度上讲,可变参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变参数的例子,请参阅 [可变参数](../02_language_guide/06_Functions.md#variadic-parameters)。
函数类型可以拥有个可变参数作为*参数类型*中的最后一个参数。从语法角度上讲,可变参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变参数的例子,请参阅 [可变参数](../chapter2/06_Functions.md#variadic_parameters)。
为了指定一个 `in-out` 参数,可以在参类型前加 `inout` 前缀。但是你不可以对可变参数或返回值类型使用 `inout`。关于这种参的详细讲解请参阅 [输入输出参数](../02_language_guide/06_Functions.md#in-out-parameters)。
为了指定一个 `in-out` 参数,可以在参类型前加 `inout` 前缀。但是你不可以对可变参数或返回值类型使用 `inout`。关于这种参的详细讲解请参阅 [输入输出参数](../chapter2/06_Functions.md#in_out_parameters)。
如果函数类型只有一个类型是元组类型的一个形参,那么元组类型在写函数类型的时候必须用圆括号括起来。比如说,`((Int, Int)) -> Void` 是接收一个元组 `(Int, Int)` 作为形并且不返回任何值的函数类型。与此相对,不加括号的 `(Int, Int) -> Void` 是一个接收两个 `Int` 作为形并且不返回任何值的函数类型。相似地,因为 `Void` 是空元组类型 `()` 的别名,函数类型 `(Void)-> Void``(()) -> ()` 是一样的 - 一个将空元组作为唯一参的函数。但这些类型和 `() -> ()` 是不一样的 - 一个无实参的函数
如果一个函数类型只有一个形式参数而且形式参数的类型是元组类型,那么元组类型在写函数类型的时候必须用圆括号括起来。比如说,`((Int, Int)) -> Void` 是接收一个元组 `(Int, Int)` 作为形式参数并且不返回任何值的函数类型。与此相对,不加括号的 `(Int, Int) -> Void` 是一个接收两个 `Int` 作为形式参数并且不返回任何值的函数类型。相似地,因为 `Void` 是空元组类型 `()` 的别名,函数类型 `(Void)-> Void``(()) -> ()` 是一样的 - 一个将空元组作为唯一参的函数。但这些类型和无变量的函数类型 `() -> ()` 是不一样的。
函数和方法中的实参名并不是函数类型的一部分。例如:
函数和方法中的变量名并不是函数类型的一部分。例如:
```swift
func someFunction(left: Int, right: Int) {}
@ -154,7 +155,7 @@ func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {}
f = functionWithDifferentNumberOfArguments // 错误
```
由于实参标签不是函数类型的一部分,你可以在写函数类型的时候省略它们。
由于变量标签不是函数类型的一部分,你可以在写函数类型的时候省略它们。
```swift
var operation: (lhs: Int, rhs: Int) -> Int // 错误
@ -162,16 +163,14 @@ var operation: (_ lhs: Int, _ rhs: Int) -> Int // 正确
var operation: (Int, Int) -> Int // 正确
```
如果一个函数类型包涵多个箭头(->),那么函数类型将从右向左进行组合。例如,函数类型 `(Int) -> (Int) -> Int` 可以理解为 `(Int) -> ((Int) -> Int)`,也就是说,该函数传入 `Int`,并返回另一个传入并返回 `Int` 的函数。
如果一个函数类型包涵多个箭头(->),那么函数类型将从右向左进行组合。例如,函数类型 `(Int) -> (Int) -> Int` 可以理解为 `(Int) -> ((Int) -> Int)`,也就是说,该函数类型的参数为 `Int` 类型,其返回类型是一个参数类型为 `Int`,返回类型为 `Int` 的函数。
使用函数类型的函数若要抛出重抛错误必须使用 `throws` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](./06_Declarations.md#throwing-functions-and-methods) 和 [重抛函数与方法](./06_Declarations.md#rethrowing-functions-and-methods)。
异步函数的函数类型必须使用 `async` 关键字来标记。 `async` 关键字也是函数类型的一部分,且同步函数是异步函数的子类型。因此,在使用异步函数的地方也可以使用同步函数。异步函数的相关描述见章节 [异步函数和方法](Declarations.xhtml#ID647)。
函数类型若要抛出错误就必须使用 `throws` 关键字来标记,若要重抛错误必须使用 `rethrows` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](./06_Declarations.md#throwing_functions_and_methods) 和 [重抛函数与方法](./06_Declarations.md#rethrowing_functions_and_methods)。
### 对非逃逸闭包的限制 {#Restrictions for Nonescaping Closures}
当非逃逸闭包函数是参时,不能存储在属性、变量或任何 `Any` 类型的常量中,因为这可能导致值的逃逸。
当非逃逸闭包函数是参时,不能存储在属性、变量或任何 `Any` 类型的常量中,因为这可能导致值的逃逸。
当非逃逸闭包函数是参时,不能作为参传递到另一个非逃逸闭包函数中。这样的限制可以让 Swift 在编译时就完成更的内存访问冲突检查,而不是在运行时。举个例子:
当非逃逸闭包函数是参时,不能作为参传递到另一个非逃逸闭包函数中。这样的限制可以让 Swift 在编译时就完成更的内存访问冲突检查,而不是在运行时。举个例子:
```swift
let external: (Any) -> Void = { _ in () }
@ -187,31 +186,37 @@ func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) {
}
```
在上面代码里,`takesTwoFunctions(first:second:)` 的两个参都是函数。它们都没有标记为 `@escaping`, 因此它们都是非逃逸的。
在上面代码里,`takesTwoFunctions(first:second:)` 的两个参都是函数。它们都没有标记为 `@escaping`, 因此它们都是非逃逸的。
上述例子里的被标记为“错误”的四个函数调用会产生编译错误。因为`first``second` 是非逃逸函数,它们不能够作为参被传递到另一个非逃逸函数。相对的, 标记“正确”的两个函数不会产生编译错误。这些函数调用不会违反限制,因为 `external` 不是 `takesTwoFunctions(first:second:)`参之一。
上述例子里的被标记为“错误”的四个函数调用会产生编译错误。因为参 `first``second` 是非逃逸函数,它们不能够作为参被传递到另一个非闭包函数。相对的, 标记“正确”的两个函数不会产生编译错误。这些函数调用不会违反限制,因为 `external` 不是 `takesTwoFunctions(first:second:)` 的参之一。
如果你需要避免这个限制,标记其中一个形参为逃逸,或者使用 `withoutActuallyEscaping(_:do:)` 函数临时转换其中一个非逃逸函数形参为逃逸函数。关于避免内存访问冲突,可以参阅 [内存安全](../02_language_guide/25_Memory_Safety.md)。
如果你需要避免这个限制,标记其中之一的参数为逃逸,或者使用 `withoutActuallyEscaping(_:do:)` 函数临时转换非逃逸函数的其中一个参数为逃逸函数。关于避免内存访问冲突,可以参阅 [内存安全](../chapter2/24_Memory_Safety.md)。
> 函数类型语法
>
#### function-type {#function-type}
> *函数类型* → [特性列表](./07_Attributes.md#attributes)<sub>可选</sub> [函数类型子句](#function-type-argument-clause) **async**<sub>可选</sub>**throws**<sub>可选</sub> **->** [类型](#type)
> *函数类型* → [*特性列表*](./07_Attributes.md#attributes)<sub>可选</sub> [*函数类型子句*](#function-type-argument-clause) **throws**<sub>可选</sub> **->** [*类型*](#type)
>
> *函数类型* → [*特性列表*](./07_Attributes.md#attributes)<sub>可选</sub> [*函数类型子句*](#function-type-argument-clause) **rethrows­** **->** [*类型*](#type)
>
#### function-type-argument-clause {#function-type-argument-clause}
> *函数类型子句***(**­ **)**­
> *函数类型子句***(** [函数类型参列表](#function-type-argument-list) *...*­ <sub>可选</sub> **)**
> *函数类型子句***(** [*函数类型参列表*](#function-type-argument-list) *...*­ <sub>可选</sub> **)**
>
#### function-type-argument-list {#function-type-argument-list}
> *函数类型参列表* → [函数类型](#function-type-argument) | [函数类型](#function-type-argument) [函数类型参列表](#function-type-argument-list)
> *函数类型参列表* → [*函数类型参数*](function-type-argument) | [*函数类型参数*](function-type-argument) [*函数类型参列表*](#function-type-argument-list)
>
#### function-type-argument {#function-type-argument}
> *函数类型实参* → [特性列表](./07_Attributes.md#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [类型](#type) | [实参标签](#argument-label) [类型注解](#type-annotation)
> *函数类型参数* → [*特性列表*](./07_Attributes.md#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [*类型*](#type) | [*参数标签*](#argument-label) [*类型注解*](#type-annotation)
>
#### argument-label {#argument-label}
> *参标签* → [标识符](./02_Lexical_Structure.md#identifier)
> *参标签* → [*标识符*](./02_Lexical_Structure.md#identifier)
>
## 数组类型 {#array-type-h}
Swift 语言为标准库中定义的 `Array<Element>` 类型提供了如下语法糖:
@ -236,13 +241,13 @@ var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
访问一个多维数组的元素时,最左边的下标指向最外层数组的相应位置元素。接下来往右的下标指向第一层嵌入的相应位置元素,依次类推。这就意味着,在上面的例子中,`array3D[0]``[[1, 2], [3, 4]]``array3D[0][1]``[3, 4]``array3D[0][1][1]` 则是 `4`
关于 Swift 标准库中 `Array` 类型的详细讨论,请参阅 [数组](../02_language_guide/04_Collection_Types.md#arrays)。
关于 Swift 标准库中 `Array` 类型的详细讨论,请参阅 [数组](../chapter2/04_Collection_Types.md#arrays)。
> 数组类型语法
>
#### array-type {#array-type}
> *数组类型***[** [类型](#type) **]**
> *数组类型***[** [*类型*](#type) **]**
>
## 字典类型 {#dictionary-type-h}
@ -264,13 +269,13 @@ let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]
字典中键的类型必须符合 Swift 标准库中的 `Hashable` 协议。
关于 Swift 标准库中 `Dictionary` 类型的详细讨论,请参阅 [字典](../02_language_guide/04_Collection_Types.md#dictionaries)。
关于 Swift 标准库中 `Dictionary` 类型的详细讨论,请参阅 [字典](../chapter2/04_Collection_Types.md#dictionaries)。
> 字典类型语法
>
#### dictionary-type {#dictionary-type}
> *字典类型***[** [类型](#type) **:** [类型](#type) **]**
> *字典类型***[** [*类型*](#type) **:** [*类型*](#type) **]**
>
## 可选类型 {#optional-type-h}
@ -294,19 +299,19 @@ optionalInteger! // 42
使用 `!` 运算符解包值为 `nil` 的可选值会导致运行错误。
你也可以使用可选链式调用和可选绑定来选择性在可选表达式上执行操作。如果值为 `nil`,不会执行任何操作,因此也就没有运行错误产生。
你也可以使用可选链式调用和可选绑定来选择性在可选表达式上执行操作。如果值为 `nil`,不会执行任何操作,因此也就没有运行错误产生。
更多细节以及更多如何使用可选类型的例子,请参阅 [可选类型](../02_language_guide/01_The_Basics.md#optionals)。
更多细节以及更多如何使用可选类型的例子,请参阅 [可选类型](../chapter2/01_The_Basics.md#optionals)。
> 可选类型语法
>
#### optional-type {#optional-type}
> *可选类型* → [类型](#type) **?**
> *可选类型* → [*类型*](#type) **?**
>
## 隐式解析可选类型 {#implicitly-unwrapped-optional-type-h}
Swift 语言定义后缀 `!` 作为标准库中命名类型 `Optional<Wrapped>` 的语法糖,其附加行为是在访问时自动解包。如果尝试对一个值为 `nil` 的可选类型进行隐式解包,将会产生运行时错误。除了隐式解包,下面两个声明等价:
当可以被访问时,Swift 语言定义后缀 `!` 作为标准库中命名类型 `Optional<Wrapped>` 的语法糖,来实现自动解包的功能。如果尝试对一个值为 `nil` 的可选类型进行隐式解包,将会产生运行时错误。因为隐式解包,下面两个声明等价:
```swift
var implicitlyUnwrappedString: String!
@ -331,23 +336,23 @@ let implicitlyUnwrappedArray: [Int]! // 正确
可以使用可选链式调用对隐式解析可选表达式选择性地执行操作。如果值为 `nil`,就不会执行任何操作,因此也不会产生运行错误。
关于隐式解析可选类型的更多细节,请参阅 [隐式解析可选类型](../02_language_guide/01_The_Basics.md#implicityly-unwrapped-optionals)。
关于隐式解析可选类型的更多细节,请参阅 [隐式解析可选类型](../chapter2/01_The_Basics.md#implicityly_unwrapped_optionals)。
> 隐式解析可选类型语法
>
#### implicitly-unwrapped-optional-type {#implicitly-unwrapped-optional-type}
> *隐式解析可选类型* → [类型](#type) **!**
> *隐式解析可选类型* → [*类型*](#type) **!**
>
## 协议合成类型 {#protocol-composition-type-h}
*协议合成类型*定义了一种遵循协议列表中每个指定协议的类型,或者一个现有类型的子类并遵循协议列表中每个指定协议。协议合成类型只能用在类型注解、泛型参子句和泛型 `where` 子句中指定类型。
*协议合成类型*定义了一种遵循协议列表中每个指定协议的类型,或者一个现有类型的子类并遵循协议列表中每个指定协议。协议合成类型只能用在类型注解、泛型参子句和泛型 `where` 子句中指定类型。
协议合成类型的形式如下:
> `Protocol 1` & `Procotol 2`
协议合成类型允许你指定一个值,其类型遵循多个协议的要求而不需要定义一个新的命名型协议来继承它想要符合的各个协议。比如,协议合成类型 `Protocol A & Protocol B & Protocol C` 等效于一个从 `Protocol A``Protocol B``Protocol C` 继承而来的新协议。同样的,你可以使用 `SuperClass & ProtocolA` 而不是声明一个新的协议来表示其类型是 `SuperClass` 的子类并遵循 `ProtocolA`
协议合成类型允许你指定一个值,其类型遵循多个协议的要求而不需要定义一个新的命名型协议来继承它想要符合的各个协议。比如,协议合成类型 `Protocol A & Protocol B & Protocol C` 等效于一个从 `Protocol A``Protocol B``Protocol C` 继承而来的新协议。同样的,你可以使用 `SuperClass & ProtocolA` 来取代申明一个新的协议作为 `SuperClass` 的子类并遵循 `ProtocolA`
协议合成列表中的每一项都必须是下面所列情况之一,列表中最多只能包含一个类:
@ -366,17 +371,17 @@ typealias PQR = PQ & Q & R
>
#### protocol-composition-type {#protocol-composition-type}
> *协议合成类型* → [协议标识符](#protocol-identifier) & [协议合成延续](#protocol-composition-continuation)
> *协议合成类型* → [*协议标识符*](#protocol-identifier) & [*协议合成延续*](#protocol-composition-continuation)
>
#### protocol-composition-continuation {#protocol-composition-continuation}
> *协议合成延续* → [协议标识符](#protocol-identifier) | [协议合成类型](#protocol-composition-type)
> *协议合成延续* → [*协议标识符*](#protocol-identifier) | [*协议合成类型*](#protocol-composition-type)
## 不透明类型 {#opaque-type-h}
*不透明类型*定义了一个遵循某个协议或者合成协议的类型,但不需要指明底层的具体类型。
*不透明类型*定义了遵循某个协议或者合成协议的类型,但不需要指明底层的具体类型。
不透明类型可以作为函数或下标的返回值类型,亦或是属性的类型使用。
不透明类型可以作为函数或下标的返回值,亦或是属性的类型使用。
不透明类型不能作为元组类型的一部分或范型类型使用,比如数组元素类型或者可选值的包装类型。
@ -384,11 +389,11 @@ typealias PQR = PQ & Q & R
> some `constraint`
*constraint* 可以是类类型,协议类型,协议组合类型或者 `Any`。只有当一个值的类型遵循该协议或者组合协议,或者从该类继承的时候,这个值才能作为这个不透明类型的实例使用。和不透明值交互的代码只能使用该值定义在 *constraint* 上的接口。
*constraint* 可以是类类型,协议类型,协议组合类型或者 `Any`只有当遵循该协议或者组合协议,或者从该类继承的时候,才能作为这个不透明类型的实例使用。和不透明值交互的代码只能使用该值定义在 *constraint* 上的接口。
协议声明里不能包括不透明类型。类不能使用不透明类型作为非 final 方法的返回值类型
协议声明里不能包括不透明类型。类不能使用不透明类型作为非 final 方法的返回值。
使用不透明类型作为返回值类型的函数必须返回单一公用底层类型。返回的类型可以包含函数范型类型参的一部分。例如,函数 `someFunction<T>()` 可以返回类型 `T` 或者 `Dictionary<String,T>` 的值。
使用不透明类型作为返回值的函数必须返回单一公用底层类型。返回的类型可以包含函数范型类型参的一部分。举个例子,函数 `someFunction<T>()` 可以返回类型 `T` 或者 `Dictionary<String,T>` 的值。
> 不透明类型语法
@ -444,39 +449,7 @@ let anotherInstance = metatype.init(string: "some string")
>
#### metatype-type {#metatype-type}
> *元类型* → [类型](#type) **.** **Type** | [类型](#type) **.** **Protocol**
## 任意类型{#any-type-h}
`Any` 类型可以包含其他类型的值。`Any` 可以用于以下类型实例的具体类型:
* 类、结构体或枚举
* 元类型,例如 `Int.self`
* 任意类型组成的元组
* 闭包或函数类型
```Swift
let mixed: [Any] = ["one", 2, true, (4, 5.3), { () -> Int in return 6 }]
```
当使用 `Any` 作为实例的具体类型时,访问其属性和方法之前需要转换其为已知类型。类型是 `Any` 的实例保留其原始的动态类型,并且可以通过任一类型转换操作符 `as``as?``as!` 进行类型转换。例如下文,使用 `as?` 将进行混合数组中第一个对象根据情况向下转换为 `String`
```swift
if let first = mixed.first as? String {
print("The first item, '\(first)', is a string.")
}
// 打印 "The first item, 'one', is a string."
```
关于转换的更多细节,请参阅 [类型转换](../02_language_guide/18_Type_Casting.md)。
`AnyObject` 协议和 `Any` 类型类似。所有类隐式遵循 `AnyObject`。和 `Any` 不一样,`AnyObject` 定义在 Swift 标准库中而不是在语言里。更多细节,请参阅 [类专属的协议](../02_language_guide/121_Protocols.md#class-only-protoco) 和 [`AnyObject`](https://developer.apple.com/documentation/swift/anyobject)。
> 任意类型语法
#### any-type{#any-type}
> *任意类型* → **Any**
> *元类型* → [*类型*](#type) **.** **Type** | [*类型*](#type) **.** **Protocol**
## 自身类型 {#self-type-h}
@ -484,14 +457,7 @@ if let first = mixed.first as? String {
在协议声明或者协议成员声明时,`Self` 类型引用的是最终遵循该协议的类型。
在结构体类或者枚举值声明时Self 类型引用的是声明的类型。在某个类型成员声明时Self 类型引用的是该类型。在类成员声明时,`Self` 只能在以下几种情况中出现:
* 作为方法的返回类型
* 作为只读下标的返回类型
* 作为只读计算属性的类型
* 在方法体中
举个例子,下面的代码演示了返回值类型是 `Self` 的实例方法 `f`
在结构体,类或者枚举值声明时,`Self` 类型引用的是声明的类型。在某个类型成员声明时,`Self` 类型引用的是该类型。在类成员声明时,`Self` 可以在方法的返回值和方法体中使用,但不能在其他上下文中使用。举个例子,下面的代码演示了返回值是 `Self` 的实例方法 `f`
```swift
class Superclass {
@ -527,24 +493,25 @@ print(type(of: z.f()))
*类型继承子句*被用来指定一个命名型类型继承自哪个类、采纳哪些协议。类型继承子句开始于冒号 `:`,其后是类型标识符列表。
类可以继承自单个超类,并遵循任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要遵循的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,请参阅 [继承](../02_language_guide/13_Inheritance.md)。
类可以继承自单个超类,并遵循任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要遵循的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,请参阅 [继承](../chapter2/13_Inheritance.md)。
其它命名型类型只能继承自或采纳一系列协议。协议类型可以继承自任意数量的其他协议。当一个协议类型继承自其它协议时,其它协议中定义的要求会被整合在一起,然后从当前协议继承的任意类型必须符合所有这些条件。
枚举定义中的类型继承子句可以是一系列协议,或者是指定单一的命名类型,此时枚举为其用例分配原始值。在枚举定义中使用类型继承子句来指定原始值类型的例子,请参阅 [原始值](../02_language_guide/08_Enumerations.md#raw-values)。
枚举定义中的类型继承子句可以是一系列协议,或者是指定单一的命名类型,此时枚举为其用例分配原始值。在枚举定义中使用类型继承子句来指定原始值类型的例子,请参阅 [原始值](../chapter2/08_Enumerations.md#raw_values)。
> 类型继承子句语法
>
#### type-inheritance-clause {#type-inheritance-clause}
> *类型继承子句***:** [类型继承列表](#type-inheritance-list)
#### type_inheritance_clause {#type-inheritance-clause}
> *类型继承子句***:** [*类型继承列表*](#type-inheritance-list)
>
#### type-inheritance-list {#type-inheritance-list}
> *类型继承列表* → [属性](./07_Attributes.md#attributes)<sub>可选</sub> [类型标识符](#type-identifier) | [属性](./07_Attributes.md#attributes)<sub>可选</sub> [类型标识符](#type-identifier) **,** [类型继承列表](#type-inheritance-list)
> *类型继承列表* → [*类型标识符*](#type-identifier) | [*类型标识符*](#type-identifier) **,** [*类型继承列表*](#type-inheritance-list)
>
## 类型推断
Swift 广泛使用*类型推断*,从而允许你省略代码中很多变量和表达式的类型或部分类型。比如,对于 `var x: Int = 0`,你可以完全省略类型而简写成 `var x = 0`,编译器会正确推断出 `x` 的类型 `Int`。类似的,当完整的类型可以从上下文推断出来时,你也可以省略类型的一部分。比如,如果你写了 `let dict: Dictionary = ["A" : 1]`,编译器能推断出 `dict` 的类型是 `Dictionary<String, Int>`
在上面的两个例子中,类型信息从表达式树的叶子节点传向根节点。也就是说,`var x: Int = 0``x` 的类型首先根据 `0` 的类型进行推断,然后将该类型信息传递到根节点(变量 `x`)。

View File

@ -8,41 +8,43 @@
> 语句语法
>
> *语句* → [表达式](./04_Expressions.md#expression) **;**<sub>可选</sub>
> *语句* → [*表达式*](./04_Expressions.md#expression) **;**<sub>可选</sub>
>
> *语句* → [声明](./06_Declarations.md#declaration) **;**<sub>可选</sub>
> *语句* → [*声明*](./06_Declarations.md#declaration) **;**<sub>可选</sub>
>
> *语句* → [循环语句](#loop-statement) **;**<sub>可选</sub>
> *语句* → [*循环语句*](#loop-statement) **;**<sub>可选</sub>
>
> *语句* → [分支语句](#branch-statement) **;**<sub>可选</sub>
> *语句* → [*分支语句*](#branch-statement) **;**<sub>可选</sub>
>
> *语句* → [带标签的语句](#labeled-statement) **;**<sub>可选</sub>
> *语句* → [*带标签的语句*](#labeled-statement) **;**<sub>可选</sub>
>
> *语句* → [控制转移语句](#control-transfer-statement) **;**<sub>可选</sub>
> *语句* → [*控制转移语句*](#control-transfer-statement) **;**<sub>可选</sub>
>
> *语句* → [defer 语句](#defer-statement) **;**<sub>可选</sub>
> *语句* → [*defer 语句*](#defer-statement) **;**<sub>可选</sub>
>
> *语句* → [do 语句](#do-statement) **:**<sub>可选</sub>
> *语句* → [*do 语句*](#do-statement) **:**<sub>可选</sub>
>
> *语句* → [编译器控制语句](#compiler-control-statement)
> *语句* → [*编译器控制语句*](#compiler-control-statement)
>
> *多条语句* → [*语句*](#statement) [*多条语句*](#statements)<sub>可选</sub>
>
> *多条语句* → [语句](#statement) [多条语句](#statements)<sub>可选</sub>
## 循环语句 {#loop-statements}
循环语句会根据特定的循环条件来重复执行代码块。Swift 提供三种类型的循环语句:`for-in` 语句、`while` 语句和 `repeat-while` 语句。
通过 `break` 语句和 `continue` 语句可以改变循环语句的控制流。有关这两条语句,详情参 [Break 语句](#break-statement) 和 [Continue 语句](#continue-statement)。
通过 `break` 语句和 `continue` 语句可以改变循环语句的控制流。有关这两条语句,详情参 [Break 语句](#break_statement) 和 [Continue 语句](#continue_statement)。
> 循环语句语法
>
>
#### loop-statement {#loop-statement}
> *循环语句* → [for-in 语句](#for-in-statement)
> *循环语句* → [*for-in 语句*](#for-in-statement)
>
> *循环语句* → [while 语句](#while-statement)
> *循环语句* → [*while 语句*](#while-statement)
>
> *循环语句* → [*repeat-while 语句*](#repeat-while-statement)
>
> *循环语句* → [repeat-while 语句](#repeat-while-statement)
### For-In 语句 {#for-in-statements}
@ -63,7 +65,8 @@ for item in collection {
>
#### for-in-statement {#for-in-statement}
> *for-in 语句***for** **case**<sub>可选</sub> [模式](./08_Patterns.md#pattern) **in** [表达式](./04_Expressions.md#expression) [where 子句](#where-clause)<sub>可选</sub> [代码块](./06_Declarations.md#code-block)
> *for-in 语句***for** **case**<sub>可选</sub> [*模式*](./08_Patterns.md#pattern) **in** [*表达式*](./04_Expressions.md#expression) [*where 子句*](#where-clause)<sub>可选</sub> [*代码块*](05_Declarations.md#code-block)
>
### While 语句 {#while-statements}
只要循环条件为真,`while` 语句就会重复执行代码块。
@ -83,29 +86,33 @@ while condition {
由于会在执行循环体中的语句前判断条件的值,因此循环体中的语句可能会被执行若干次,也可能一次也不会被执行。
条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding)。
条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
> while 语句语法
>
>
#### while-statement {#while-statement}
> *while 语句***while** [条件子句](#condition-clause) [代码块](./06_Declarations.md#code-block)
> *while 语句***while** [*条件子句*](#condition-clause) [*代码块*](./05_Declarations.md#code-block)
>
#### condition-clause {#condition-clause}
> *条件子句* → [表达式](./04_Expressions.md#expression) | [表达式](./04_Expressions.md#expression) **,** [条件列表](#condition-list)
> *条件子句* → [*表达式*](./04_Expressions.md#expression) | [*表达式*](./04_Expressions.md#expression) **,** [*条件列表*](#condition-list)
>
#### condition {#condition}
> *条件* → [表达式](./04_Expressions.md#expression) |[可用性条件](#availability-condition) | [case 条件](#case-condition) | [可选绑定条件](#optional-binding-condition)
> *条件* → [*表达式*](./04_Expressions.md#expression) |[*可用性条件*](#availability-condition) | [*case 条件*](#case-condition) | [*可选绑定条件*](#optional-binding-condition)
>
>
#### case-condition {#case-condition}
> *case 条件***case** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer)
> *case 条件***case** [*模式*](./08_Patterns.md#pattern) [*构造器*](./06_Declarations.md#initializer)
>
#### optional-binding-condition {#optional-binding-condition}
> *可选绑定条件***let** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer)<sub>可选</sub> | **var** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer)<sub>可选</sub>
> *可选绑定条件***let** [*模式*](./08_Patterns.md#pattern) [*构造器*](./06_Declarations.md#initializer) | **var** [*模式*](./08_Patterns.md#pattern) [*构造器*](./06_Declarations.md#initializer)
>
### Repeat-While 语句 {#repeat-while-statements}
`repeat-while` 语句至少执行一次代码块,之后只要循环条件为真,就会重复执行代码块。
@ -125,30 +132,32 @@ repeat {
由于条件的值是在循环体中的语句执行后才进行判断,因此循环体中的语句至少会被执行一次。
条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding)。
条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
> repeat-while 语句语法
>
>
#### repeat-while-statement {#repeat-while-statement}
> *repeat-while 语句***repeat** [代码块](./06_Declarations.md#code-block) **while** [表达式](./04_Expressions.md#expression)
> *repeat-while 语句***repeat** [*代码块*](./06_Declarations.md#code-block) **while** [*表达式*](./04_Expressions.md#expression)
>
## 分支语句 {#branch-statements}
分支语句会根据一个或者多个条件来执行指定部分的代码。分支语句中的条件将会决定程序如何分支以及执行哪部分代码。Swift 提供三种类型的分支语句:`if` 语句、 `guard` 语句和 `switch` 语句。
`if` 语句和 `switch` 语句中的控制流可以用 `break` 语句改变,请参阅 [Break 语句](#break-statement)。
`if` 语句和 `switch` 语句中的控制流可以用 `break` 语句改变,请参阅 [Break 语句](#break_statement)。
> 分支语句语法
>
>
#### branch-statement {#branch-statement}
> *分支语句* → [if 语句](#if-statement)
> *分支语句* → [*if 语句*](#if-statement)
>
> *分支语句* → [guard 语句](#guard-statement)
> *分支语句* → [*guard 语句*](#guard-statement)
>
> *分支语句* → [*switch 语句*](#switch-statement)
>
> *分支语句* → [switch 语句](#switch-statement)
### If 语句 {#if-statements}
`if` 语句会根据一个或多个条件来决定执行哪一块代码。
@ -185,17 +194,19 @@ if condition 1 {
}
```
`if` 语句中条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding)。
`if` 语句中条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
> if 语句语法
>
>
#### if-statement {#if-statement}
> *if 语句***if** [条件子句](#condition-clause) [代码块](./06_Declarations.md#code-block) [else 子句](#else-clause)<sub>可选</sub>
> *if 语句***if** [*条件子句*](#condition-clause) [*代码块*](05_Declarations.md#code-block) [*else 子句*](#else-clause)<sub>可选</sub>
>
#### else-clause {#else-clause}
> *else 子句***else** [代码块](./06_Declarations.md#code-block) | **else** [if 语句](#if-statement)
> *else 子句***else** [*代码块*](./06_Declarations.md#code-block) | **else** [*if 语句*](#if-statement)
>
### Guard 语句 {#guard-statements}
如果一个或者多个条件不成立,可用 `guard` 语句来退出当前作用域。
@ -208,7 +219,7 @@ guard condition else {
}
```
`guard` 语句中条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件也可以是一条可选绑定,请参阅 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding)。
`guard` 语句中条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件也可以是一条可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
`guard` 语句中进行可选绑定的任何常量或者变量,其可用范围从声明开始直到作用域结束。
@ -219,17 +230,17 @@ guard condition else {
* `continue`
* `throw`
关于控制转移语句,请参阅 [控制转移语句](#control-transfer-statements)。关于 `Never` 返回类型的函数,请参阅 [永不返回的函数](./06_Declarations.md#rethrowing-functions-and-methods)。
关于控制转移语句,请参阅 [控制转移语句](#control_transfer_statements)。关于 `Never` 返回类型的函数,请参阅 [永不返回的函数](05_Declarations.md#rethrowing_functions_and_methods)。
> guard 语句语法
>
>
#### guard-statement {#guard-statement}
> *guard 语句***guard** [条件子句](#condition-clause) **else** [代码块](./06_Declarations.md#code-block)
> *guard 语句***guard** [*条件子句*](#condition-clause) **else** [*代码块*] (05_Declarations.md#code-block)
>
### Switch 语句 {#switch-statements}
`switch` 语句会根据控制表达式的值来决定执行哪部分代码。
`switch` 语句的形式如下:
@ -250,7 +261,7 @@ default:
`switch` 语句会先计算*控制表达式*的值,然后与每一个 `case` 的模式进行匹配。如果匹配成功,程序将会执行对应的 `case` 中的语句。另外,每一个 `case` 的作用域都不能为空,也就是说在每一个 `case` 的冒号(`:`)后面必须至少有一条语句。如果你不想在匹配到的 `case` 中执行代码,只需在该 `case` 中写一条 `break` 语句即可。
可以用作控制表达式的值是十分灵活的。除了标量类型外,如 `Int``Character`,你可以使用任何类型的值,包括浮点数、字符串、元组、自定义类型的实例和可选类型。控制表达式的值还可以用来匹配枚举类型中的成员值或是检查该值是否包含在指定的 `Range` 中。关于如何在 `switch` 语句中使用这些类型,请参阅 [控制流](../02_language_guide/05_Control_Flow.md) 一章中的 [Switch](../02_language_guide/05_Control_Flow.md#switch)。
可以用作控制表达式的值是十分灵活的。除了标量类型外,如 `Int``Character`,你可以使用任何类型的值,包括浮点数、字符串、元组、自定义类型的实例和可选类型。控制表达式的值还可以用来匹配枚举类型中的成员值或是检查该值是否包含在指定的 `Range` 中。关于如何在 `switch` 语句中使用这些类型,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章中的 [Switch](../chapter2/05_Control_Flow.md#switch)。
每个 `case` 的模式后面可以有一个 `where` 子句。`where` 子句由 `where` 关键字紧跟一个提供额外条件的表达式组成。因此,当且仅当控制表达式匹配一个 `case` 的模式且 `where` 子句的表达式为真时,`case` 中的语句才会被执行。在下面的例子中,控制表达式只会匹配包含两个相等元素的元组,例如 `(1, 1)`
@ -269,7 +280,7 @@ case let (x, y) where x == y:
在 Swift 中,`switch` 语句中控制表达式的每一个可能的值都必须至少有一个 `case` 与之对应。在某些无法面面俱到的情况下(例如,表达式的类型是 `Int`),你可以使用 `default` 分支满足该要求。
#### 对未来枚举的 `case` 进行 `switch` {#future-case}
非冻结枚举(`nonfronzen enumeration`)是一种特殊的枚举类型,它可能在未来会增加新的枚举 `case`,即使这时候你已经编译并且发布了你的应用,所以在 switch 非冻结枚举前需要深思熟虑。当一个库的作者们把一个枚举标记为非冻结的,这意味着他们保留了增加新的枚举 `case` 的权利,并且任何和这个枚举交互的代码都*必须*在无需重新编译的条件下能够处理那些未来可能新加入的 `case` 。只有演进模式的库代码、标准库代码、用 Swift 实现的 Apple 框架C 以及 Objective-C 代码才能够声明非冻结枚举。更多关于冻结和非冻结枚举的内容,请参阅 [冻结](./07_Attributes.md#frozen)
非冻结枚举(`nonfronzen enumeration`)是一种特殊的枚举类型,它可能在未来会增加新的枚举 `case`,即使这时候你已经编译并且发布了你的应用,所以在 switch 非冻结枚举前需要深思熟虑。当一个库的作者们把一个枚举标记为非冻结的,这意味着他们保留了增加新的枚举 `case` 的权利,并且任何和这个枚举交互的代码都要在不需要重新编译的条件下能够处理那些未来可能新加入的 `case` 。只有那些标准库,比如用 Swift 实现的苹果的一些框架C 以及 Objective-C 代码才能够声明非冻结枚举。你在 Swift 中声明的枚举不能是非冻结的
当你对未来枚举进行 switch 时,你总是需要有一个 `default case`,即使每种枚举类型都已经有对应的 `case` 了。你可以在 default 前标注 `@unknown`,意思是这个 `case` 应该只匹配未来加入的枚举 `case`。如果你的 `default case` 中匹配了任何在编译时就能确定的枚举 `case`Swift 会抛出一个警告。这可以很好地提醒你库的作者已经新增了一种 `case`,并且你还没有去处理。
@ -292,81 +303,95 @@ case .suppressed:
#### 不存在隐式落入
当匹配到的 `case` 中的代码执行完毕后,`switch` 语句会直接退出,而不会继续执行下一个 `case` 。这就意味着,如果你想执行下一个 `case`,需要显式地在当前 `case` 中使用 `fallthrough` 语句。关于 `fallthrough` 语句的更多信息,请参阅 [Fallthrough 语句](#fallthrough-statements)。
当匹配到的 `case` 中的代码执行完毕后,`switch` 语句会直接退出,而不会继续执行下一个 `case` 。这就意味着,如果你想执行下一个 `case`,需要显式地在当前 `case` 中使用 `fallthrough` 语句。关于 `fallthrough` 语句的更多信息,请参阅 [Fallthrough 语句](#fallthrough_statements)。
> switch 语句语法
>
>
#### switch-statement {#switch-statement}
> *switch 语句***switch** [表达式](./04_Expressions.md#expression) **{** [switch-case 列表](#switch-cases)<sub>可选</sub> **}**
> *switch 语句***switch** [*表达式*](./04_Expressions.md#expression) **{** [*switch-case 列表*](#switch-cases)<sub>可选</sub> **}**
>
#### switch-cases {#switch-cases}
> *switch case 列表* → [switch-case](#switch-case) [switch-case 列表](#switch-cases)<sub>可选</sub>
> *switch case 列表* → [*switch-case*](#switch-case) [*switch-case 列表*](#switch-cases)<sub>可选</sub>
>
#### switch-case {#switch-case}
> *switch case* → [case 标签](#case-label) [多条语句](#statements) | [default 标签](#default-label) [多条语句](#statements) | [conditional-switch-case](#conditional-switch-case-label)
> *switch case* → [*case 标签*](#case-label) [*多条语句*](#statements) | [*default 标签*](#default-label) [*多条语句*](#statements) | [*conditional-switch-case*](#conditional-switch-case-label)
>
#### case-label {#case-label}
> *case 标签* → [属性](#switch-case-attributes-label)<sub>可选</sub> **case** [case 项列表](#case-item-list) **:**
> *case 标签* → [*属性*](#switch-case-attributes-label)<sub>可选</sub> **case** [*case 项列表*](#case-item-list) **:**
>
#### case-item-list {#case-item-list}
> *case 项列表* → [模式](./08_Patterns.md#pattern) [where 子句](#where-clause)<sub>可选</sub> | [模式](./08_Patterns.md#pattern) [where 子句](#where-clause)<sub>可选</sub> **,** [case 项列表](#case-item-list)
> *case 项列表* → [*模式*](./08_Patterns.md#pattern) [*where 子句*](#where-clause)<sub>可选</sub> | [*模式*](07_Patterns.md#pattern) [*where 子句*](#where-clause)<sub>可选</sub> **,** [*case 项列表*](#case-item-list)
>
#### default-label {#default-label}
> *default 标签* → [属性](#switch-case-attributes-label)<sub>可选</sub> **default** **:**
> *default 标签* → [*属性*](#switch-case-attributes-label)<sub>可选</sub> **default** **:**
>
>
#### where-clause {#where-clause}
> *where-clause***where** [where 表达式](#where-expression)
> *where-clause***where** [*where 表达式*](#where-expression)
>
#### where-expression {#where-expression}
> *where-expression* → [表达式](./04_Expressions.md#expression)
> *where-expression* → [*表达式*](./04_Expressions.md#expression)
>
>
#### grammar-conditional-switch-case {#grammar-conditional-switch-case}
> *conditional-switch-case* → [switch-if-directive-clause](#switch-case-attributes-label) [switch-elseif-directive-clauses](#switch-case-attributes-label) <sub>可选</sub> [switch-else-directive-clause](#switch-case-attributes-label) <sub>可选</sub> [endif-directive](#switch-case-attributes-label)
#### grammar_conditional-switch-case {#grammar-conditional-switch-case}
> *conditional-switch-case* → [*switch-if-directive-clause*](#switch-case-attributes-label) [*switch-elseif-directive-clauses*](#switch-case-attributes-label) <sub>可选</sub> [*switch-else-directive-clause*](#switch-case-attributes-label) <sub>可选</sub> [*endif-directive*](#switch-case-attributes-label)
>
#### grammar-switch-if-directive-clause {#grammar-switch-if-directive-clause}
> *switch-if-directive 语句* → [if-directive](#switch-case-attributes-label) [compilation-condition](#switch-case-attributes-label) [switch-cases](#switch-case-attributes-label) <sub>可选</sub>
#### grammar_switch-if-directive-clause {#grammar-switch-if-directive-clause}
> *switch-if-directive 语句* → [*if-directive*](#switch-case-attributes-label) [*compilation-condition*](#switch-case-attributes-label) [*switch-cases*](#switch-case-attributes-label) <sub>可选</sub>
>
#### grammar-switch-elseif-directive-clauses {#grammar-switch-elseif-directive-clauses}
> *switch-elseif-directive 语句(复数)* → [elseif-directive-clause](#switch-case-attributes-label) [switch-elseif-directive-clauses](#switch-case-attributes-label)<sub>可选</sub>
#### grammar_switch-elseif-directive-clauses {#grammar-switch-elseif-directive-clauses}
> *switch-elseif-directive 语句(复数)* → [*elseif-directive-clause*](#switch-case-attributes-label) [*switch-elseif-directive-clauses*](#switch-case-attributes-label)<sub>可选</sub>
>
#### grammar-switch-elseif-directive-clause {#grammar-switch-elseif-directive-clause}
> *switch-elseif-directive 语句* → [elseif-directive](#switch-case-attributes-label) [compilation-condition](#switch-case-attributes-label) [switch-cases](#switch-case-attributes-label)<sub>可选</sub>
#### grammar_switch-elseif-directive-clause {#grammar-switch-elseif-directive-clause}
> *switch-elseif-directive 语句* → [*elseif-directive*](#switch-case-attributes-label) [*compilation-condition*](#switch-case-attributes-label) [*switch-cases*](#switch-case-attributes-label)<sub>可选</sub>
>
#### grammar-switch-else-directive-clause {#grammar-switch-else-directive-clause}
> *switch-else-directive 语句* → [else-directive](#switch-case-attributes-label) [switch-cases](#switch-case-attributes-label) <sub>可选</sub>
#### grammar_switch-else-directive-clause {#grammar-switch-else-directive-clause}
> *switch-else-directive 语句* → [*else-directive*](#switch-case-attributes-label) [*switch-cases*](#switch-case-attributes-label) <sub>可选</sub>
>
## 带标签的语句 {#labeled-statements}
你可以在循环语句或 `switch` 语句前面加上标签,它由标签名和紧随其后的冒号(`:`)组成。在 `break``continue` 后面跟上标签名可以显式地在循环语句或 `switch` 语句中改变相应的控制流。关于这两条语句用法,请参阅 [Break 语句](#break-statement) 和 [Continue 语句](#continue-statement)。
你可以在循环语句或 `switch` 语句前面加上标签,它由标签名和紧随其后的冒号(`:`)组成。在 `break``continue` 后面跟上标签名可以显式地在循环语句或 `switch` 语句中改变相应的控制流。关于这两条语句用法,请参阅 [Break 语句](#break_statement) 和 [Continue 语句](#continue_statement)。
标签的作用域在该标签所标记的语句内。可以嵌套使用带标签的语句,但标签名必须唯一。
关于使用带标签的语句的例子,请参阅 [控制流](../02_language_guide/05_Control_Flow.md) 一章中的 [带标签的语句](../02_language_guide/05_Control_Flow.md#labeled-statements)。
关于使用带标签的语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章中的 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。
> 带标签的语句语法
>
>
#### labeled-statement {#labeled-statement}
> *带标签的语句* → [语句标签](#statement-label) [循环语句](#grammar-loop-statement)
> *带标签的语句* → [*语句标签*](#statement-label) [*循环语句*](#grammar_loop-statement)
>
> *带标签的语句* → [语句标签](#statement-label) [if 语句](#if-statement)
> *带标签的语句* → [*语句标签*](#statement-label) [*if 语句*](#if-statement)
>
> *带标签的语句* → [语句标签](#statement-label) [switch 语句](#switch-statement)
> *带标签的语句* → [*语句标签*](#statement-label) [*switch 语句*](#switch-statement)
>
> > *带标签的语句* → [*语句标签*](#statement-label) [*do 语句*](#sdo-statement)
>
> > *带标签的语句* → [语句标签](#statement-label) [do 语句](#sdo-statement)
#### statement-label {#statement-label}
> *语句标签* → [标签名称](#label-name) **:**
> *语句标签* → [*标签名称*](#label-name) **:**
>
#### label-name {#label-name}
> *标签名称* → [标识符](./02_Lexical_Structure.md#identifier)
> *标签名称* → [*标识符*](./02_Lexical_Structure.md#identifier)
>
## 控制转移语句 {#control-transfer-statements}
控制转移语句能够无条件地把控制权从一片代码转移到另一片代码从而改变代码执行的顺序。Swift 提供五种类型的控制转移语句:`break` 语句、`continue` 语句、`fallthrough` 语句、`return` 语句和 `throw` 语句。
@ -376,15 +401,16 @@ case .suppressed:
>
#### control-transfer-statement {#control-transfer-statement}
> *控制转移语句* → [break 语句](#break-statement)
> *控制转移语句* → [*break 语句*](#break-statement)
>
> *控制转移语句* → [continue 语句](#continue-statement)
> *控制转移语句* → [*continue 语句*](#continue-statement)
>
> *控制转移语句* → [fallthrough 语句](#fallthrough-statement)
> *控制转移语句* → [*fallthrough 语句*](#fallthrough-statement)
>
> *控制转移语句* → [return 语句](#return-statement)
> *控制转移语句* → [*return 语句*](#return-statement)
>
> *控制转移语句* → [*throw 语句*](#throw-statement)
>
> *控制转移语句* → [throw 语句](#throw-statement)
### Break 语句 {#break-statement}
`break` 语句用于终止循环语句、`if` 语句或 `switch` 语句的执行。使用 `break` 语句时,可以只写 `break` 这个关键词,也可以在 `break` 后面跟上标签名,像下面这样:
@ -392,6 +418,7 @@ case .suppressed:
> break
>
> break `label name`
>
`break` 语句后面带标签名时,可用于终止由这个标签标记的循环语句、`if` 语句或 `switch` 语句的执行。
@ -399,14 +426,15 @@ case .suppressed:
无论哪种情况,控制权都会被转移给被终止的控制流语句后面的第一行语句。
关于使用 `break` 语句的例子,请参阅 [控制流](../02_language_guide/05_Control_Flow.md) 一章的 [Break](../02_language_guide/05_Control_Flow.md#break) 和 [带标签的语句](../02_language_guide/05_Control_Flow.md#labeled-statements)。
关于使用 `break` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [Break](../chapter2/05_Control_Flow.md#break) 和 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。
> break 语句语法
>
>
#### break-statement {#break-statement}
> *break 语句***break** [标签名称](#label-name)<sub>可选</sub>
> *break 语句***break** [*标签名称*](#label-name)<sub>可选</sub>
>
### Continue 语句 {#continue-statement}
`continue` 语句用于终止循环中当前迭代的执行,但不会终止该循环的执行。使用 `continue` 语句时,可以只写 `continue` 这个关键词,也可以在 `continue` 后面跟上标签名,像下面这样:
@ -414,6 +442,7 @@ case .suppressed:
> continue
>
> continue `label name`
>
`continue` 语句后面带标签名时,可用于终止由这个标签标记的循环中当前迭代的执行。
@ -423,21 +452,22 @@ case .suppressed:
`for` 语句中,`continue` 语句执行后,增量表达式还是会被计算,这是因为每次循环体执行完毕后,增量表达式都会被计算。
关于使用 `continue` 语句的例子,请参阅 [控制流](../02_language_guide/05_Control_Flow.md) 一章的 [Continue](../02_language_guide/05_Control_Flow.md#continue) 和 [带标签的语句](../02_language_guide/05_Control_Flow.md#labeled-statements)。
关于使用 `continue` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [Continue](../chapter2/05_Control_Flow.md#continue) 和 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。
> continue 语句语法
>
>
#### continue-statement {#continue-statement}
> *continue 语句***continue** [标签名称](#label-name)<sub>可选</sub>
> *continue 语句***continue** [*标签名称*](#label-name)<sub>可选</sub>
>
### Fallthrough 语句 {#fallthrough-statements}
`fallthrough` 语句用于在 `switch` 语句中转移控制权。`fallthrough` 语句会把控制权从 `switch` 语句中的一个 `case` 转移到下一个 `case`。这种控制权转移是无条件的,即使下一个 `case` 的模式与 `switch` 语句的控制表达式的值不匹配。
`fallthrough` 语句可出现在 `switch` 语句中的任意 `case` 中,但不能出现在最后一个 `case` 中。同时,`fallthrough` 语句也不能把控制权转移到使用了值绑定的 `case`
关于在 `switch` 语句中使用 `fallthrough` 语句的例子,请参阅 [控制流](../02_language_guide/05_Control_Flow.md) 一章的 [控制转移语句](../02_language_guide/05_Control_Flow.md#control-transfer-statements)。
关于在 `switch` 语句中使用 `fallthrough` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [控制转移语句](../chapter2/05_Control_Flow.md#control_transfer_statements)。
> fallthrough 语句语法
>
@ -445,6 +475,7 @@ case .suppressed:
#### fallthrough-statement {#fallthrough-statement}
> *fallthrough 语句* → **fallthrough**
>
### Return 语句 {#return-statements}
`return` 语句用于在函数或方法的实现中将控制权转移到调用函数或方法,接着程序将会从调用位置继续向下执行。
@ -454,13 +485,15 @@ case .suppressed:
> return
>
> return `expression`
>
`return` 语句后面带表达式时表达式的值将会返回给调用函数或方法。如果表达式的值的类型与函数或者方法声明的返回类型不匹配Swift 则会在返回表达式的值之前将表达式的值的类型转换为返回类型。
> 注意
>
>
> 正如 [可失败构造器](./06_Declarations.md#failable-initializers) 中所描述的,`return nil` 在可失败构造器中用于表明构造失败。
> 正如 [可失败构造器](./06_Declarations.md#failable_initializers) 中所描述的,`return nil` 在可失败构造器中用于表明构造失败。
>
而只写 `return` 时,仅仅是从该函数或方法中返回,而不返回任何值(也就是说,函数或方法的返回类型为 `Void` 或者说 `()`)。
@ -469,10 +502,11 @@ case .suppressed:
>
#### return-statement {#return-statement}
> *return 语句***return** [表达式](./04_Expressions.md#expression)<sub>可选</sub>
> *return 语句***return** [*表达式*](./04_Expressions.html#expression)<sub>可选</sub>
### Throw 语句 {#throw-statements}
### Throw 语句 {#throw-statements}
`throw` 语句出现在抛出函数或者抛出方法体内,或者类型被 `throws` 关键字标记的闭包表达式体内。
`throw` 语句使程序在当前作用域结束执行,并向外围作用域传播错误。抛出的错误会一直传递,直到被 `do` 语句的 `catch` 子句处理掉。
@ -480,17 +514,19 @@ case .suppressed:
`throw` 语句由 `throw` 关键字紧跟一个表达式组成,如下所示:
> throw `expression`
>
表达式的结果必须符合 `ErrorType` 协议。
关于如何使用 `throw` 语句的例子,请参阅 [错误处理](../02_language_guide/17_Error_Handling.md) 一章的 [用 throwing 函数传递错误](../02_language_guide/17_Error_Handling.md#propagating-errors-using-throwing-functions)。
关于如何使用 `throw` 语句的例子,请参阅 [错误处理](../chapter2/17_Error_Handling.md) 一章的 [用 throwing 函数传递错误](../chapter2/17_Error_Handling.md#propagating_errors_using_throwing_functions)。
> throw 语句语法
>
>
#### throw-statement {#throw-statement}
> *throw 语句***throw** [表达式](./04_Expressions.md#expression)
> *throw 语句***throw** [*表达式*](./04_Expressions.md#expression)
>
## Defer 语句 {#defer-statements}
`defer` 语句用于在退出当前作用域之前执行代码。
@ -526,7 +562,8 @@ f()
>
#### defer-statement {#defer-statement}
> *延迟语句***defer** [代码块](./06_Declarations.md#code-block)
> *延迟语句***defer** [*代码块*](./06_Declarations.md#code-block)
>
## Do 语句 {#do-statements}
`do` 语句用于引入一个新的作用域,该作用域中可以含有一个或多个 `catch` 子句,`catch` 子句中定义了一些匹配错误条件的模式。`do` 语句作用域内定义的常量和变量只能在 `do` 语句作用域内使用。
@ -543,41 +580,30 @@ do {
statements
} catch pattern 2 where condition {
statements
} catch pattern 3, pattern 4 where condition {
statements
} catch {
statements
}
```
`do` 代码块中的任何语句抛出了错误,程序会跳转到第一个能模式匹配该错误的 `catch` 子句。如果没有任何子句匹配,错误会传递到外层作作用域。如果错误在最顶层依旧没有被处理,程序执行会因为运行时错误而停止
如同 `switch` 语句,编译器会判断 `catch` 子句是否有遗漏。如果 `catch` 子句没有遗漏,则认为错误已被处理。否则,错误会自动传递到外层作用域,被某个 `catch` 子句处理掉或者被用 `throws` 关键字声明的抛出函数继续向外抛出。
拥有多个模式匹配的 `catch` 子句只需其中一个匹配到错误即可。如果 `catch` 子句拥有多个模式匹配,所有的模式必须包含相同的绑定常量或变量,并且每个 `catch` 子句里所有绑定的变量或常量的类型必须相同。
`switch` 语句,编译器会判断 `catch` 子句是否有遗漏。如果 `catch` 子句没有遗漏,则认为错误已被处理。否则,错误会自动传递到外作用域,被某个 `catch` 子句处理掉或者被用 `throws` 关键字声明的抛出函数继续向外抛出
为了确保错误已经被处理,可以让 `catch` 子句使用匹配所有错误的模式,如通配符模式(`_`)。如果一个 `catch` 子句不指定一种具体模式,`catch` 子句会匹配任何错误,并绑定到名为 `error` 的局部常量。有关在 `catch` 子句中使用模式的更多信息,请参阅 [模式](./08_Patterns.md)。
关于如何在 `do` 语句中使用一系列 `catch` 子句的例子,请参阅 [错误处理](../02_language_guide/17_Error_Handling.md#handling-errors)。
关于如何在 `do` 语句中使用一系列 `catch` 子句的例子,请参阅 [错误处理](../chapter2/17_Error_Handling.md#handling_errors)。
> do 语句语法
>
>
#### do-statement {#do-statement}
> *do 语句***do** [代码块](./06_Declarations.md#code-block) [多条 catch 子句](#catch-clauses)<sub>可选</sub>
> *do 语句***do** [*代码块*](./06_Declarations.md#code-block) [*多条 catch 子句*](#catch-clauses)<sub>可选</sub>
>
#### catch-clauses {#catch-clauses}
> *多条 catch 子句* → [catch 子句](#catch-clause) [多条 catch 子句](#catch-clauses)<sub>可选</sub>
> *多条 catch 子句* → [*catch 子句*](#catch-clause) [*多条 catch 子句*](#catch-clauses)<sub>可选</sub>
>
#### catch-clause {#catch-clause}
> *catch 子句***catch** [模式](./08_Patterns.md#pattern)<sub>可选</sub> [where 子句](#where-clause)<sub>可选</sub> [代码块](./06_Declarations.md#code-block)
#### catch-pattern-list{#catch-pattern-list}
> *catch 模式列表* → [catch 模式](#catch-pattern) | [catch 模式](#catch-pattern) [catch 模式列表](#catch-pattern-list)
#### catch-pattern{#catch-pattern}
> *catch 模式* → [模式](./08_Patterns.md#pattern) [where 子句](./05_Statements.md#where-clause)<sub>可选</sub>
> *catch 子句***catch** [*模式*](./08_Patterns.md#pattern)<sub>可选</sub> [*where 子句*](#where-clause)<sub>可选</sub> [*代码块*](05_Declarations.md#code-block)
>
## 编译器控制语句 {#compiler-control-statements}
编译器控制语句允许程序改变编译器的行为。Swift 有三种编译器控制语句:条件编译语句、线路控制语句和编译时诊断语句。
@ -587,14 +613,14 @@ do {
>
#### compiler-control-statement {#compiler-control-statement}
> *编译器控制语句* → [条件编译语句](#grammar-conditional-compilation-block)
> *编译器控制语句* → [*条件编译语句*](#grammar_conditional-compilation-block)
>
> *编译器控制语句* → [线路控制语句](#line-control-statement)
> *编译器控制语句* → [*线路控制语句*](#line-control-statement)
>
> *编译器控制语句* → [*诊断语句*](#grammar_diagnostic-statement)
>
> *编译器控制语句* → [诊断语句](#grammar-diagnostic-statement)
### 条件编译代码块 {#Conditional-Compilation-Block}
条件编译代码块可以根据一个或多个配置来有条件地编译代码。
每一个条件编译代码块都以 `#if` 开始,`#endif` 结束。如下:
@ -611,12 +637,12 @@ statements
| 函数 | 可用参数 |
| --- | --- |
| `os()` | `OSX``iOS``watchOS``tvOS``Linux``Windows` |
| `arch()` | `i386``x86_64``arm``arm64` |
| `os()` | `OSX`, `iOS`, `watchOS`, `tvOS`, `Linux` |
| `arch()` | `i386`, `x86_64`, `arm`, `arm64` |
| `swift()` | `>=``<` 后跟版本号 |
| `compiler()` | `>=``<` 后跟版本号 |
| `canImport()` | 模块名 |
| `targetEnvironment()` | `simulator``macCatalyst` |
| `targetEnvironment()` | 模拟器 |
`swift()``compiler()` 之后的版本号包含有主版本号,可选副版本号,可选补丁版本号类似,并且用(`.`)来分隔。在比较符和版本号之间不能有空格,版本号与前面的函数相对应,比如 `compiler()` 对应的就是这个编译器的版本号,`swift()` 对应的就是你要编译的 `Swift` 语言的版本号。举个简单的例子,如果你在使用 `Swift 5` 的编译器,想编译 `Swift 4.2` ,可以看下面的例子:
@ -635,14 +661,15 @@ print("Compiled with the Swift 5 compiler or later in a Swift mode earlier than
// 打印 "Compiled with the Swift 5 compiler or later in a Swift mode earlier than 5"
```
`canImport()` 条件传入的实参是模块的名字,这个模块可能并不是每个平台上都存在的。该模块的命名可以包含 `.` 符号。使用它可以检测是否可以导入这个模块,但实际上并没有导入。如果模块存在就返回 `true`否则返回 `false`
`canImport()` 后面跟的变量是模块的名字,这里这个模块可能并不是每个平台上都存在的。使用它检测是否可以导入这个模块,如果模块存在就返回 `true` 否则返回 `false`
`targetEnvironment()` 条件在特殊环境编译时返回 `true`,否则返回 `false`
`targetEnvironment()` 当为模拟器编译时返回 `true`,否则返回 `false`
> 注意
>
>
> `arch(arm)` 平台检测函数在 ARM 64 位设备上不会返回 `true`。如果代码在 32 位的 iOS 模拟器上编译,`arch(i386)` 平台检测函数会返回 `true`
>
你可以使用逻辑操作符 `&&``||``!` 来组合多个编译配置,还可以使用圆括号来进行分组。
@ -661,27 +688,34 @@ statements to compile if both compilation conditions are false
> 注意
>
>
> 即使没有被编译,上面编译配置中的每个语句仍然会被解析。然而,唯一的例外是编译配置语句中包含 `swift()` `compiler()` 条件:这时仅当编译器版本和语言版本匹配时,语句才会被解析。这种设定能确保旧的编译器不会尝试去解析新 Swift 版本的语法。
> 即使没有被编译,编译配置中的语句仍然会被解析。然而,唯一的例外是编译配置语句中包含语言版本检测函数:仅当 `Swift` 编译器版本和语言版本检测函数中指定的版本号匹配时,语句才会被解析。这种设定能确保旧的编译器不会尝试去解析新 Swift 版本的语法。
>
关于在条件编译块中如何包装显式成员表达式,请参见 [显式成员表达式](#Expressions.xhtml#ID400) 章节。
#### build-config-statement {#build-config-statement}
> 条件编译代码块语法
>
>
#### grammar-conditional-compilation-block {#grammar-conditional-compilation-block}
> *条件编译代码块* → [if-directive 语句](#grammar-if-directive-clause) [elseif-directive 语句(复数)](#grammar-elseif-directive-clauses)<sub>可选</sub> [else-directive 语句](#grammar-else-directive-clause)<sub>可选</sub> [endif-directive](#grammar-endif-directive)
#### grammar_conditional-compilation-block {#grammar-conditional-compilation-block}
> *条件编译代码块* → [*if-directive 语句*](#grammar_if-directive-clause) [*elseif-directive 语句(复数)*](#grammar_elseif-directive-clauses)<sub>可选</sub> [*else-directive 语句*](#grammar_else-directive-clause)<sub>可选</sub> [*endif-directive*](#grammar_endif-directive)
>
#### grammar-if-directive-clause {#grammar-if-directive-clause}
> *if-directive 语句* → [if-directive](#grammar-if-directive) [编译条件](#compilation-condition) [语句(复数)](#statements)<sub>可选</sub>
#### grammar_if-directive-clause {#grammar-if-directive-clause}
> *if-directive 语句* → [*if-directive*](#grammar_if-directive) [*编译条件*](#compilation-condition) [*语句(复数)*](#statements)<sub>可选</sub>
>
#### grammar-elseif-directive-clauses {#grammar-elseif-directive-clauses}
> *elseif-directive 语句(复数)* → [elseif-directive 语句](#grammar-elseif-directive-clause) [elseif-directive 语句(复数)](#grammar-elseif-directive-clauses)
#### grammar_elseif-directive-clauses {#grammar-elseif-directive-clauses}
> *elseif-directive 语句(复数)* → [*elseif-directive 语句*](#grammar_elseif-directive-clause) [*elseif-directive 语句(复数)*](#grammar_elseif-directive-clauses)
>
#### grammar-elseif-directive-clauses {#grammar-elseif-directive-clauses}
> *elseif-directive 语句* → [elseif-directive](#grammar-elseif-directive) [编译条件](#compilation-condition) [语句(复数)](#statements)<sub>可选</sub>
#### grammar_elseif-directive-clauses {#grammar-elseif-directive-clauses}
> *elseif-directive 语句* → [*elseif-directive*](#grammar_elseif-directive) [*编译条件*](#compilation-condition) [*语句(复数)*](#statements)<sub>可选</sub>
>
#### grammar-else-directive-clause {#grammar-else-directive-clause}
> *else-directive 语句* → [else-directive](#grammar-else-directive) [语句(复数)](#statements)<sub>可选</sub>
#### grammar_else-directive-clause {#grammar-else-directive-clause}
> *else-directive 语句* → [*else-directive*](#grammar_else-directive) [*语句(复数)*](#statements)<sub>可选</sub>
>
> *if-directive***#if**
@ -691,73 +725,88 @@ statements to compile if both compilation conditions are false
> *else-directive***#else**
>
> *endif-directive***#endif**
>
#### compilation-condition {#compilation-condition}
> *编译条件* → [平台条件](#grammar-platform-condition)
> *编译条件* → [*平台条件*](#grammar_platform-condition)
>
> *编译条件* → [标识符](./02_Lexical_Structure.md#identifier)
> *编译条件* → [*标识符*](./02_Lexical_Structure.md#identifier)
>
> *编译条件* → [布尔值字面量](./02_Lexical_Structure.md#boolean-literal)
> *编译条件* → [*布尔值字面量*](./02_Lexical_Structure.md#boolean-literal)
>
> *编译条件***(** [编译条件](#compilation-condition) **)**
> *编译条件***(** [*编译条件*](#compilation-condition) **)**
>
> *编译条件***!** [编译条件](#compilation-condition)
> *编译条件***!** [*编译条件*](#compilation-condition)
>
> *编译条件* → [编译条件](#compilation-condition) **&&** [编译条件](#compilation-condition)
> *编译条件* → [*编译条件*](#compilation-condition) **&&** [*编译条件*](#compilation-condition)
>
> *编译条件* → [*编译条件*](#compilation-condition) **||** [*编译条件*](#compilation-condition)
>
> *编译条件* → [编译条件](#compilation-condition) **||** [编译条件](#compilation-condition)
#### grammar-platform-condition {#grammar-platform-condition}
#### grammar_platform-condition {#grammar-platform-condition}
#### grammar-platform-condition-os {#grammar-platform-condition-os}
> *平台条件* → **os ( [操作系统](#operating-system) )**
#### grammar_platform-condition-os {#grammar-platform-condition-os}
> *平台条件* → **os ( [*操作系统*](#operating-system) )**
>
#### grammar-platform-condition-arch {#grammar-platform-condition-arch}
> *平台条件* → **arch ( [架构](#architecture) )**
#### grammar_platform-condition-arch {#grammar-platform-condition-arch}
> *平台条件* → **arch ( [*架构*](#architecture) )**
>
#### grammar-platform-condition-swift {#grammar-platform-condition-swift}
> *平台条件***swift ( >= [swift 版本](#swift-version) )** | **swift ( < [swift 版本](#swift-version) )**
#### grammar_platform-condition-swift {#grammar-platform-condition-swift}
> *平台条件***swift ( >= [*swift 版本*](#swift-version) )** | **swift ( < [*swift 版本*](#swift-version) )**
>
#### grammar-platform-condition-compiler {#grammar-platform-condition-compiler}
> *平台条件***compiler ( >= [swift 版本](#swift-version) )** | **compiler ( < [swift 版本](#swift-version) )**
#### grammar_platform-condition-compiler {#grammar-platform-condition-compiler}
> *平台条件***compiler ( >= [*swift 版本*](#swift-version) )** | **compiler ( < [*swift 版本*](#swift-version) )**
>
#### grammar-platform-condition-canImport {#grammar-platform-condition-canImport}
> *平台条件* → **canImport ( [模块名](#grammar-module-name) )**
#### grammar_platform-condition-canImport {#grammar-platform-condition-canImport}
> *平台条件* → **canImport ( [*模块名*](#grammar_module-name) )**
>
#### grammar-platform-condition-targetEnvironment {#grammar-platform-condition-targetEnvironment}
> *平台条件* → **targetEnvironment ( [环境](#grammar-environment) )**
#### grammar_platform-condition-targetEnvironment {#grammar-platform-condition-targetEnvironment}
> *平台条件* → **targetEnvironment ( [*环境*](#grammar_environment) )**
>
#### operating-system {#operating-system}
> *操作系统***macOS** | **iOS** | **watchOS** | **tvOS****Linux****Windows**
> *操作系统***macOS** | **iOS** | **watchOS** | **tvOS**
>
#### architecture {#architecture}
> *架构***i386** | **x86_64** | **arm** | **arm64**
>
#### swift-version {#swift-version}
> *swift 版本* → [十进制数字](./02_Lexical_Structure.md#decimal-digit) ­**.** ­[swift 版本延续](#grammar-swift-version-continuation) <sub>可选</sub>
> *swift 版本* → [*十进制数字*](./02_Lexical_Structure.md#decimal-digit) ­**.** ­[*swift 版本延续*](#grammar_swift-version-continuation) <sub>可选</sub>
>
#### grammar-swift-version-continuation {#grammar-swift-version-continuation}
> *swift 版本延续***.** [十进制数字](./02_Lexical_Structure.md#decimal-digit) [swift 版本延续](#grammar-swift-version-continuation) <sub>可选</sub>
#### grammar_swift-version-continuation {#grammar-swift-version-continuation}
> *swift 版本延续***.** [*十进制数字*](./02_Lexical_Structure.md#decimal-digit) [*swift 版本延续*](#grammar_swift-version-continuation) <sub>可选</sub>
>
#### grammar-module-name {#grammar-module-name}
> *模块名* → [identifier](./02_Lexical_Structure.md#identifier)
#### grammar_module-name {#grammar-module-name}
> *模块名* → [*identifier*](./02_Lexical_Structure.md#identifier)
>
#### grammar-environment {#grammar-environment}
> *环境***模拟器** | **macCatalyst**
#### grammar_environment {#grammar-environment}
> *环境* → **模拟器**
>
### 行控制语句 {#line-control-statements}
行控制语句可以为被编译的源代码指定行号和文件名,从而改变源代码的定位信息,以便进行分析和调试。
行控制语句形式如下:
> #sourceLocation(file: file path, line: line number)
>
> #sourceLocation()
> \#sourceLocation(file: `filename` , line:`line number`)
>
第一种的行控制语句会改变该语句之后的代码中的字面量表达式 `#line``#file``#filePath` 所表示的值,从行控制语句里行号的代码开始。`行号` 是一个大于 0 的整形字面量,会改变 `#line` 表达式的值。`文件名` 是一个字符串字面量,会改变 `#file``#filePath` 表达式的值。指定的字符串会变成 `#filePath` 的值,且字符串最后的路径部分会变成 `#file` 的值。
> \#sourceLocation()
>
第一种的行控制语句会改变该语句之后的代码中的字面量表达式 `#line``#file` 所表示的值。`行号` 是一个大于 0 的整形字面量,会改变 `#line` 表达式的值。`文件名` 是一个字符串字面量,会改变 `#file` 表达式的值。
第二种的行控制语句,`#sourceLocation()`,会将源代码的定位信息重置回默认的行号和文件名。
@ -765,16 +814,19 @@ statements to compile if both compilation conditions are false
#### line-control-statement {#line-control-statement}
> 行控制语句语法
>
>*行控制语句* → **#sourceLocation(file:[文件名](#file-name),line:[行号](#line-number))**
>
> *行控制语句***#sourceLocation(file:[*文件名*](#file-name),line:[*行号*](#line-number))**
>
> *行控制语句***#sourceLocation()**
>
> *行控制语句***#sourceLocation( )**
#### line-number {#line-number}
> *行号* → 大于 0 的十进制整数
>
#### file-name {#file-name}
> *文件名* → [静态字符串字面量](./02_Lexical_Structure.md#static-string-literal)
> *文件名* → [*静态字符串字面量*](./02_Lexical_Structure.md#static-string-literal)
>
### 编译时诊断语句 {#compile-time-diagnostic-statement}
@ -789,16 +841,17 @@ statements to compile if both compilation conditions are false
> 编译时诊断语句语法
>
>
#### grammar-compile-time-diagnostic-statement {#grammar-compile-time-diagnostic-statement}
> *诊断语句***#error** **(** [诊断消息](#grammar-diagnostic-message) **)**
#### grammar_compile-time-diagnostic-statement {#grammar-compile-time-diagnostic-statement}
> *诊断语句***#error** **(** [*diagnostic-message*](#grammar_diagnostic-message) **)**
>
> *诊断语句***#warning** **(** [诊断消息](#grammar-diagnostic-message) **)**
> *诊断语句***#warning** **(** [*diagnostic-message*](#grammar_diagnostic-message) **)**
>
> *诊断语句* → [*静态字符串字面量*](./02_Lexical_Structure.md#static-string-literal)
>
> *诊断语句* → [静态字符串字面量](./02_Lexical_Structure.md#static-string-literal)
## 可用性条件 {#availability-condition}
可用性条件可作为 `if``while``guard` 语句的条件,可以在运行时基于特定的平台参数来查询 API 的可用性。
可用性条件的形式如下:
@ -819,18 +872,22 @@ if #available(platform name version, ..., *) {
> 可用性条件语法
>
>
#### availability-condition {#availability-condition}
> *可用性条件***#available** **(** [可用性参数列表](#availability-arguments) **)**
>
> *可用性条件***#unavaliable** **** [可用性参数列表](#availability-arguments) **)**
> *可用性条件***#available** **(** [*可用性参数列表*](#availability-arguments) **)**
>
#### availability-arguments {#availability-arguments}
> *可用性参数列表* → [可用性参数](#availability-argument) | [可用性参数](#availability-argument) **,** [可用性参数列表](#availability-arguments)
> *可用性参数列表* → [*可用性参数*](#availability-argument) | [*可用性参数*](#availability-argument) **,** [*可用性参数列表*](#availability-arguments)
>
#### availability-argument {#availability-argument}
> *可用性参数* → [平台名称](#platform-name) [平台版本](#platform-version)
>
> *可用性条件*__*__
>
>
#### platform-name {#platform-name}
> *平台名称***iOS** | **iOSApplicationExtension**
@ -840,6 +897,7 @@ if #available(platform name version, ..., *) {
> *平台名称* → **watchOS**
>
> *平台名称* → **tvOS**
>
#### platform-version {#platform-version}
> *平台版本* → [十进制数字](./02_Lexical_Structure.md#decimal-digits)
@ -847,3 +905,4 @@ if #available(platform name version, ..., *) {
> *平台版本* → [十进制数字](./02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](./02_Lexical_Structure.md#decimal-digits)
>
> *平台版本* → [十进制数字](./02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](./02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](./02_Lexical_Structure.md#decimal-digits)
>

419
source/chapter3/07_Attributes.md Executable file
View File

@ -0,0 +1,419 @@
# 特性Attributes
在 Swift 中有两种特性,分别用于修饰声明和类型。特性提供了有关声明和类型的更多信息。例如,使用 `discardableResult` 特性声明的函数,表明该函数虽然有返回值,但如果没有使用该返回值,编译器不会产生警告。
您可以通过以下方式指定一个特性,通过符号 `@` 后跟特性的名称和特性接收的任何参数:
@`特性名`
@`特性名`(`特性参数`)
有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰某个特定的声明的。这些_特性的参数_写在圆括号内它们的格式由它们所属的特性来定义。
## 声明特性 {#declaration-attributes}
声明特性只能应用于声明。
### `available` {#available}
`available` 特性用于声明时,表示该声明的生命周期是相对于特定的平台和操作系统版本。
`available` 特性经常与参数列表一同出现,该参数列表至少有两个特性参数,参数之间由逗号分隔。这些参数由以下这些平台名字中的一个起头:
- `iOS`
- `iOSApplicationExtension`
- `macOS`
- `macOSApplicationExtension`
- `watchOS`
- `watchOSApplicationExtension`
- `tvOS`
- `tvOSApplicationExtension`
- `swift`
当然,你也可以用一个星号(`*`)来表示上面提到的所有平台。指定 Swift 版本的 `available` 特性参数,不能使用星号表示。
其余的参数,可以按照任何顺序出现,并且可以添加关于声明生命周期的附加信息,包括重要事件。
- `unavailable` 参数表示该声明在指定的平台上是无效的。当指定 Swift 版本可用性时不可使用该参数。
- `introduced` 参数表示指定平台从哪一版本开始引入该声明。格式如下:
`introduced`: `版本号`
*版本号*由一至三个正整数构成,由句点分隔的。
- `deprecated` 参数表示指定平台从哪一版本开始弃用该声明。格式如下:
`deprecated`: `版本号`
可选的*版本号*由一个或多个正整数构成,由句点分隔的。省略版本号表示该声明目前已弃用,当弃用出现时没有给出任何有关信息。如果你省略了版本号,冒号(`:`)也可省略。
- `obsoleted` 参数表示指定平台或语言从哪一版本开始废弃该声明。当一个声明被废弃后,它就从平台或语言中移除,不能再被使用。格式如下:
`obsoleted`: `版本号`
*版本号*由一至三个正整数构成,由句点分隔的。
- `message` 参数用来提供文本信息。当使用被弃用或者被废弃的声明时,编译器会抛出警告或错误信息。格式如下:
`message`: `信息内容`
_信息内容_由一个字符串构成。
- `renamed` 参数用来提供文本信息,用以表示被重命名的声明的新名字。当使用声明的旧名字时,编译器会报错提示新名字。格式如下:
`renamed`: `新名字`
_新名字_由一个字符串构成。
你可以将 `renamed` 参数和 `unavailable` 参数用于 `available` 特性,来表示声明在不同平台和 Swift 版本上的可用性。如下所示,表示声明的名字在一个框架或者库的不同发布版本间发生了变化。以此组合表示该声明被重命名的编译错误。
```swift
// 首发版本
protocol MyProtocol {
// 这里是协议定义
}
```
```swift
// 后续版本重命名了 MyProtocol
protocol MyRenamedProtocol {
// 这里是协议定义
}
@available(*, unavailable, renamed:"MyRenamedProtocol")
typealias MyProtocol = MyRenamedProtocol
```
你可以在某个声明上使用多个 `available` 特性,以指定该声明在不同平台和 Swift 版本上的可用性。编译器只有在与 `available` 特性中指定的平台或语言版本匹配时,才会使用 `available` 特性。
如果 `available` 特性除了平台名称参数外,只指定了一个 `introduced` 参数,那么可以使用以下简写语法代替:
@available(`平台名称` `版本号`, *)
@available(swift `版本号`)
`available` 特性的简写语法可以简明地表达出声明在多个平台上的可用性。尽管这两种形式在功能上是相同的,但请尽可能地使用简写语法形式。
```swift
@available(iOS 10.0, macOS 10.12, *)
class MyClass {
// 这里是类定义
}
```
`available` 特性需要同时指定 Swift 版本和平台可用性,需要使用单独的 `available` 特性来声明。
```swift
@available(swift 3.0.2)
@available(macOS 10.12, *)
struct MyStruct {
// 这里是结构体定义
}
```
### `discardableResult` {#discardableresult}
该特性用于的函数或方法声明,以抑制编译器中函数或方法的返回值被调而没有使用其结果的警告。
### `dynamicCallable` {#dynamiccallable}
该特性用于类、结构体、枚举或协议,以将该类型的实例视为可调用的函数。该类型必须实现 `dynamicallyCall(withArguments:)``dynamicallyCall(withKeywordArguments:)` 方法之一,或两者同时实现。
你可以调用 `dynamicCallable` 特性的实例,就像是调用一个任意数量参数的函数。
```swift
@dynamicCallable
struct TelephoneExchange {
func dynamicallyCall(withArguments phoneNumber: [Int]) {
if phoneNumber == [4, 1, 1] {
print("Get Swift help on forums.swift.org")
} else {
print("Unrecognized number")
}
}
}
let dial = TelephoneExchange()
// 使用动态方法调用
dial(4, 1, 1)
// 打印“Get Swift help on forums.swift.org”
dial(8, 6, 7, 5, 3, 0, 9)
// 打印“Unrecognized number”
// 直接调用底层方法
dial.dynamicallyCall(withArguments: [4, 1, 1])
```
`dynamicallyCall(withArguments:)` 方法的声明必须至少有一个参数遵循 [`ExpressibleByArrayLiteral`](https://developer.apple.com/documentation/swift/expressiblebyarrayliteral) 协议,如 `[Int]`,而返回值类型可以是任意类型。
```swift
@dynamicCallable
struct Repeater {
func dynamicallyCall(withKeywordArguments pairs: KeyValuePairs<String, Int>) -> String {
return pairs
.map { label, count in
repeatElement(label, count: count).joined(separator: " ")
}
.joined(separator: "\n")
}
}
let repeatLabels = Repeater()
print(repeatLabels(a: 1, b: 2, c: 3, b: 2, a: 1))
// a
// b b
// c c c
// b b
// a
```
`dynamicallyCall(withKeywordArguments:)` 方法声明必须至少有一个参数遵循 [`ExpressibleByDictionaryLiteral`](https://developer.apple.com/documentation/swift/expressiblebydictionaryliteral) 协议,返回值可以任意类型。参数的 [`Key`](https://developer.apple.com/documentation/swift/expressiblebydictionaryliteral/2294108-key) 必须遵循 [`ExpressibleByStringLiteral`](https://developer.apple.com/documentation/swift/expressiblebystringliteral) 协议。上述的示例使用 [`KeyValuePairs`](https://developer.apple.com/documentation/swift/keyvaluepairs) 作为参数类型,以便调用者可以传入重复的参数标签,`a``b` 在调用 `repeat`中多次使用。
如果你同时实现两种 `dynamicallyCall` 方法,则当在方法调用中包含关键字参数时,会调用 `dynamicallyCall(withKeywordArguments:)` 方法,否则调用 `dynamicallyCall(withArguments:)` 方法。
你只能调用参数和返回值与 `dynamicallyCall` 方法实现匹配的动态调用实例。在下面示例的调用无法编译,因为其 `dynamicallyCall(withArguments:)` 实现不接受 `KeyValuePairs<String, String>` 参数。
```swift
repeatLabels(a: "four") // Error
```
### `dynamicMemberLookup` {#dynamicmemberlookup}
该特性用于类、结构体、枚举或协议,让其能在运行时查找成员。该类型必须实现 `subscript(dynamicMemberLookup:)` 下标。
在显式成员表达式中,如果没有成名指定成员,则该表达式被理解为对该类型的 `subscript(dynamicMemberLookup:)` 下标的调用,传递包含成员名称字符串的参数。下标接收参数既可以是键路径,也可以是成员名称字符串;如果你同时实现这两种方式的下标调用,那么以键路径参数方式为准。
`subscript(dynamicMemberLookup:)` 实现允许接收 [`KeyPath`](https://developer.apple.com/documentation/swift/keypath)[`WritableKeyPath`](https://developer.apple.com/documentation/swift/writablekeypath) 或 [`ReferenceWritableKeyPath`](https://developer.apple.com/documentation/swift/referencewritablekeypath) 类型的键路径参数。而遵循 [`ExpressibleByStringLiteral`](https://developer.apple.com/documentation/swift/expressiblebystringliteral) 协议,下标调用接收参数为成员名称字符串 —— 在大多数情况下,下标的参数是一个 `String` 值。下标返回值类型可以为任意类型。
根据成员名称来动态地查找成员,可以帮助我们创建一个包裹数据的包装类型,但该类型无法在编译时进行类型检查,例如其他语言的数据桥接到 Swift 语言时。例如:
```swift
@dynamicMemberLookup
struct DynamicStruct {
let dictionary = ["someDynamicMember": 325,
"someOtherMember": 787]
subscript(dynamicMember member: String) -> Int {
return dictionary[member] ?? 1054
}
}
let s = DynamicStruct()
// 使用动态成员查找
let dynamic = s.someDynamicMember
print(dynamic)
// 打印“325”
// 直接调用底层下标
let equivalent = s[dynamicMember: "someDynamicMember"]
print(dynamic == equivalent)
// 打印“true”
```
根据键路径来动态地查找成员,可用于创建一个包裹数据的包装类型,该类型在编译时期进行类型检查。例如:
```swift
struct Point { var x, y: Int }
@dynamicMemberLookup
struct PassthroughWrapper<Value> {
var value: Value
subscript<T>(dynamicMember member: KeyPath<Value, T>) -> T {
get { return value[keyPath: member] }
}
}
let point = Point(x: 381, y: 431)
let wrapper = PassthroughWrapper(value: point)
print(wrapper.x)
```
### `GKInspectable` {#gkinspectable}
应用此属性,暴露一个自定义 GameplayKit 组件属性给 SpriteKit 编辑器 UI。
### `inlinable` {#inlinable}
该特性用于函数、方法、计算属性、下标、便利构造器或析构器的声明,以将该声明的实现公开为模块公开接口的一部分。编译器允许在调用处把 `inlinable` 标记的符号替换为符号实现的副本。
内联代码可以与任意模块中 `public` 访问级别的符号进行交互,同时可以与在相同模块中标记 `usableFromInline` 特性的 `internal` 访问级别的符号进行交互。内联代码不能与 `private``fileprivate` 级别的符号进行交互。
该特性不能用于嵌套在函数内的声明,也不能用于 `fileprivate``private` 访问级别的声明。在内联函数定义的函数和闭包是隐式非内联的,即使他们不能标记该特性。
### `nonobjc` {#nonobjc}
该特性用于方法、属性、下标、或构造器的声明,这些声明本可以在 Objective-C 代码中使用,而使用 `nonobjc` 特性则告诉编译器这个声明不能在 Objective-C 代码中使用。
该特性用在扩展中,与在没有明确标记为 `objc` 特性的扩展中给每个成员添加该特性具有相同效果。
可以使用 `nonobjc` 特性解决标有 `objc` 的类中桥接方法的循环问题,该特性还允许对标有 `objc` 的类中的构造器和方法进行重载。
标有 `nonobjc` 特性的方法不能重写标有 `objc` 特性的方法。然而,标有 `objc` 特性的方法可以重写标有 `nonobjc` 特性的方法。同样,标有 `nonobjc` 特性的方法不能满足标有 `@objc` 特性的协议中的方法要求。
### `NSApplicationMain` {#nsapplicationmain}
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `NSApplicationMain(_:_:)` 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个特性,可以提供一个 `main.swift` 文件,并在代码**顶层**调用 `NSApplicationMain(_:_:)` 函数,如下所示:
```swift
import AppKit
NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
```
### `NSCopying` {#nscopying}
该特性用于修饰一个类的存储型变量属性。该特性将使属性的设值方法使用传入值的副本进行赋值,这个值由传入值的 `copyWithZone(_:)` 方法返回。该属性的类型必需符合 `NSCopying` 协议。
`NSCopying` 特性的行为与 Objective-C 中的 `copy` 特性相似。
### `NSManaged` {#nsmanaged}
该特性用于修饰 `NSManagedObject` 子类中的实例方法或存储型变量属性,表明它们的实现由 `Core Data` 在运行时基于相关实体描述动态提供。对于标记了 `NSManaged` 特性的属性,`Core Data` 也会在运行时为其提供存储。应用这个特性也意味着 `objc` 特性。
### `objc` {#objc}
该特性用于修饰任何可以在 Objective-C 中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限原始值为整型的枚举)、类和协议中的属性和方法(包括存取方法)、构造器、析构器以及下标运算符。`objc` 特性告诉编译器这个声明可以在 Objective-C 代码中使用。
该特性用在扩展中,与在没有明确标记为 `nonobjc` 特性的扩展中给每个成员添加该特性具有相同效果。
编译器隐式地将 `objc` 特性添加到 Objective-C 中定义的任何类的子类。但是,子类不能是泛型的,并且不能继承于任何泛型类。你可以将 `objc` 特性显式添加到满足这些条件的子类,来指定其 Objective-C 名称,如下所述。添加了 `objc` 的协议不能继承于没有此特性的协议。
在以下情况中隐式添加了 `objc` 特性。
- 父类有 `objc` 特性,且重写为子类的声明。
- 遵循带有 `objc` 特性协议的声明。
- 带有 `IBAction``IBSegueAction``IBOutlet``IBDesignable``IBInspectable``NSManaged``GKInspectable` 特性的声明。
如果你将 `objc` 特性应用于枚举,每一个枚举用例都会以枚举名称和用例名称组合的方式暴露在 Objective-C 代码中。例如,在 `Planet` 枚举中有一个名为 `Venus` 的用例,该用例暴露在 Objective-C 代码中时叫做 `PlanetVenus`
`objc` 特性有一个可选的参数,由标识符构成。当你想把 `objc` 所修饰的实体以一个不同的名字暴露给 Objective-C 时,你就可以使用这个特性参数。你可以使用这个参数来命名类、枚举类型、枚举用例、协议、方法、存取方法以及构造器。如果你要指定类、协议或枚举在 Objective-C 下的名称,在名称上包含三个字母的前缀,如 [Objective-C 编程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011210) 中的 [约定](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Conventions/Conventions.html#//apple_ref/doc/uid/TP40011210-CH10-SW1)。下面的例子把 `ExampleClass` 中的 `enabled` 属性的取值方法暴露给 Objective-C名字是 `isEnabled`,而不是它原来的属性名。
```swift
class ExampleClass: NSObject {
@objc var enabled: Bool {
@objc(isEnabled) get {
// 返回适当的值
}
}
}
```
### `objcMembers` {#objcmembers}
该特性用于类声明,以将 `objc` 特性应用于该类、扩展、子类以及子类的扩展的所有 Objective-C 兼容成员。
大多数代码应该使用 `objc` 特性,以暴露所需的声明。如果需要暴露多个声明,可以将其分组到添加 `objc` 特性的扩展中。`objcMembers` 特性为大量使用 Objective-C 运行时的内省工具的库提供了便利。添加不必要的 `objc` 特性会增加二进制体积并影响性能。
### `requires_stored_property_inits` {#requires-stored-property-inits}
该特性用于类声明,以要求类中所有存储属性提供默认值作为其定义的一部分。对于从中继承的任何类都推断出 `NSManagedObject` 特性。
### `testable` {#testable}
在导入允许测试的编译模块时,该特性用于修饰 `import` 声明,这样就能访问被导入模块中的任何标有 `internal` 访问级别修饰符的实体,犹如它们被标记了 `public` 访问级别修饰符。测试也可以访问使用 `internal` 或者 `public` 访问级别修饰符标记的类和类成员,就像它们是 `open` 访问修饰符声明的。
### `UIApplicationMain` {#uiapplicationmain}
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `UIApplicationMain` 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个特性,可以提供一个 `main.swift` 文件,并在代码顶层调用 `UIApplicationMain(_:_:_:_:)` 函数。比如,如果你的应用程序使用一个继承于 UIApplication 的自定义子类作为主要类,你可以调用 `UIApplicationMain(_:_:_:_:)` 函数而不是使用该特性。
### `usableFromInline` {#usablefrominline}
该特性用于函数、方法、计算属性、下标、构造器或析构器的声明,以在同一模块中允许该符号用于内联代码的声明。声明必须具有 `internal` 访问级别修饰符。
`public` 访问修饰符相同的是,该特性将声明公开为模块公共接口的一部分。区别于 `public`,编译器不允许在模块外部的代码通过名称引用 `usableFromInline` 标记的声明,即使导出了声明符号也是无法引用。但是,模块外的代码仍然可以通过运行时交换声明符号。
标记为 `inlinable` 特性的声明,在内联代码中可以隐式使用。虽然 `inlinable``usableFromInline` 可以用于 `internal` 声明,但这两者不能同时使用。
### `warn_unqualified_access` {#warn-unqualified-access}
该特性应用于顶级函数、实例方法、类方法或静态方法,以在没有前置限定符(例如模块名称、类型名称、实例变量或常量)的情况下使用该函数或方法时触发警告。使用该特性可以帮助减少在同一作用于访问同名函数之间的歧义。
例如Swift 标准库包含 [`min(_:_:)`](https://developer.apple.com/documentation/swift/1538339-min/) 顶级函数和用于序列比较元素的 [`min()`](https://developer.apple.com/documentation/swift/sequence/1641174-min) 方法。序列方法声明使用了 `warn_unqualified_access`,以减少在 `Sequence` 扩展中使用它们的歧义。
### Interface Builder 使用的声明特性 {#declaration-attributes-used-by-interface-builder}
`Interface Builder` 特性是 `Interface Builder` 用来与 Xcode 同步的声明特性。`Swift` 提供了以下的 `Interface Builder` 特性:`IBAction``IBSegueAction``IBOutlet``IBDesignable`,以及 `IBInspectable` 。这些特性与 Objective-C 中对应的特性在概念上是相同的。
`IBOutlet``IBInspectable` 用于修饰一个类的属性声明,`IBAction` 特性用于修饰一个类的方法声明,`IBDesignable` 用于修饰类的声明。
应用 `IBAction``IBSegueAction``IBOutlet``IBDesignable` 或者 `IBInspectable` 特性都意味着同时应用 `objc` 特性。
## 类型特性 {#type-attributes}
类型特性只能用于修饰类型。
### `autoclosure` {#autoclosure}
这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以修饰类型为返回表达式结果类型的无参数函数类型的函数参数。关于如何使用 autoclosure 特性的例子,请参阅 [自动闭包](../chapter2/07_Closures.md#autoclosures) 和 [函数类型](./03_Types.md#function_type)。
### `convention` {#convention}
该特性用于修饰函数类型,它指出了函数调用的约定。
convention 特性总是与下面的参数之一一起出现。
- `swift` 参数用于表示一个 Swift 函数引用。这是 Swift 中函数值的标准调用约定。
- `block` 参数用于表示一个 Objective-C 兼容的块引用。函数值会作为一个块对象的引用,块是一种 `id` 兼容的 Objective-C 对象,其中嵌入了调用函数。调用函数使用 C 的调用约定。
- `c` 参数用于表示一个 C 函数引用。函数值没有上下文,不具备捕获功能,同样使用 C 的调用约定。
使用 C 函数调用约定的函数也可用作使用 Objective-C 块调用约定的函数,同时使用 Objective-C 块调用约定的函数也可用作使用 Swift 函数调用约定的函数。然而,只有非泛型的全局函数、局部函数以及未捕获任何局部变量的闭包,才可以被用作使用 C 函数调用约定的函数。
### `escaping` {#escaping}
在函数或者方法声明上使用该特性,它表示参数将不会被存储以供延迟执行,这将确保参数不会超出函数调用的生命周期。在使用 `escaping` 声明特性的函数类型中访问属性和方法时不需要显式地使用 `self.`。关于如何使用 `escaping` 特性的例子,请参阅 [逃逸闭包](../chapter2/07_Closures.md#escaping_closures)。
## Switch Case 特性 {#switch-case-attributes}
你只能在 switch cases 中使用 switch case 特性。
### `unknown` {#unknown}
次特性用于 switch case表示在编译时该地方不会匹配枚举的任何情况。有关如何使用 `unknown` 特性的示例,可参阅 [对未来枚举的 `case` 进行 `switch`](./05_Statements.md#future-case)。
> 特性语法
>
>
>
#### attribute {#attribute}
>
> *特性*→ [特性名](#attribute_name) [特性参数子句](#atribute_argument_clause)<sub>可选</sub>
>
>
#### attribute_name {#attribute-name}
>
> *特性名* → [标识符](./02_Lexical_Structure.md#identifier)
>
>
#### atribute_argument_clause {#atribute-argument-clause}
>
> *特性参数子句* → **(** [均衡令牌列表](#balanced_tokens)<sub>可选</sub> **)**
>
>
#### attributes {#attributes}
>
> *特性列表* → [特性](#attribute) [特性列表](#attributes)<sub>可选</sub>
>
>
>
#### balanced_tokens {#balanced-tokens}
>
> *均衡令牌列表* → [均衡令牌](#balanced_token) [均衡令牌列表](#balanced_tokens)<sub>可选</sub>
>
>
#### balanced_token {#balanced-token}
>
> *均衡令牌* → **(** [均衡令牌列表](#balanced_tokens)<sub>可选</sub> **)**
>
> *均衡令牌* → **\[** [均衡令牌列表](#balanced_tokens)<sub>可选</sub> **\]**
>
> *均衡令牌* → **{** [均衡令牌列表](#balanced_tokens)<sub>可选</sub> **}**
>
> *均衡令牌* → 任意标识符,关键字,字面量或运算符
>
> *均衡令牌* → 任意标点除了 **(****)****[****]****{**,或 **}**
>

View File

@ -12,21 +12,21 @@ Swift 中的模式分为两类:一种能成功匹配任何类型的值,另
>
#### pattern {#pattern}
> *模式* → [通配符模式](#wildcard-pattern) [类型注解](./03_Types.md#type-annotation)<sub>可选</sub>
> *模式* → [*通配符模式*](#wildcard_pattern) [*类型注解*](03_Types.md#type-annotation)<sub>可选</sub>
>
> *模式* → [标识符模式](#identifier-pattern) [类型注解](./03_Types.md#type-annotation)<sub>可选</sub>
> *模式* → [*标识符模式*](#identifier_pattern) [*类型注解*](03_Types.md#type-annotation)<sub>可选</sub>
>
> *模式* → [值绑定模式](#value-binding-pattern)
> *模式* → [*值绑定模式*](#value-binding-pattern)
>
> *模式* → [元组模式](#tuple-pattern) [类型注解](./03_Types.md#type-annotation)<sub>可选</sub>
> *模式* → [*元组模式*](#tuple-pattern) [*类型注解*](03_Types.md#type-annotation)<sub>可选</sub>
>
> *模式* → [枚举用例模式](#enum-case-pattern)
> *模式* → [*枚举用例模式*](#enum-case-pattern)
>
> *模式* → [可选模式](#optional-pattern)
> *模式* → [*可选模式*](#optional-pattern)
>
> *模式* → [类型转换模式](#type-casting-pattern)
> *模式* → [*类型转换模式*](#type-casting-pattern)
>
> *模式* → [表达式模式](#expression-pattern)
> *模式* → [*表达式模式*](#expression-pattern)
>
## 通配符模式Wildcard Pattern {#wildcard-pattern}
@ -41,7 +41,8 @@ for _ in 1...3 {
> 通配符模式语法
>
> #### wildcard-pattern {#wildcard-pattern}
#### wildcard-pattern {#wildcard-pattern}
> *通配符模式* → **_**
>
@ -58,8 +59,9 @@ let someValue = 42
> 标识符模式语法
>
> #### identifier-pattern {#identifier-pattern}
> *标识符模式* → [标识符](./02_Lexical_Structure.md#identifier)
#### identifier-pattern {#identifier-pattern}
> *标识符模式* → [*标识符*](./02_Lexical_Structure.md#identifier)
>
## 值绑定模式Value-Binding Pattern {#value-binding-pattern}
@ -81,8 +83,9 @@ case let (x, y):
> 值绑定模式语法
>
> #### value-binding-pattern {#value-binding-pattern}
> *值绑定模式***var** [模式](#pattern) | **let** [模式](#pattern)
#### value-binding-pattern {#value-binding-pattern}
> *值绑定模式***var** [*模式*](#pattern) | **let** [*模式*](#pattern)
>
## 元组模式 {#tuple-pattern}
@ -110,42 +113,29 @@ let (a): Int = 2 // a: Int = 2
> 元组模式语法
>
> #### tuple-pattern {#tuple-pattern}
> *元组模式***(** [元组模式元素列表](#tuple-pattern-element-list)<sub>可选</sub> **)**
#### tuple-pattern {#tuple-pattern}
> *元组模式***(** [*元组模式元素列表*](#tuple-pattern-element-list)<sub>可选</sub> **)**
>
#### tuple-pattern-element-list {#tuple-pattern-element-list}
> *元组模式元素列表* → [元组模式元素](#tuple-pattern-element) | [元组模式元素](#tuple-pattern-element) **,** [元组模式元素列表](#tuple-pattern-element-list)
> *元组模式元素列表* → [*元组模式元素*](#tuple-pattern-element) | [*元组模式元素*](#tuple-pattern-element) **,** [*元组模式元素列表*](#tuple-pattern-element-list)
>
> #### tuple-pattern-element {#tuple-pattern-element}
> *元组模式元素* → [模式](#pattern)
#### tuple-pattern-element {#tuple-pattern-element}
> *元组模式元素* → [*模式*](#pattern)
>
## 枚举用例模式Enumeration Case Pattern {#enumeration-case-pattern}
*枚举用例模式*匹配现有的某个枚举类型的某个用例。枚举用例模式出现在 `switch` 语句中的 `case` 标签中,以及 `if``while``guard``for-in` 语句的 `case` 条件中。
如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。关于使用 `switch` 语句来匹配包含关联值的枚举用例的例子,请参阅 [关联值](../02_language_guide/08_Enumerations.md#associated-values)。
枚举用例模式同样会匹配那些被包装成可选值的用例。简化的语法能将可选模式过滤掉。注意,由于 `Optional` 是枚举实现的,`.none``.some` 都会作为枚举类型的用例出现在 switch 中。
```swift
enum SomeEnum { case left, right }
let x: SomeEnum? = .left
switch x {
case .left:
print("Turn left")
case .right:
print("Turn right")
case nil:
print("Keep going straight")
}
// 打印 "Turn left"
```
如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。关于使用 `switch` 语句来匹配包含关联值的枚举用例的例子,请参阅 [关联值](../chapter2/08_Enumerations.md#associated_values)。
> 枚举用例模式语法
>
> #### enum-case-pattern {#enum-case-pattern}
> *枚举用例模式* → [类型标识](./03_Types.md#type-identifier)<sub>可选</sub> **.** [枚举用例名](./06_Declarations.md#enum-case-name) [元组模式](#tuple-pattern)<sub>可选</sub>
#### enum-case-pattern {#enum-case-pattern}
> *枚举用例模式* → [*类型标识*](./03_Types.md#type-identifier)<sub>可选</sub> **.** [*枚举用例名*](./06_Declarations.md#enum-case-name) [*元组模式*](#tuple-pattern)<sub>可选</sub>
>
## 可选模式Optional Pattern {#optional-pattern}
@ -181,8 +171,9 @@ for case let number? in arrayOfOptinalInts {
> 可选模式语法
>
> #### optional-pattern {#optional-pattern}
> *可选模式* → [标识符模式](./03_Types.md#type-identifier) **?**
#### optional-pattern {#optional-pattern}
> *可选模式* → [*标识符模式*](./03_Types.md#type-identifier) **?**
>
## 类型转换模式Type-Casting Patterns {#type-casting-patterns}
@ -197,18 +188,21 @@ for case let number? in arrayOfOptinalInts {
`as` 模式仅当一个值的类型在运行时和 `as` 模式右边的指定类型一致,或者是其子类的情况下,才会匹配这个值。如果匹配成功,被匹配的值的类型被转换成 `as` 模式右边指定的类型。
关于使用 `switch` 语句配合 `is` 模式和 `as` 模式来匹配值的例子,请参阅 [Any 和 AnyObject 的类型转换](../02_language_guide/18_Type_Casting.md#type-casting-for-any-and-anyobject)。
关于使用 `switch` 语句配合 `is` 模式和 `as` 模式来匹配值的例子,请参阅 [Any 和 AnyObject 的类型转换](../chapter2/18_Type_Casting.md#type_casting_for_any_and_anyobject)。
> 类型转换模式语法
>
> #### type-casting-pattern {#type-casting-pattern}
> *类型转换模式* → [is 模式](#is-pattern) | [as 模式](#as-pattern)
#### type-casting-pattern {#type-casting-pattern}
> *类型转换模式* → [*is 模式*](#is-pattern) | [*as 模式*](#as-pattern)
>
> #### is-pattern {#is-pattern}
> *is 模式***is** [类型](./03_Types.md#type)
#### is-pattern {#is-pattern}
> *is 模式***is** [*类型*](./03_Types.md#type)
>
> #### as-pattern {#as-pattern}
> *as 模式* → [模式](#pattern) **as** [类型](./03_Types.md#type)
#### as-pattern {#as-pattern}
> *as 模式* → [*模式*](#pattern) **as** [*类型*](03_Types.md#type)
>
## 表达式模式Expression Pattern {#expression-pattern}
@ -248,6 +242,7 @@ default:
> 表达式模式语法
>
> #### expression-pattern {#expression-pattern}
> *表达式模式* → [表达式](./04_Expressions.md#expression)
#### expression-pattern {#expression-pattern}
> *表达式模式* → [*表达式*](./04_Expressions.md#expression)
>

View File

@ -2,7 +2,7 @@
本节涉及泛型类型、泛型函数以及泛型构造器的参数,包括形参和实参。声明泛型类型、函数或构造器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型构造器时,就用具体的类型实参替代之。
关于 Swift 语言的泛型概述,请参阅 [泛型](../02_language_guide/22_Generics.md)。
关于 Swift 语言的泛型概述,请参阅 [泛型](../chapter2/22_Generics.md)。
## 泛型形参子句 {#generic-parameter}
*泛型形参子句*指定泛型类型或函数的类型形参,以及这些参数相关的约束和要求。泛型形参子句用尖括号(`<>`)包住,形式如下:
@ -49,63 +49,51 @@ simpleMax(3.14159, 2.71828) // T 被推断为 Double 类型
当然,替代类型形参的类型实参必须满足所有的约束和要求。
`where` 子句可以存在于包含类型参数的声明中,或作为声明的一部分,被嵌套另一个在含有类型参数的声明中。被嵌套的 `where` 子句依然可以指向包围它的声明中的类型参数,此时 `where` 子句需要满足的条件仅用于它被声明的地方。
如果外层的声明也有一个 `where` 子句,两个子句的条件都需要满足。下面的例子中,`startsWithZero()` 只有在 `Element` 同时满足 `SomeProtocol``Numeric` 才有效。
```swift
extension Collection where Element: SomeProtocol {
func startsWithZero() -> Bool where Element: Numeric {
return first == .zero
}
}
```
泛型函数或构造器可以重载,但在泛型形参子句中的类型形参必须有不同的约束或要求,抑或二者皆不同。当调用重载的泛型函数或构造器时,编译器会根据这些约束来决定调用哪个重载函数或构造器。
更多关于泛型 where 从句的信息和关于泛型函数声明的例子,可以看一看 [泛型 where 子句](../02_language_guide/22_Generics.md#where-clauses)。
更多关于泛型 where 从句的信息和关于泛型函数声明的例子,可以看一看 [泛型 where 子句](../chapter2/22_Generics.md#where_clauses)。
> 泛型形参子句语法
>
#### generic-parameter-clause {#generic-parameter-clause}
> *泛型形参子句***<** [泛型形参列表](#generic-parameter-list) [约束子句](#requirement-clause)<sub>可选</sub> **>**
> *泛型形参子句***<** [*泛型形参列表*](#generic-parameter-list) [*约束子句*](#requirement-clause)<sub>可选</sub> **>**
>
#### generic-parameter-list {#generic-parameter-list}
> *泛型形参列表* → [泛形形参](#generic-parameter) | [泛形形参](#generic-parameter) **,** [泛型形参列表](#generic-parameter-list)
> *泛型形参列表* → [*泛形形参*](#generic-parameter) | [*泛形形参*](#generic-parameter) **,** [*泛型形参列表*](#generic-parameter-list)
>
#### generic-parameter {#generic-parameter}
> *泛形形参* → [类型名称](./03_Types.md#type-name)
> *泛形形参* → [*类型名称*](./03_Types.md#type-name)
>
> *泛形形参* → [类型名称](./03_Types.md#type-name) **:** [类型标识符](./03_Types.md#type-identifier)
> *泛形形参* → [*类型名称*](./03_Types.md#type-name) **:** [*类型标识符*](./03_Types.md#type-identifier)
>
> *泛形形参* → [类型名称](./03_Types.md#type-name) **:** [协议合成类型](./03_Types.md#protocol-composition-type)
> *泛形形参* → [*类型名称*](./03_Types.md#type-name) **:** [*协议合成类型*](./03_Types.md#protocol-composition-type)
>
>
#### requirement-clause {#requirement-clause}
>
> *约束子句***where** [约束列表](#requirement-list)
> *约束子句***where** [*约束列表*](#requirement-list)
>
#### requirement-list {#requirement-list}
> *约束列表* → [约束](#requirement) | [约束](#requirement) **,** [约束列表](#requirement-list)
> *约束列表* → [*约束*](#requirement) | [*约束*](#requirement) **,** [*约束列表*](#requirement-list)
>
#### requirement {#requirement}
> *约束* → [一致性约束](#conformance-requirement) | [同类型约束](#same-type-requirement)
> *约束* → [*一致性约束*](#conformance-requirement) | [*同类型约束*](#same-type-requirement)
>
>
#### conformance-requirement {#conformance-requirement}
>
> *一致性约束* → [类型标识符](./03_Types.md#type-identifier) **:** [类型标识符](./03_Types.md#type-identifier)
> *一致性约束* → [*类型标识符*](./03_Types.md#type-identifier) **:** [*类型标识符*](./03_Types.md#type-identifier)
>
> *一致性约束* → [类型标识符](./03_Types.md#type-identifier) **:** [协议合成类型](./03_Types.md#protocol-composition-type)
> *一致性约束* → [*类型标识符*](./03_Types.md#type-identifier) **:** [*协议合成类型*](./03_Types.md#protocol-composition-type)
>
#### same-type-requirement {#same-type-requirement}
> *同类型约束* → [类型标识符](./03_Types.md#type-identifier) **==** [类型](./03_Types.md#type)
> *同类型约束* → [*类型标识符*](./03_Types.md#type-identifier) **==** [*类型*](./03_Types.md#type)
>
## 泛型实参子句 {#generic-argument}
@ -130,19 +118,19 @@ struct Dictionary<Key: Hashable, Value>: CollectionType, DictionaryLiteralConver
let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
```
如 [泛型形参子句](#generic-parameter) 所述,不能用泛型实参子句来指定泛型函数或构造器的类型实参。
如 [泛型形参子句](#generic_parameter) 所述,不能用泛型实参子句来指定泛型函数或构造器的类型实参。
> 泛型实参子句语法
>
#### generic-argument-clause {#generic-argument-clause}
> *泛型实参子句***<** [泛型实参列表](#generic-argument-list) **>**
> *泛型实参子句***<** [*泛型实参列表*](#generic-argument-list) **>**
>
#### generic-argument-list {#generic-argument-list}
> *泛型实参列表* → [泛型实参](#generic-argument) | [泛型实参](#generic-argument) **,** [泛型实参列表](#generic-argument-list)
> *泛型实参列表* → [*泛型实参*](#generic-argument) | [*泛型实参*](#generic-argument) **,** [*泛型实参列表*](#generic-argument-list)
>
#### generic-argument {#generic-argument}
> *泛型实参* → [类型](./03_Types.md#type)
> *泛型实参* → [*类型*](./03_Types.md#type)
>

View File

@ -1,3 +1,3 @@
# Swift 语言参考
# Swift 教程
本章描述了 Swift 的语言参考。

View File

@ -11,7 +11,7 @@
> 小明说哇靠学姐你还会妖法......
Swift 语言从 Xcode 6 beta 5 版本起加入了对权限控制Access Control的支持。其实权限控制和小明的物品一样你可以设定水壶是只有自己能用还是只有宿舍里的人能用还是全校都可以用。
Swift 语言从 Xcode 6 beta 5版本起加入了对权限控制Access Control的支持。其实权限控制和小明的物品一样你可以设定水壶是只有自己能用还是只有宿舍里的人能用还是全校都可以用。
从此以后,你可以好像神盾局局长一样,完全掌控自己的代码块的”保密级别“,哪些是只能在本文件引用,哪些能用在整个项目里,你还可以发挥大爱精神,把它开源成只要导入你的框架,大家都可以使用的 API。

View File

View File

@ -14,18 +14,15 @@ Swift 官方文档中文翻译由 [numbbbbb](https://github.com/numbbbbb) 发起
- [Hale](https://github.com/wuqiuhao)
- [Joeytat](https://github.com/joeytat)
- [jojotov](https://github.com/jojotov)
- [Licardo](https://github.com/L1cardo)
- [Khala-wan](https://github.com/Khala-wan)
- [Nemocdz](https://github.com/Nemocdz)
- [numbbbbb](https://github.com/numbbbbb)
- [pmst](https://github.com/colourful987)
- [Phenmod](https://github.com/Phenmod)
- [RickeyBoy](https://github.com/RickeyBoy)
- [SunsetWan](https://github.com/SunsetWan)
- [WAMaker](https://github.com/WAMaker)
- [YiYiZheng](https://github.com/YiYiZheng)
- [Yousanflics](https://github.com/Yousanflics)
- [CyberHex](https://cyberhex.me/)
- [Yousanflics](https://github.com/Yousanflics)
## Swift 4.x 主要贡献者