74 Commits

Author SHA1 Message Date
b8084200e7 修复优化高级运算符章节 (#1233)
* 修复斜体格式与理顺语句

* 修复图片链接
2023-04-11 22:03:01 -05:00
b9480f1b6c 修复语病与理顺语句 (#1232) 2023-04-10 14:23:29 -05:00
51e9f8133c 修复优化内存安全章节 (#1231)
* 修复失效的图片链接

* 修复错别字与优化表述
2023-04-07 07:47:56 -05:00
52917cffb4 修复优化自动引用计数章节 (#1230)
* 修复格式错误, 斜体以及删除多余的无意义字符

* 修复失效的图片链接

* 修复语病, 删除多余的字符
2023-04-06 21:35:40 -05:00
0d1124917a 修改不透明类型章节的语病 (#1229) 2023-04-04 07:28:43 -05:00
9da5252f5f 修改格式, 修复语病, 优化翻译 (#1228)
* 修复格式错误, 文本缩进与内容斜体

* 修改语病与错别字

* 优化翻译
2023-03-30 07:37:08 -05:00
40cb1217d7 Update 03_a_swift_tour.md 2023-03-29 10:27:42 -05:00
a5c95690f5 修改优化构造过程章节 (#1224)
* 修复构造过程章节的图片链接

* 修改构造过程章节的错别字和理顺部分语句
2023-03-22 09:25:47 -05:00
c54223ad3b 修改下标章节和继承章节 (#1223)
* 修复下标章节的图片链接以及类型下标的标题

* 修改继承章节的错别字
2023-03-20 07:45:55 -05:00
2acb695871 Update 01_The_Basics.md (#1222)
修正斜体
2023-03-19 21:37:50 -05:00
96b6cac45a 修复属性章节的图片链接, 理顺部分语句 (#1221) 2023-03-15 08:27:58 -05:00
645cbfba23 修复枚举, 类和结构体章节的图片链接 (#1220) 2023-03-14 13:07:57 -05:00
e31ea50e8f 修复字符串和字符, 集合类型, 函数章节的图片链接 (#1219) 2023-03-09 08:01:47 -06:00
ed0939a579 修改函数章节的错字 (#1218) 2023-03-08 23:40:54 -06:00
9a000a2ff0 typo and code format (#1217) 2023-03-03 21:19:02 -06:00
fe2c9ede92 Update 22_Generics.md 2023-02-17 09:46:18 -06:00
4dc673e444 fix: 同步官方的错误勘正 (#1212)
Co-authored-by: lanbangjue <lanbangjue@taqu.cn>
2022-12-09 07:43:46 -06:00
8f3b7b5b23 勘误, 第522行 (#1207)
声明一个可选常量或者变量->声明一个可选变量
2022-12-04 19:20:18 -06:00
d71b74bc10 fix: 修正描述 (#1210)
原文:Classes can use the class keyword instead.
https://docs.swift.org/swift-book/LanguageGuide/Subscripts.html
2022-12-04 19:20:08 -06:00
8c24b2e9c8 Update 02_Basic_Operators.md (#1211)
精准描述求余操作符,尤其是对负数求余
2022-12-04 19:19:36 -06:00
ef29944f75 Update 23_Opaque_Types.md (#1209)
Fix a typo in the original English code snippet where "smallTriangle" is misspelled as "smallTriange"
2022-11-22 19:24:15 -06:00
960616507f Update 01_The_Basics.md (#1204)
勘误,第608行
print("My number is \(muNumber)")  -> print("My number is \(myNumber)")
2022-10-24 21:11:39 -05:00
9751fa16db Update README.md (#1202) 2022-10-04 13:22:40 -05:00
a762239d2d Update README.md 2022-09-26 09:08:02 -05:00
77b5caff91 更新部分内容到 Swift 5.7 (#1200)
* 更新内容到 Swift 5.7

* 更新内容到 Swift 5.7

* 更新内容到 Swift 5.7

* update to Swift version 5.7

* 更新内容到 Swift 5.7

* 更新内容到 Swift 5.7

* 修正部分术语

* 更新内容到 Swift 5.7

* 更新内容到 Swift 5.7

* 标题格式修改

* 修改了部分用词

* 修改了代码块格式

* 修改了代码段及行内代码格式

* 修改了代码段及行内代码样式

* 按照排版要求重新修改了部分格式

* Delete 02_Lexical_Structure.md

* Delete 03_Types.md

* Delete 04_Expressions.md

* Delete 05_Statements.md

* Delete 07_Attributes.md

* Delete 10_Summary_of_the_Grammar.md

* 根据排版指南修改了部分格式

* 根据排版指南修改了部分格式

* Update source/03_language_reference/02_Lexical_Structure.md

Co-authored-by: Jie Liang <lj925184928@gmail.com>
2022-09-10 08:00:58 -05:00
df35244821 语病更正 (#1197)
* 语病更正

* 删除多余的反引号
2022-08-02 10:03:13 -05:00
2128ce6f51 Update 06_Declarations.md
#1195
2022-07-18 21:27:49 -05:00
69c79b8d02 fix tiny typo of wrong usage Chinese punctuation (#1191) 2022-06-19 20:45:41 -05:00
a1b4a69db2 fix typo (#1190)
* 校对: chapter2/03_Strings_and_Characters 方法名变化

index(of:) 已更名为: firstIndex(of:)

* Update 03_Strings_and_Characters.md

fix typo
2022-06-14 06:54:18 -05:00
363cec309c Update 03_Types.md (#1189) 2022-06-07 06:56:49 -05:00
739176f380 Update 21_Protocols.md (#1188) 2022-06-04 14:05:29 -05:00
2f77c8dc2d Update 20_Extensions.md (#1187) 2022-06-03 07:26:27 -05:00
8f21fcf5e3 Develop (#1186)
* 删除了一个多余'>'

* 修改几条语句和一个错误
2022-06-02 10:43:45 -05:00
e38b314d40 删除了一个多余'>' (#1185) 2022-05-31 06:58:28 -05:00
6205b4d2f8 删除了一些 (#1184) 2022-05-18 06:24:33 -05:00
60744b4033 修改病句,“一个段字符串”改为“一段字符串” (#1182) 2022-03-23 07:12:21 -05:00
5851c6fb8a Language Reference - Lexical Structure(Swift 5.5 beta -> Swift 5.5) (#1178)
* chore: 更新正式版内容

* chore: 更新文案
2021-12-13 12:19:33 -06:00
495cd079e1 Update 25_Memory_Safety.md 2021-12-09 09:19:46 -06:00
382c645a22 Update 03_Strings_and_Characters.md 2021-12-09 09:14:52 -06:00
9e8082c6c2 Update 03_a_swift_tour.md 2021-12-09 09:05:55 -06:00
be91198c30 Update 03_a_swift_tour.md 2021-12-09 09:05:04 -06:00
c7ab79f41c Add missing code snippet (#1172)
* Add missing code snippet

* Modify the snippet

Co-authored-by: nm4j <doco>
2021-12-09 09:02:47 -06:00
93212eca82 Update 03_a_swift_tour.md 2021-12-09 09:02:20 -06:00
0825cd43e5 Update README.md 2021-10-25 08:52:50 -05:00
aed8ee585b Update README.md 2021-10-25 08:52:10 -05:00
a40f423705 fix: 错别字弱应用改为弱引用 (#1170)
Co-authored-by: levilai <levilai@futunn.com>
2021-10-18 15:00:21 -05:00
bf09ec6368 Language Reference - Declarations (#1160)
* feat:添加部分内容的翻译

* feat:更新「访问控制级别」

* feat:更新「访问控制级别」

* fix:更新「访问控制级别」

* feat:更新 GRAMMAR OF A FUNCTION DECLARATION

* feat:更新 GRAMMAR OF A PRECEDENCE GROUP DECLARATION

* feat:更新 GRAMMAR OF A DECLARATION MODIFIER

* feat:更新 GRAMMAR OF A DECLARATION MODIFIER

* fix:补完剩下的内容

* fix:补上 GRAMMAR OF A PROTOCOL DECLARATION 修改的部分

* Update 06_Declarations.md

Co-authored-by: Jie Liang <lj925184928@gmail.com>
2021-09-21 20:56:03 -05:00
4d13d67217 修改错别字,理顺部分语句 (#1168) 2021-09-10 08:47:32 -05:00
e5756d5ca7 更新部分章节到 Swift 5.5 (#1166)
* Advanced_Operators Swift 5.3 beata

* feat: update Opaque Types Swift 5.5

* feat: update Properties Swift 5.5

* feat: update AdvancedOperators Swift 5.5

* fix: 根据校对意见修改

* feat: update Statements to Swift 5.5

* feat: [wip] experssion to Swift 5.5

* feat: update Expression to Swift 5.5

* feat: [wip] revision history to Swift 5.5

* feat: [wip] revision history to Swift 5.5

* feat: revision history to Swift 5.5

* fix: typo

* [WIP] ARC

* feat: update ARC to Swift 5.5

* Update 24_Automatic_Reference_Counting.md

* feat: update Type to Swift 5.5

Co-authored-by: Jie Liang <lj925184928@gmail.com>
2021-09-04 21:33:37 -05:00
32a078e84b fix: syntax highlighting (#1167)
For better reading experience XD
2021-09-04 21:32:35 -05:00
49e45e154e Merge pull request #1165 from muxinqi/patch-1
fix(typo): introducor -> introducer
2021-08-23 11:19:20 +08:00
c0b147b67a fix(typo): introducor -> introducer 2021-08-23 00:07:50 +08:00
1491926107 Update 20_Extensions.md 2021-08-18 11:40:14 -05:00
245f7268dc Update 14_Initialization.md 2021-08-18 11:32:00 -05:00
79076b3617 fix: 修改了并发中的若干问题 & 增加了贡献者 (#1164)
Co-authored-by: goupy <goupy@fenbi.com>
2021-08-18 11:22:17 -05:00
95e2a467fc feat: concurrency (#1162)
* feat: concurrency

* Update 28_Concurrency.md

Co-authored-by: goupy <goupy@fenbi.com>
Co-authored-by: Jie Liang <lj925184928@gmail.com>
2021-08-10 08:03:49 -05:00
e97a29d09f Chapter3-Attributes 新增 5.3 缺失内容 (#1159)
* chore: 更新方法名与 propertyWrapper 中对于本地变量的描述

* chore: 翻译 resultBuilder 与 Result-Building Methods

* feat: 新增结果构造器翻译

* chore: 修改结果构造方法的翻译

* chore: 修改结果转换的翻译

* chore: 修改不合适的翻译

* feat: 新增 main 节

Co-authored-by: WAMaker <wamaker@WAMakerdeMacBook-Pro-2.local>
2021-07-20 11:48:31 -05:00
deff7fe8b9 更新内容到 Swift 5.5 (#1158)
* Advanced_Operators Swift 5.3 beata

* feat: update Opaque Types Swift 5.5

* feat: update Properties Swift 5.5

* feat: update AdvancedOperators Swift 5.5

* fix: 根据校对意见修改

* feat: update Statements to Swift 5.5

* feat: [wip] experssion to Swift 5.5

* feat: update Expression to Swift 5.5

* feat: [wip] revision history to Swift 5.5

* feat: [wip] revision history to Swift 5.5

* feat: revision history to Swift 5.5

* fix: typo
2021-07-19 09:53:44 -05:00
b4f1d398ea Language Reference - Attributes (#1154)
* chore: 更新方法名与 propertyWrapper 中对于本地变量的描述

* chore: 翻译 resultBuilder 与 Result-Building Methods

* feat: 新增结果构造器翻译

* chore: 修改结果构造方法的翻译

* chore: 修改结果转换的翻译

* chore: 修改不合适的翻译

Co-authored-by: WAMaker <wamaker@WAMakerdeMacBook-Pro-2.local>
2021-07-18 20:58:44 -05:00
292399ba70 更新内容到 Swift 5.5 (#1156)
* Advanced_Operators Swift 5.3 beata

* feat: update Opaque Types Swift 5.5

* feat: update Properties Swift 5.5

* feat: update AdvancedOperators Swift 5.5

* fix: 根据校对意见修改

* feat: update Statements to Swift 5.5

* feat: [wip] experssion to Swift 5.5

* feat: update Expression to Swift 5.5
2021-07-17 08:52:25 -05:00
67c28841a7 更新部分章节到 Swift 5.5 (#1155)
* Advanced_Operators Swift 5.3 beata

* feat: update Opaque Types Swift 5.5

* feat: update Properties Swift 5.5

* feat: update AdvancedOperators Swift 5.5

* fix: 根据校对意见修改

* feat: update Statements to Swift 5.5
2021-07-13 09:16:11 -05:00
42bed2bf9a 更新部分章节到 Swift 5.5 (#1152)
* Advanced_Operators Swift 5.3 beata

* feat: update Opaque Types Swift 5.5

* feat: update Properties Swift 5.5

* feat: update AdvancedOperators Swift 5.5

* fix: 根据校对意见修改
2021-07-10 21:14:10 -05:00
a8f729a52d Language Guide - Functions (#1143)
* feat: 可变参数新增内容

* fix: 根据修改意见修改
2021-07-04 06:11:39 -05:00
80c2ac750d chore: upload docs 2021-06-27 00:13:49 +08:00
704d910495 fix:add translations (#1151) 2021-06-26 06:52:58 -05:00
0ab60a97c3 fix:add translations (#1150) 2021-06-26 06:52:44 -05:00
f43cb3fdb9 feat:示例代码更新 (#1149) 2021-06-26 06:52:30 -05:00
872d7af8a3 fix:示例代码更新 (#1148) 2021-06-22 13:03:11 -05:00
24d88f684a fix:示例代码更新 (#1147) 2021-06-22 13:03:02 -05:00
a15735f51b fix:for -> for-in (#1146) 2021-06-22 11:08:23 -05:00
aba2a5fb55 Language Guide - Structures and Classes (#1139)
* feat:添加 note,新建「并发」章节的 md 文件,修改文件顺序

* fix:删除冗余的字符串

* Revert "feat:添加 note,新建「并发」章节的 md 文件,修改文件顺序"

This reverts commit 898930b0ec.

# Conflicts:
#	source/02_language_guide/18_Concurrency.md

* feat:新增 `note` 一句

* feat:新建「并发」章节 md 文件

* fix:修改 SUMMARY.md

* Update 09_Structures_And_Classes.md

Co-authored-by: Jie Liang <lj925184928@gmail.com>
2021-06-22 10:56:03 -05:00
096f2acfc4 feat: 更新链接 (#1145) 2021-06-22 08:51:07 -05:00
8b99054c0c feat: 更新示例代码 (#1144) 2021-06-22 08:50:55 -05:00
d9d2952e85 feat:「参数名称缩写 」节第二段和第三段有更新 (#1142) 2021-06-21 18:54:23 -05:00
43 changed files with 2462 additions and 1139 deletions

View File

@ -11,9 +11,15 @@
# 当前阶段
- 更新到 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.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
@ -58,6 +64,7 @@ diff 操作如下:
| 术语 | 备选翻译 |
| --- | --- |
| result builder | 结果构造器 |
| property wrapper | 属性包装器([翻译相关讨论](https://github.com/SwiftGGTeam/the-swift-programming-language-in-chinese/issues/982#issuecomment-536244784) |
| projected value | 被呈现值 |
| wrapped value | 被包装值 |

Binary file not shown.

Binary file not shown.

View File

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

View File

@ -10,12 +10,6 @@ print("Hello, world!")
这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解到。
> 注意
>
> 最好的体验是把这一章作为 Playground 文件在 Xcode 中打开。 Playgrounds 允许你可以编辑代码并立刻看到输出结果。
>
> [下载 Playground](https://docs.swift.org/swift-book/GuidedTour/GuidedTour.playground.zip)
## 简单值 {#simple-values}
使用 `let` 来声明常量,使用 `var` 来声明变量。一个常量的值,在编译的时候,并不需要有明确的值,但是你只能为它赋值一次。这说明你可以用一个常量来命名一个值,一次赋值就可在多个地方使用。
@ -43,7 +37,7 @@ let explicitDouble: Double = 70
值永远不会被隐式转换为其他类型。如果你需要把一个值转换成其他类型,请显式转换。
```swift
let label = "The width is"
let label = "The width is "
let width = 94
let widthLabel = label + String(width)
```
@ -65,7 +59,7 @@ let fruitSummary = "I have \(apples + oranges) pieces of fruit."
>
> 使用 `\()` 来把一个浮点计算转换成字符串,并加上某人的名字,和他打个招呼。
使用三个双引号(`"""`)来包含多行字符串内容。每行行首的缩进会被去除,直到和结尾引号的缩进相匹配。举个例子:
使用三个双引号(`"""`)来包含多行字符串内容。每行行首的缩进会被去除,只要和结尾引号的缩进相匹配。举个例子:
```swift
let quotation = """
@ -154,6 +148,14 @@ let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickName ?? fullName)"
```
你还可以使用较短的代码解包一个值,并且对该被包装值使用相同的名称。
```swift
if let nickname {
print("Hey, \(nickName)")
}
```
`switch` 支持任意类型的数据以及各种比较操作——不仅仅是整数以及测试相等。
```swift
@ -542,7 +544,7 @@ if let convertedRank = Rank(rawValue: 3) {
}
```
枚举的关联值是实际值,并不是原始值的另一种表达方法。实际上,如果没有比较有意义的原始值,你就不需要提供原始值。
枚举值是实际值,并不是原始值的另一种表达方法。实际上,如果没有比较有意义的原始值,你就不需要提供原始值。
```swift
enum Suit {
@ -613,6 +615,52 @@ 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"
```
## 协议和扩展 {#protocols-and-extensions}
使用 `protocol` 来声明一个协议。
@ -815,4 +863,4 @@ anyCommonElements([1, 2, 3], [3])
>
> 修改 `anyCommonElements(_:_:)` 函数来创建一个函数,返回一个数组,内容是两个序列的共有元素。
`<T: Equatable>``<T> ... where T: Equatable>` 的写法是等价的。
`<T: Equatable>``<T> ... where T: Equatable` 的写法是等价的。

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` 的变量。”
@ -118,7 +118,7 @@ print(friendlyWelcome)
`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)")
@ -217,11 +217,11 @@ Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台
## 类型安全和类型推断 {#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")
@ -485,7 +485,7 @@ print("The status message is \(http200Status.description)")
## 可选类型 {#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,16 +587,38 @@ if let actualNumber = Int(possibleNumber) {
如果转换成功,`actualNumber` 常量可以在 `if` 语句的第一个分支中使用。它已经被可选类型 *包含的* 值初始化过,所以不需要再使用 `!` 后缀来获取它的值。在这个例子中,`actualNumber` 只被用来输出转换结果。
你可以在可选绑定中使用常量和变量。如果你想在 `if` 语句的第一个分支中操作 `actualNumber` 的值,你可以改成 `if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。
如果你在访问它包含的值后不需要引用原来的可选常量或是可选变量,你可以对新的常量或是新的变量使用相同的名称:
你可以包含多个可选绑定或多个布尔条件在一个 `if` 语句中,只要使用逗号分开就行。只要有任意一个可选绑定的值为 `nil`,或者任意一个布尔条件为 `false`,则整个 `if` 条件判断为 `false`。下面的两个 `if` 语句是等价的:
```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` 语句是等效的:
```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 {

View File

@ -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}
@ -106,7 +106,7 @@ Swift 中所有数值类型都支持了基本的四则*算术运算符*
-9 % 4 // 等于 -1
```
`-9``4` 代入等式,`-2` 是取到的最大整数:
`-9``4` 代入等式,`-2`使 `余数``-9` 同符号时能取到的最大整数:
-9 = (4 × -2) + -1

View File

@ -89,7 +89,7 @@ It also ends with a line break.
```swift
let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imageination is more important than knowledge" - Enistein
// "Imagination is more important than knowledge" - Einstein
let dollarSign = "\u{24}" // $Unicode 标量 U+0024
let blackHeart = "\u{2665}" // ♥Unicode 标量 U+2665
let sparklingHeart = "\u{1F496}" // 💖Unicode 标量 U+1F496
@ -416,7 +416,7 @@ for index in greeting.indices {
### 插入和删除 {#inserting-and-removing}
调用 `insert(_:at:)` 方法可以在一个字符串的指定索引插入一个字符,调用 `insert(contentsOf:at:)` 方法可以在一个字符串的指定索引插入一段字符串。
调用 `insert(_:at:)` 方法可以在一个字符串的指定索引插入一个字符,调用 `insert(contentsOf:at:)` 方法可以在一个字符串的指定索引插入一段字符串。
```swift
var welcome = "hello"
@ -460,7 +460,7 @@ let newString = String(beginning)
上面的例子,`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)
> 注意
>
@ -631,6 +631,11 @@ 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: "")
@ -674,6 +679,11 @@ 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_2x.png)
![](https://docs.swift.org/swift-book/images/CollectionTypes_intro~dark@2x.png)
Swift 中的数组、集合和字典必须明确其中保存的键和值类型,这样就可以避免插入一个错误数据类型的值。同理,对于获取到的值你也可以放心,其数据类型是确定的。
@ -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:)` 方法来判断一个集合中的所有值是否也被包含在另外一个集合中。

View File

@ -120,7 +120,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)
游戏的规则如下:
@ -422,11 +422,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 匹配同一个值。实际上,在这个例子中,点 (0, 0)可以匹配所有_四个 case_。但是,如果存在多个匹配,那么只会执行第一个被匹配到的 case 分支。考虑点 (0, 0)会首先匹配 `case (0, 0)`,因此剩下的能够匹配的分支都会被忽视掉。
不像 C 语言Swift 允许多个 case 匹配同一个值。实际上,在这个例子中,四个 `case` 都可以匹配点 (0, 0) 。但是,如果存在多个匹配,那么只会执行第一个被匹配到的 case 分支。考虑点 (0, 0)会首先匹配 `case (0, 0)`,因此剩下的能够匹配的分支都会被忽视掉。
#### 值绑定Value Bindings {#value-bindings}
@ -447,7 +447,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 +476,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` 上,或者不在对角线上。
@ -639,7 +639,7 @@ print(description)
为了实现这个目的,你可以使用标签(*statement label*)来标记一个循环体或者条件语句,对于一个条件语句,你可以使用 `break` 加标签的方式,来结束这个被标记的语句。对于一个循环语句,你可以使用 `break` 或者 `continue` 加标签,来结束或者继续这条被标记语句的执行。
声明一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签作为这个语句的前导关键字introducor keyword并且该标签后面跟随一个冒号。下面是一个针对 `while` 循环体的标签语法,同样的规则适用于所有的循环体和条件语句。
声明一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签作为这个语句的前导关键字introducer keyword并且该标签后面跟随一个冒号。下面是一个针对 `while` 循环体的标签语法,同样的规则适用于所有的循环体和条件语句。
```swift
label name: while condition {
@ -655,7 +655,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` 值被和之前一样的方式初始化:
@ -762,6 +762,38 @@ if #available(iOS 10, macOS 10.12, *) {
if #available(平台名称 版本号, ..., *) {
APIs 可用,语句将执行
} else {
APIs 不可用,语句将执行
APIs 不可用,使用先前版本API的语句将执行
}
```
当你在 `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,6 +225,9 @@ print(anotherGreeting(for: "Dave"))
正如你将会在 [简略的 Getter 声明](./10_Properties.md) 里看到的, 一个属性的 getter 也可以使用隐式返回的形式。
>注意
>作为隐式返回值编写的代码需要返回一些值。例如,你不能使用 `print(13)` 作为隐式返回值。然而,你可以使用不返回值的函数(如 `fatalError("Oh no!")`)作为隐式返回值,因为 Swift 知道它们并不会产生任何隐式返回。
## 函数参数标签和参数名称 {#Function-Argument-Labels-and-Parameter-Names}
@ -310,9 +313,7 @@ arithmeticMean(3, 8.25, 18.75)
// 返回 10.0, 是这 3 个数的平均数。
```
> 注意
>
> 一个函数最多只能拥有一个可变参数。
一个函数能拥有多个可变参数。可变参数后的第一个形参前必须加上实参标签。实参标签用于区分实参是传递给可变参数,还是后面的形参。
### 输入输出参数 {#in-out-parameters}
@ -456,7 +457,7 @@ func stepBackward(_ input: Int) -> Int {
}
```
如下名为 `chooseStepFunction(backward:)` 的函数,它的返回类型是 `(Int) -> Int` 类型的函数。`chooseStepFunction(backward:)` 根据布尔值 `backwards` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数:
如下名为 `chooseStepFunction(backward:)` 的函数,它的返回类型是 `(Int) -> Int` 类型的函数。`chooseStepFunction(backward:)` 根据布尔值 `backward` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数:
```swift
func chooseStepFunction(backward: Bool) -> (Int) -> Int {

View File

@ -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` 类型的参数。
在这个例子中,`$0``$1` 表示闭包中第一个和第二个 `String` 类型的参数。因为 `$1` 是编号最大的缩写参数,所以可以理解为:该闭包需要两个参数。这里的 `sorted(by:)` 函数希望得到一个参数都是字符串的闭包,因此缩写参数 `$0``$1` 的类型均为 `String`
### 运算符方法 {#operator-methods}
@ -214,6 +214,35 @@ 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}
闭包可以在其被定义的上下文中*捕获*常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。

View File

@ -114,7 +114,7 @@ print("\(numberOfChoices) beverages available")
// 打印“3 beverages available”
```
在前面的例子中,通过 `Beverage.allCases` 可以访问到包含 `Beverage` 枚举所有成员的集合。`allCases` 的使用方法和其它一般集合一样——集合中的元素是枚举类型的实例,所以在上面的情况中,这些元素是 `Beverage` 值。在前面的例子中,统计了总共有多少个枚举成员。而在下面的例子中,则使用 `for` 循环来遍历所有枚举成员。
在前面的例子中,通过 `Beverage.allCases` 可以访问到包含 `Beverage` 枚举所有成员的集合。`allCases` 的使用方法和其它一般集合一样——集合中的元素是枚举类型的实例,所以在上面的情况中,这些元素是 `Beverage` 值。在前面的例子中,统计了总共有多少个枚举成员。而在下面的例子中,则使用 `for-in` 循环来遍历所有枚举成员。
```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 码。

View File

@ -1,6 +1,6 @@
# 结构体和类
*结构体*和*类*作为一种通用而又灵活的结构,成为了人们构建代码的基础。你可以使用定义常量、变量和函数的语法,为你的结构体和类定义属性、添加方法。
*结构体*和*类*作为一种通用而又灵活的结构,成为了人们构建代码的基础。你可以使用定义常量、变量和函数的语法,为你的结构体和类定义属性、添加方法。
与其他编程语言所不同的是Swift 并不要求你为自定义的结构体和类的接口与实现代码分别创建文件。你只需在单一的文件中定义一个结构体或者类,系统将会自动生成面向其它代码的外部接口。
@ -32,6 +32,10 @@ Swift 中结构体和类有很多共同点。两者都可以:
类支持的附加功能是以增加复杂性为代价的。作为一般准则,优先使用结构体,因为它们更容易理解,仅在适当或必要时才使用类。实际上,这意味着你的大多数自定义数据类型都会是结构体和枚举。更多详细的比较参见 [在结构和类之间进行选择](https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes)。
> 注意
>
> 类和 actors 共享很多特性。更多信息请参见 [并发](./28_Concurrency.md)。
### 类型定义的语法 {#definition-syntax}
结构体和类有着相似的定义方式。你通过 `struct` 关键字引入结构体,通过 `class` 关键字引入类,并将它们的具体定义放在一对大括号中:
@ -125,7 +129,7 @@ let vga = Resolution(width: 640, height: 480)
Swift 中所有的结构体和枚举类型都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型的属性,在代码中传递的时候都会被复制。
> 注意
> 注意
>
> 标准库定义的集合,例如数组,字典和字符串,都对复制进行了优化以降低性能成本。新集合不会立即复制,而是跟原集合共享同一份内存,共享同样的元素。在集合的某个副本要被修改前,才会复制它的元素。而你在代码中看起来就像是立即发生了复制。
@ -162,7 +166,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)
枚举也遵循相同的行为准则:
@ -210,7 +214,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`
@ -221,7 +225,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,4 +251,4 @@ if tenEighty === alsoTenEighty {
### 指针 {#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

@ -69,7 +69,7 @@ class DataImporter {
class DataManager {
lazy var importer = DataImporter()
var data = [String]()
var data: [String] = []
// 这里会提供数据管理功能
}
@ -105,7 +105,7 @@ Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属
## 计算属性 {#computed-properties}
除存储属性外,类、结构体和枚举可以定义*计算属性*。计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter来间接获取和设置其他属性或变量的值。
除存储属性外,类、结构体和枚举可以定义*计算属性*。计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter来间接获取和设置其他属性或变量的值。
```swift
struct Point {
@ -132,6 +132,7 @@ 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)”
@ -147,11 +148,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}
@ -294,8 +295,7 @@ stepCounter.totalSteps = 896
```swift
@propertyWrapper
struct TwelveOrLess {
private var number: Int
init() { self.number = 0 }
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
@ -303,12 +303,12 @@ struct TwelveOrLess {
}
```
这个 setter 确保新值小于 12而且返回被存储的值。
这个 setter 确保新值小于或等于 12而且返回被存储的值。
> 注意
>
> 上面例子以 `private` 的方式声明 `number` 变量,这使得 `number` 仅在 `TwelveOrLess` 的实现中使用。写在其他地方的代码通过使用 `wrappedValue` 的 getter 和 setter 来获取这个值,但不能直接使用 `number`。有关 `private` 的更多信息,请参考 [访问控制](./26_Access_Control.md)
通过在属性之前写上包装器名称作为特性的方式,你可以把一个包装器应用到一个属性上去。这里有个存储小矩形的结构体通过 `TwelveOrLess` 属性包装器实现类似(挺随意的)对“小”的定义
通过在属性之前写上包装器名称作为特性的方式,你可以把一个包装器应用到一个属性上去。这里有个存储小矩形的结构体通过 `TwelveOrLess` 属性包装器来确保它的长宽均小于等于 12
```swift
struct SmallRectangle {
@ -463,11 +463,8 @@ print(mixedRectangle.height)
@propertyWrapper
struct SmallNumber {
private var number: Int
var projectedValue: Bool
init() {
self.number = 0
self.projectedValue = false
}
private(set) var projectedValue: Bool
var wrappedValue: Int {
get { return number }
set {
@ -480,6 +477,11 @@ struct SmallNumber {
}
}
}
init() {
self.number = 0
self.projectedValue = false
}
}
struct SomeStructure {
@SmallNumber var someNumber: Int
@ -540,6 +542,22 @@ struct SizedRectangle {
>
> 局部范围的常量和变量从不延迟计算。
可以在局部存储型变量上使用属性包装器,但不能在全局变量或者计算型变量上使用。比如下面的代码,`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}
实例属性属于一个特定类型的实例,每创建一个实例,实例都拥有属于自己的一套属性值,实例之间的属性相互独立。
@ -610,7 +628,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

@ -105,7 +105,7 @@ print("The point is now at (\(somePoint.x), \(somePoint.y))")
// 打印“The point is now at (3.0, 4.0)”
```
上面的 `Point` 结构体定义了一个可变方法 `moveByxy :)` 来移动 `Point` 实例到给定的位置。该方法被调用时修改了这个点,而不是返回一个新的点。方法定义时加上了 `mutating` 关键字,从而允许修改属性。
上面的 `Point` 结构体定义了一个可变方法 `moveBy(x:y:)` 来移动 `Point` 实例到给定的位置。该方法被调用时修改了这个点,而不是返回一个新的点。方法定义时加上了 `mutating` 关键字,从而允许修改属性。
注意不能在结构体类型的常量a constant of structure type上调用可变方法因为其属性不能被改变即使属性是变量属性详情参见 [常量结构体的存储属性](./10_Properties.md#stored-properties-of-constant-structure-instances)

View File

@ -72,8 +72,8 @@ numberOfLegs["bird"] = 2
## 下标选项 {#subscript-options}
下标可以接受任意数量的入参,并且这些入参可以是任何类型。下标的返回值也可以是任意类型。
下标可以接受任意数量的入参,并且这些入参可以是任何类型。下标的返回值也可以是任意类型。
与函数一样,下标可以接受不同数量的参数,并且为这些参数提供默认值,如在[可变参数](./06_Functions.md#variadic-parameters) 和 [默认参数值](./06_Functions.md#default-parameter-values) 中所述。但是,与函数不同的是,下标不能使用 in-out 参数。
一个类或结构体可以根据自身需要提供多个下标实现,使用下标时将通过入参的数量和类型进行区分,自动匹配合适的下标。它通常被称为*下标的重载*。
@ -115,7 +115,7 @@ var matrix = Matrix(rows: 2, columns: 2)
上例中创建了一个两行两列的 `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 +126,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` 的值是否在矩阵范围内:
@ -143,8 +143,8 @@ let someValue = matrix[2, 2]
// 断言将会触发,因为 [2, 2] 已经超过了 matrix 的范围
```
## 类型下标{#type-subscripts}
正如上节所述实例下标是在特定类型的一个实例上调用的下标。你也可以定义一种在这个类型自身上调用的下标。这种下标被称作_类型下标_。你可以通过在 `subscript` 关键字之前写下 `static` 关键字的方式来表示一个类型下标。类型可以使用 `class` 关键字来代替 `static`,它允许子类重写父类中对那个下标的实现。下面的例子展示了如何定义和调用一个类型下标:
## 类型下标 {#type-subscripts}
正如上节所述实例下标是在特定类型的一个实例上调用的下标。你也可以定义一种在这个类型自身上调用的下标。这种下标被称作_类型下标_。你可以通过在 `subscript` 关键字之前写下 `static` 关键字的方式来表示一个类型下标。类型可以使用 `class` 关键字来代替 `static`,它允许子类重写父类中对那个下标的实现。下面的例子展示了如何定义和调用一个类型下标:
```
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune

View File

@ -69,7 +69,7 @@ class Bicycle: Vehicle {
除了所继承的特性,`Bicycle` 类还定义了一个默认值为 `false` 的存储型属性 `hasBasket`(属性推断为 `Bool`)。
默认情况下,你创建的所有新的 `Bicycle` 实例不会有一个篮子(即 `hasBasket` 属性默认为 `false`)。创建该实例之后,你可以为 `Bicycle` 实例设置 `hasBasket` 属性为 `ture`
默认情况下,你创建的所有新的 `Bicycle` 实例不会有一个篮子(即 `hasBasket` 属性默认为 `false`)。创建该实例之后,你可以为 `Bicycle` 实例设置 `hasBasket` 属性为 `true`
```swift
let bicycle = Bicycle()
@ -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}
@ -225,7 +225,7 @@ var item = ShoppingListItem()
### 结构体的逐一成员构造器 {#memberwise-initializers-for-structure-types}
结构体如果没有定义任何自定义构造器,它们将自动获得一个*逐一成员构造器memberwise initializer*。不像默认构造器,即使存储型属性没有默认值,结构体也会获得逐一成员构造器。
结构体如果没有定义任何自定义构造器,它们将自动获得一个*逐一成员构造器memberwise initializer*。不像默认构造器,即使存储型属性没有默认值,结构体也会获得逐一成员构造器。
逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法。新实例的属性初始值可以通过名字传入逐一成员构造器中。
@ -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()
@ -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,7 +475,7 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
### 构造器的继承和重写 {#initializer-inheritance-and-overriding}
跟 Objective-C 中的子类不同Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更精细的子类继承,而在用来创建子类的新实例时没有完全或错误被初始化。
跟 Objective-C 中的子类不同Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更精细的子类继承,而在用来创建子类的新实例时没有完全或错误被初始化。
> 注意
>
@ -485,7 +485,7 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
当你在编写一个和父类中指定构造器相匹配的子类构造器时,你实际上是在重写父类的这个指定构造器。因此,你必须在定义子类构造器时带上 `override` 修饰符。即使你重写的是系统自动提供的默认构造器,也需要带上 `override` 修饰符,具体内容请参考 [默认构造器](#default-initializers)。
正如重写属性,方法或者是下标,`override` 修饰符会让编译器去检查父类中是否有相匹配的指定构造器,并验证构造器参数是否按预想中被指定。
正如重写属性,方法或者是下标,`override` 修饰符会让编译器去检查父类中是否有相匹配的指定构造器,并验证构造器参数是否按预想中被指定。
> 注意
>
@ -535,9 +535,9 @@ print("Bicycle: \(bicycle.description)")
// 打印“Bicycle: 2 wheel(s)”
```
如果子类的构造器没有在阶段 2 过程中做自定义操作,并且父类有一个无参数的指定构造器,你可以在所有子类的存储属性赋值之后省略 `super.init()` 的调用。
如果子类的构造器没有在阶段 2 过程中做自定义操作,并且父类有一个同步、无参数的指定构造器,你可以在所有子类的存储属性赋值之后省略 `super.init()` 的调用。若父类有一个异步的构造器,你就需要明确地写入 `await 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,7 +640,7 @@ 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。
@ -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

@ -16,7 +16,7 @@ deinit {
析构器是在实例释放发生前被自动调用的。你不能主动调用析构器。子类继承了父类的析构器,并且在子类析构器实现的最后,父类的析构器会被自动调用。即使子类没有提供自己的析构器,父类的析构器也同样会被调用。
因为直到实例的析构器被调用后,实例才会被释放,所以析构器可以访问实例的所有属性,并且可以根据那些属性可以修改它的行为(比如查找一个需要被关闭的文件)。
因为直到实例的析构器被调用后,实例才会被释放,所以析构器可以访问实例的所有属性,并且可以根据那些属性修改它的行为(比如查找一个需要被关闭的文件)。
## 析构器实践 {#deinitializers-in-action}

View File

@ -97,7 +97,7 @@ class Person {
```swift
class Residence {
var rooms = [Room]()
var rooms: [Room] = []
var numberOfRooms: Int {
return rooms.count
}

View File

@ -141,7 +141,7 @@ Swift 为不确定类型提供了两种特殊的类型别名:
这里有个示例,使用 `Any` 类型来和混合的不同类型一起工作,包括函数类型和非类类型。它创建了一个可以存储 `Any` 类型的数组 `things`
```swift
var things = [Any]()
var things: [Any] = []
things.append(0)
things.append(0.0)

View File

@ -11,7 +11,7 @@ Swift 中的扩展可以:
- 定义和使用新的嵌套类型
- 使已经存在的类型遵循conform一个协议
在 Swift 中,你甚至可以扩展协议以提供其需要的实现,或者添加额外功能给遵循的类型所使用。你可以从 [协议扩展](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID521) 获取更多细节。
在 Swift 中,你甚至可以扩展协议以提供其需要的实现,或者添加额外功能给遵循的类型所使用。你可以从 [协议扩展](./21_Protocols.md#protocol-extensions) 获取更多细节。
> 注意
>
@ -35,9 +35,9 @@ extension SomeType: SomeProtocol, AnotherProtocol {
}
```
这种遵循协议的方式在 [使用扩展遵循协议](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID277) 中有描述。
这种遵循协议的方式在 [在扩展里添加协议遵循](./21_Protocols.md#adding-protocol-conformance-with-an-extension) 中有描述。
扩展可以使用在现有型类型上,就像 [扩展范型类型](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID185) 中描述的一样。你还可以使用扩展给泛型类型有条件的添加功能,就像 [扩展一个带有 Where 句的范型](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID553) 中描述的一样。
扩展可以使用在现有型类型上,就像 [泛型扩展](./22_Generics.md#extending-a-generic-type) 中描述的一样。你还可以使用扩展给泛型类型有条件的添加功能,就像 [具有泛型 Where 句的扩展](./22_Generics.md#extensions-with-a-generic-where-clause) 中描述的一样。
> 注意
>
@ -87,7 +87,7 @@ print("A marathon is \(aMarathon) meters long")
扩展可以给一个类添加新的便利构造器,但是它们不能给类添加新的指定构造器或者析构器。指定构造器和析构器必须始终由类的原始实现提供。
如果你使用扩展给一个值类型添加构造器,而这个值类型已经为所有存储属性提供默认值,且没有定义任何自定义构造器,那么你可以在该值类型扩展的构造器中使用默认构造器和成员构造器。如果你已经将构造器写在值类型的原始实现中,则不适用于这种情况,如同 [值类型的构造器委托](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID215) 中所描述的那样。
如果你使用扩展给一个值类型添加构造器,而这个值类型已经为所有存储属性提供默认值,且没有定义任何自定义构造器,那么你可以在该值类型扩展的构造器中使用默认构造器和成员构造器。如果你已经将构造器写在值类型的原始实现中,则不适用于这种情况,如同 [值类型的构造器代理](./14_Initialization.md#initializer-delegation-for-value-types) 中所描述的那样。
如果你使用扩展给另一个模块中定义的结构体添加构造器,那么新的构造器直到定义模块中使用一个构造器之前,不能访问 `self`
@ -106,7 +106,7 @@ struct Rect {
}
```
因为 `Rect` 结构体给所有的属性都提供了默认值,所以它自动获得了一个默认构造器和一个成员构造器,就像 [默认构造器](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID213) 中描述的一样。这些构造器可以用来创建新的 `Rect` 实例:
因为 `Rect` 结构体给所有的属性都提供了默认值,所以它自动获得了一个默认构造器和一个成员构造器,就像 [默认构造器](./14_Initialization.md#default-initializers) 中描述的一样。这些构造器可以用来创建新的 `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` 结构体来提供一个允许指定 point 和 size 的构造器:
你可以通过扩展 `Rect` 结构体来提供一个允许指定 center 和 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

@ -105,7 +105,7 @@ var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
```swift
protocol SomeProtocol {
static func someTypeMethod()
static func someTypeMethod()
}
```
@ -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.md#downcasting)。
`Dice` 类还有一个构造器,用来设置初始状态。构造器有一个名为 `generator`,类型为 `RandomNumberGenerator` 的形参。在调用构造方法创建 `Dice` 的实例时,可以传入任何遵循 `RandomNumberGenerator` 协议的实例给 `generator`
@ -632,7 +632,7 @@ print(game.prettyTextualDescription)
## 类专属的协议 {#class-only-protocol}
你通过添加 `AnyObject` 关键字到协议的继承列表,就可以限制协议只能被类类型采纳(以及非结构体或者枚举类型)。
你通过添加 `AnyObject` 关键字到协议的继承列表,就可以限制协议只能被类类型遵循(而不能是结构体类型或者枚举类型)。
```swift
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
@ -675,7 +675,7 @@ 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` 同时遵循这两个协议,所以这个参数合法,函数将打印生日问候语。
@ -826,7 +826,7 @@ 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)。
@ -857,7 +857,7 @@ for _ in 1...4 {
// 12
```
上述代码新建了一个 `Counter` 实例,并将它的数据源设置为一个 `ThreeSource` 的实例,然后调用 `increment()` 方法 `4` 次。按照预期预期一样,每次调用都会将 `count` 的值增加 `3`.
上述代码新建了一个 `Counter` 实例,并将它的数据源设置为一个 `ThreeSource` 的实例,然后调用 `increment()` 方法 `4` 次。正如预期一样,每次调用都会将 `count` 的值增加 `3`.
下面是一个更为复杂的数据源 `TowardsZeroSource`,它将使得最后的值变为 `0`

View File

@ -123,7 +123,7 @@ swapTwoValues(&someString, &anotherString)
下图展示了入栈push和出栈pop的行为
![](https://docs.swift.org/swift-book/_images/stackPushPop_2x.png)
![](https://docs.swift.org/swift-book/images/stackPushPop~dark@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_2x.png)
![](https://docs.swift.org/swift-book/images/stackPushedFourStrings~dark@2x.png)
移除并返回栈顶部的值“cuatro”即出栈
@ -197,7 +197,7 @@ let fromTheTop = stackOfStrings.pop()
下图展示了如何将顶部的值出栈:
![](https://docs.swift.org/swift-book/_images/stackPoppedOneString_2x.png)
![](https://docs.swift.org/swift-book/images/stackPoppedOneString~dark@2x.png)
## 泛型扩展 {#extending-a-generic-type}
@ -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)
}
@ -721,7 +721,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

@ -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))
}
@ -133,7 +133,7 @@ print(opaqueJoinedTriangles.draw())
// *
```
这个例子中 `opaqueJoinedTriangles` 的值和前文 [不透明类型解决的问题](#the-problem-that-opaque-types-solve) 中关于泛型的那个例子中的 `joinedTriangles` 完全一样。不过和前文不一样的是,`flip(-:)``join(-:-:)` 将对泛型参数的操作后的返回结果包装成了不透明类型,这样保证了在结果中泛型参数类型不可见。两个函数都是泛型函数,因为他们都依赖于泛型参数,而泛型参数又将 `FlippedShape``JoinedShape` 所需要的类型信息传递给它们。
这个例子中 `opaqueJoinedTriangles` 的值和前文 [不透明类型解决的问题](#the-problem-that-opaque-types-solve) 中关于泛型的那个例子中的 `joinedTriangles` 完全一样。不过和前文不一样的是,`flip(_:)``join(_:_:)` 将对泛型参数的操作后的返回结果包装成了不透明类型,这样保证了在结果中泛型参数类型不可见。两个函数都是泛型函数,因为他们都依赖于泛型参数,而泛型参数又将 `FlippedShape``JoinedShape` 所需要的类型信息传递给它们。
如果函数中有多个地方返回了不透明类型,那么所有可能的返回值都必须是同一类型。即使对于泛型函数,不透明返回类型可以使用泛型参数,但仍需保证返回类型唯一。比如,下面就是一个*非法*示例 —— 包含针对 `Square` 类型进行特殊处理的翻转函数。
@ -207,7 +207,7 @@ protoFlippedTriangle == sameThing // 错误
将协议类型作为函数的返回类型能更加灵活,函数只要返回遵循协议的类型即可。然而,更具灵活性导致牺牲了对返回值执行某些操作的能力。上面的例子就说明了为什么不能使用 == 运算符 —— 它依赖于具体的类型信息,而这正是使用协议类型所无法提供的。
这种方法的另一个问题在于,变换形状的操作不能嵌套。翻转三角形的结果是一个 `Shape` 类型的值,而 `protoFlip(_:)` 方法则将遵循 `Shape` 协议的类型作为形参,然而协议类型的值并不遵循这个协议;`protoFlip(_:)` 的返回值也并不遵循 `Shape` 协议。这就是说 `protoFlip(protoFlip(smallTriange))` 这样的多重变换操作是非法的,因为经过翻转操作后的结果类型并不能作为 `protoFlip(_:)` 的形参。
这种方法的另一个问题在于,变换形状的操作不能嵌套。翻转三角形的结果是一个 `Shape` 类型的值,而 `protoFlip(_:)` 方法则将遵循 `Shape` 协议的类型作为形参,然而协议类型的值并不遵循这个协议;`protoFlip(_:)` 的返回值也并不遵循 `Shape` 协议。这就是说 `protoFlip(protoFlip(smallTriangle))` 这样的多重变换操作是非法的,因为经过翻转操作后的结果类型并不能作为 `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` 协议中的 `subscipt` 方法会返回 `Item`,这也意味着 `twelve` 的类型也被能推断出为 `Int`
`twelve` 的类型可以被推断出为 `Int` 这说明了类型推断适用于不透明类型。在 `makeOpaqueContainer(item:)` 的实现中,底层类型是不透明集合 `[T]`。在上述这种情况下,`T` 就是 `Int` 类型,所以返回值就是整数数组,而关联类型 `Item` 也被推断出为 `Int``Container` 协议中的 `subscript` 方法会返回 `Item`,这也意味着 `twelve` 的类型也被能推断出为 `Int`

View File

@ -1,6 +1,6 @@
# 自动引用计数
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)。
@ -129,7 +129,7 @@ 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` 中的实例,这样实例的属性才能被赋值:
@ -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` 实例之间的强引用关系保留了下来并且不会被断开。
@ -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` 实例的强引用了:
@ -232,7 +232,7 @@ unit4A = nil
由于再也没有指向 `Apartment` 实例的强引用,该实例会被销毁:
![](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`,因为非可选类型的变量不允许被赋值`nil`
但和弱引用不同,无主引用通常都被期望拥有值。所以将值标记为无主引用不会将它变为可选类型ARC 也不会将无主引用的值设置`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,6 +321,64 @@ 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}
上面弱引用和无主引用的例子涵盖了两种常用的需要打破循环强引用的场景。
@ -371,7 +429,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}
@ -451,7 +509,7 @@ 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))。
@ -549,7 +607,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` 实例将会被销毁,并能看到它的析构器打印出的消息:

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/code_diagnostics/thread_sanitizer) 去帮助检测多线程的冲突。
> 如果你曾经在单线程代码里有访问冲突Swift 可以保证你在编译或者运行时会得到错误。对于多线程的代码,可以使用 [Thread Sanitizer](https://developer.apple.com/documentation/xcode/diagnosing_memory_thread_and_crash_issues_early) 去帮助检测多线程的冲突。
### 内存访问性质 {#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

@ -31,7 +31,7 @@ Swift 为代码中的实体提供了五种不同的*访问级别*。这些访问
open 为最高访问级别限制最少private 为最低访问级别(限制最多)。
open 只能作用于类和类的成员,它和 public 的区别主要在于 open 限定的类和成员能够在模块外被继承和重写,在下面的 [子类](#subclassing) 这一节中有详解。将类的访问级别显式指定为 `open` 表明你已经设计好了类的代码,并且充分考虑过这个类在其他模块中用作父类时的影响。
open 只能作用于类和类的成员,它和 public 的区别主要在于 open 限定的类和成员能够在模块外被继承和重写,在下面的 [子类](#subclassing) 这一节中有详解。将类的访问级别显式指定为 `open` 表明你已经设计好了类的代码,并且充分考虑过这个类在其他模块中用作父类时的影响。
### 访问级别基本原则 {#guiding-principle-of-access-levels}
@ -46,7 +46,7 @@ Swift 中的访问级别遵循一个基本原则:*实体不能定义在具有
### 默认访问级别 {#default-access-levels}
你代码中所有的实体,如果你不显式的指定它们的访问级别,那么它们将都有一个 `internal` 的默认访问级别,(有一些例外情况,本文稍后会有说明)。因此,多数情况下你不需要显指定实体的访问级别。
你代码中所有的实体,如果你不显式的指定它们的访问级别,那么它们将都有一个 `internal` 的默认访问级别,(有一些例外情况,本文稍后会有说明)。因此,多数情况下你不需要显指定实体的访问级别。
### 单 target 应用程序的访问级别 {#access-levels-for-single-target-apps}
@ -127,7 +127,7 @@ private class SomePrivateClass { // 显式 private 类
> 注意
>
> 元组不同于类、结构体、枚举、函数那样有单独的定义。一个元组的访问级别由元组中元素的访问级别来决定的,不能被显指定。
> 元组不同于类、结构体、枚举、函数那样有单独的定义。一个元组的访问级别由元组中元素的访问级别来决定的,不能被显指定。
### 函数类型 {#function-types}
@ -305,7 +305,7 @@ public struct TrackedString {
### 协议继承 {#protocol-inheritance}
如果定义了一个继承自其他协议的新协议,那么新协议拥有的访问级别最高也只能和被继承协议的访问级别相同。例如,你不能将继承自 `internal` 协议的新协议访问级别指定为 `public` 协议
如果定义了一个继承自其他协议的新协议,那么新协议拥有的访问级别最高也只能和被继承协议的访问级别相同。例如,你不能将继承自 `internal` 协议的新协议访问级别指定为 `public`
### 协议遵循 {#protocol-conformance}

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,7 +208,7 @@ 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) 所描述的。
@ -221,7 +221,7 @@ signedOverflow = signedOverflow &- 1
`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)
对于无符号与有符号整型数值来说,当出现上溢时,它们会从数值所能容纳的最大数变成最小数。同样地,当发生下溢时,它们会从所能容纳的最小数变成最大数。
@ -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}
@ -444,3 +444,149 @@ let plusMinusVector = firstVector +- secondVector
>
> 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。
## 结果构造器 {#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

@ -0,0 +1,298 @@
# 并发
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
}
```

File diff suppressed because it is too large Load Diff

View File

@ -129,7 +129,7 @@ someTuple = (left: 5, right: 5) // 错误:命名类型不匹配
你可以对形参类型为 `() -> T`(其中 T 是任何类型)的函数使用 `autoclosure` 特性,这会在调用侧隐式创建一个闭包。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被调用。以自动闭包做为形参的函数类型的例子详见 [自动闭包](../02_language_guide/07_Closures.md#autoclosures)。
函数类型可以拥有个可变参数在*形参类型*中。从语法角度上讲,可变参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变参数的例子,请参阅 [可变参数](../02_language_guide/06_Functions.md#variadic-parameters)。
函数类型可以拥有个可变参数在*形参类型*中。从语法角度上讲,可变参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变参数的例子,请参阅 [可变参数](../02_language_guide/06_Functions.md#variadic-parameters)。
为了指定一个 `in-out` 参数,可以在形参类型前加 `inout` 前缀。但是你不可以对可变参数或返回值类型使用 `inout`。关于这种形参的详细讲解请参阅 [输入输出参数](../02_language_guide/06_Functions.md#in-out-parameters)。
@ -164,7 +164,9 @@ var operation: (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)。
使用函数类型的函数若要抛出或重抛错误就必须使用 `throws` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数的子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](./06_Declarations.md#throwing-functions-and-methods) 和 [重抛函数与方法](./06_Declarations.md#rethrowing-functions-and-methods)。
异步函数的函数类型必须使用 `async` 关键字来标记。 `async` 关键字也是函数类型的一部分,且同步函数是异步函数的子类型。因此,在使用异步函数的地方也可以使用同步函数。异步函数的相关描述见章节 [异步函数和方法](Declarations.xhtml#ID647)。
### 对非逃逸闭包的限制 {#Restrictions for Nonescaping Closures}
当非逃逸闭包函数是形参时,不能存储在属性、变量或任何 `Any` 类型的常量中,因为这可能导致值的逃逸。
@ -187,7 +189,7 @@ func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) {
在上面代码里,`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)。
@ -195,7 +197,7 @@ func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) {
>
#### function-type {#function-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) **async**<sub>可选</sub>**throws**<sub>可选</sub> **->** [类型](#type)
#### function-type-argument-clause {#function-type-argument-clause}
> *函数类型子句* → **(**­ **)**­
@ -304,7 +306,7 @@ optionalInteger! // 42
>
## 隐式解析可选类型 {#implicitly-unwrapped-optional-type-h}
当可以被访问时,Swift 语言定义后缀 `!` 作为标准库中命名类型 `Optional<Wrapped>` 的语法糖,来实现自动解包的功能。如果尝试对一个值为 `nil` 的可选类型进行隐式解包,将会产生运行时错误。因为隐式解包,下面两个声明等价:
Swift 语言定义后缀 `!` 作为标准库中命名类型 `Optional<Wrapped>` 的语法糖,其附加行为是在访问时自动解包。如果尝试对一个值为 `nil` 的可选类型进行隐式解包,将会产生运行时错误。除了隐式解包,下面两个声明等价:
```swift
var implicitlyUnwrappedString: String!
@ -345,7 +347,7 @@ let implicitlyUnwrappedArray: [Int]! // 正确
> `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`
协议合成列表中的每一项都必须是下面所列情况之一,列表中最多只能包含一个类:
@ -372,9 +374,9 @@ typealias PQR = PQ & Q & R
## 不透明类型 {#opaque-type-h}
*不透明类型*定义了遵循某个协议或者合成协议的类型,但不需要指明底层的具体类型。
*不透明类型*定义了一个遵循某个协议或者合成协议的类型,但不需要指明底层的具体类型。
不透明类型可以作为函数或下标的返回值,亦或是属性的类型使用。
不透明类型可以作为函数或下标的返回值类型,亦或是属性的类型使用。
不透明类型不能作为元组类型的一部分或范型类型使用,比如数组元素类型或者可选值的包装类型。
@ -382,11 +384,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,6 +446,38 @@ 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**
## 自身类型 {#self-type-h}
`Self` 类型不是具体的类型,而是让你更方便的引用当前类型,不需要重复或者知道该类的名字。
@ -457,7 +491,7 @@ let anotherInstance = metatype.init(string: "some string")
* 作为只读计算属性的类型
* 在方法体中
举个例子,下面的代码演示了返回值是 `Self` 的实例方法 `f`
举个例子,下面的代码演示了返回值类型`Self` 的实例方法 `f`
```swift
class Superclass {
@ -507,7 +541,7 @@ print(type(of: z.f()))
>
#### type-inheritance-list {#type-inheritance-list}
> *类型继承列表* → [类型标识符](#type-identifier) | [类型标识符](#type-identifier) **,** [类型继承列表](#type-inheritance-list)
> *类型继承列表* → [属性](./07_Attributes.md#attributes)<sub>可选</sub> [类型标识符](#type-identifier) | [属性](./07_Attributes.md#attributes)<sub>可选</sub> [类型标识符](#type-identifier) **,** [类型继承列表](#type-inheritance-list)
## 类型推断

View File

@ -1,19 +1,17 @@
# 表达式Expressions
Swift 中存在四种表达式:前缀表达式,二元表达式,基本表达式和后缀表达式。表达式在返回一个值的同时还可以引发副作用。
Swift 中存在四种表达式:前缀表达式,中缀表达式,基本表达式和后缀表达式。表达式在返回一个值的同时还可以引发副作用。
通过前缀表达式和二元表达式可以对简单表达式使用各种运算符。基本表达式从概念上讲是最简单的一种表达式,它是一种访问值的方式。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。
通过前缀表达式和中缀表达式可以对简单表达式使用各种运算符。基本表达式从概念上讲是最简单的一种表达式,它是一种访问值的方式。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。
> 表达式语法
>
#### expression {#expression}
> *表达式* → [try 运算符](#try-operator)<sub>可选</sub> [前缀表达式](#prefix-expression) [二元表达式列表](#binary-expressions)<sub>可选</sub>
> *表达式* → [try 运算符](#try-operator)<sub>可选</sub> [await 运算符](#await-operator)<sub>可选</sub> [前缀表达式](#prefix-expression) [中缀表达式列表](#infix-expressions)<sub>可选</sub>
#### expression-list {#expression-list}
> *表达式列表* → [表达式](#expression) | [表达式](#expression) **,** [表达式列表](#expression-list)
>
## 前缀表达式 {#prefix-expressions}
前缀表达式由可选的前缀运算符和表达式组成。前缀运算符只接收一个参数,表达式则紧随其后。
@ -25,61 +23,110 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表
除了标准库运算符,你也可以对某个变量使用 `&` 运算符,从而将其传递给函数的输入输出参数。更多信息,请参阅 [输入输出参数](../02_language_guide/06_Functions.md#in-out-parameters)。
> 前缀表达式语法
>
#### prefix-expression {#prefix-expression}
> *前缀表达式* → [前缀运算符](./02_Lexical_Structure.md#prefix-operator)<sub>可选</sub> [后缀表达式](#postfix-expression)
>
> *前缀表达式* → [输入输出表达式](#in-out-expression)
>
### 输入输出表达式
*输入输出表达式* 将函数调用表达式传入的变量标记为输入输出实参。
> &`表达式`
更多关于输入输出形参的信息和例子,请参阅 [输入输出形参](../02_language_guide/06_Functions.md#in-out-parameters)。
输入输出表达式也可以用于将非指针实参传入到需要指针的上下文中,如 [指针类型的隐式转换](#implicit-conversion-to-a-pointer) 中所述。
#### in-out-expression {#in-out-expression}
> *输入输出表达式* → **&** [标识符](./02_Lexical_Structure.md#identifier)
>
### Try 运算符 {#try-operator}
try 表达式由 `try` 运算符加上紧随其后的可抛出错误的表达式组成,形式如下:
> try `可抛出错误的表达式`
>
*try 表达式*由 `try` 运算符加上紧随其后的可抛出错误的表达式组成,形式如下:
可选的 try 表达式由 `try?` 运算符加上紧随其后的可抛出错误的表达式组成,形式如下:
> try `表达式`
> try? `可抛出错误的表达式`
>
`try` 表达式的返回值是该*表达式*的值。
如果可抛出错误的表达式没有抛出错误,整个表达式返回的可选值将包含可抛出错误的表达式的返回值,否则,该可选值为 `nil`
*可选 try 表达式*由 `try?` 运算符加上紧随其后的可抛出错误的表达式组成,形式如下:
强制的 try 表达式由 `try!` 运算符加上紧随其后的可抛出错误的表达式组成,形式如下:
> try? `表达式`
> try! `可抛出错误的表达式`
>
如果*表达式*没有抛出错误,可选 try 表达式的返回值是可选的该*表达式*的值,否则,返回值为 `nil`
如果可抛出错误的表达式抛出了错误,将会引发运行时错误。
*强制 try 表达式*由 `try!` 运算符加上紧随其后的可抛出错误的表达式组成,形式如下:
在二元运算符左侧的表达式被标记上 `try``try?` 或者 `try!` 时,这个运算符对整个二元表达式都产生作用。也就是说,你可以使用括号来明确运算符的作用范围。
> try! `表达式`
强制 try 表达式的返回值是该*表达式*的值。如果该*表达式*抛出了错误,将会引发运行时错误。
在中缀运算符左侧的表达式被标记上 `try``try?` 或者 `try!` 时,这个运算符对整个中缀表达式都产生作用。也就是说,你可以使用括号来明确运算符的作用范围。
```swift
sum = try someThrowingFunction() + anotherThrowingFunction() // try 对两个函数调用都产生作用
sum = try (someThrowingFunction() + anotherThrowingFunction()) // try 对两个函数调用都产生作用
sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try 只对第一个函数调用产生作用
// try 对两个函数调用都产生作用
sum = try someThrowingFunction() + anotherThrowingFunction()
// try 对两个函数调用都产生作用
sum = try (someThrowingFunction() + anotherThrowingFunction())
// 错误try 只对第一个函数调用产生作用
sum = (try someThrowingFunction()) + anotherThrowingFunction()
```
`try` 表达式不能出现在二元运算符的的右侧,除非二元运算符是赋值运算符或者 `try` 表达式是被圆括号括起来的。
`try` 表达式不能出现在中缀运算符的的右侧,除非中缀运算符是赋值运算符或者 `try` 表达式是被圆括号括起来的。
关于 `try``try?``try!` 的更多信息,以及该如何使用的例子,请参阅 [错误处理](../02_language_guide/17_Error_Handling.md)
如果表达式中同时包含 `try``await` 运算符,`try` 运算符必须在前面
更多关于 `try``try?``try!` 的信息,以及该如何使用的例子,请参阅 [错误处理](../02_language_guide/17_Error_Handling.md)。
> Try 表达式语法
>
#### try-operator {#try-operator}
> *try 运算符* → **try** | **try?** | **try!**
>
> *try 运算符* → **try** | **try?** | **try!**
## 二元表达式 {#binary-expressions}
*二元表达式*由中缀运算符和左右参数表达式组成。形式如下:
### Await 运算符{#await-operators}
> `左侧参数` `二元运算符` `右侧参数`
>
*await 表达式*由 `await` 运算符加上紧随其后的异步操作结果的表达式。形式如下:
> await `表达式`
`await` 表达式返回值就是该*表达式*的值。
`await` 标记的表达式被称为*潜在的暂停点*。
异步函数的执行可以在每个标记 `await` 的表达式的位置暂停。除此之外,并发代码的执行永远不会在其他位置暂停。这意味着在潜在暂停点之间的代码可以暂时打破不变量的状态进行安全更新,只要更新在下一个潜在暂停点之前完成。
`await` 表达式只能在异步的上下文中出现,比如传入 `async(priority:operation:)` 函数的尾随闭包中。它不能在 `defer` 语句的闭包中,或者在同步函数的自动闭包中出现。
在中缀运算符左侧的表达式被标记上 `await` 运算符时,这个运算符对整个中缀表达式都产生作用。也就是说,你可以使用括号来明确运算符的作用范围。
```swift
// await 对两个函数调用都产生作用
sum = await someAsyncFunction() + anotherAsyncFunction()
// await 对两个函数调用都产生作用
sum = await (someAsyncFunction() + anotherAsyncFunction())
// 错误await 只对第一个函数调用产生作用
sum = (await someAsyncFunction()) + anotherAsyncFunction()
```
`await` 表达式不能出现在中缀运算符的的右侧,除非中缀运算符是赋值运算符或者 `await` 表达式是被圆括号括起来的。
如果表达式中同时包含 `try``await` 运算符,`try` 运算符必须在前面。
> Await 表达式语法
#### await-operator {#await-operator}
> *await 运算符* → **await**
## 中缀表达式 {#infix-expressions}
*中缀表达式*由中缀运算符和左右参数表达式组成。形式如下:
> `左侧参数` `中缀运算符` `右侧参数`
关于这些运算符的更多信息,请参阅 [基本运算符](../02_language_guide/02_Basic_Operators.md) 和 [高级运算符](../02_language_guide/27_Advanced_Operators.md)。
@ -87,31 +134,27 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try
> 注意
>
> 在解析时,一个二元表达式将作为一个扁平列表表示,然后根据运算符的优先级,再进一步进行组合。例如,`2 + 3 * 5` 首先被看作具有五个元素的列表,即 `2`、`+`、`3`、`*`、`5`,随后根据运算符优先级组合为 `(2 + (3 * 5))`。
>
> 在解析时,一个中缀表达式将作为一个扁平列表表示,然后根据运算符的优先级,再进一步进行组合。例如,`2 + 3 * 5` 首先被看作具有五个元素的列表,即 `2`、`+`、`3`、`*`、`5`,随后根据运算符优先级组合为 `(2 + (3 * 5))`。
#### binary-expression {#binary-expression}
> 二元表达式语法
#### infix-expression {#infix-expression}
> 中置表达式语法
>
> *二元表达式* → [二元运算符](./02_Lexical_Structure.md#binary-operator) [前缀表达式](#prefix-expression)
> *中置表达式* → [中置运算符](./02_Lexical_Structure.md#infix-operator) [前缀表达式](#prefix-expression)
>
> *二元表达式* → [赋值运算符](#assignment-operator) [try 运算符](#try-operator)<sub>可选</sub> [前缀表达式](#prefix-expression)
> *中置表达式* → [赋值运算符](#assignment-operator) [try 运算符](#try-operator)<sub>可选</sub> [前缀表达式](#prefix-expression)
>
> *二元表达式* → [条件运算符](#conditional-operator) [try 运算符](#try-operator)<sub>可选</sub> [前缀表达式](#prefix-expression)
>
> *二元表达式* → [类型转换运算符](#type-casting-operator)
> *中置表达式* → [条件运算符](#conditional-operator) [try 运算符](#try-operator)<sub>可选</sub> [前缀表达式](#prefix-expression)
>
> *中置表达式* → [类型转换运算符](#type-casting-operator)
#### binary-expressions {#binary-expressions}
> *二元表达式列表* → [二元表达式](#binary-expression) [二元表达式列表](#binary-expressions)<sub>可选</sub>
>
#### infix-expressions {#infix-expressions}
> *中置表达式列表* → [中置表达式](#infix-expression) [中置表达式列表](#infix-expressions)<sub>可选</sub>
### 赋值表达式 {#assignment-operator}
赋值表达式会为某个给定的表达式赋值,形式如下;
> `表达式` = `值`
>
右边的值会被赋值给左边的表达式。如果左边表达式是一个元组,那么右边必须是一个具有同样元素个数的元组。(嵌套元组也是允许的。)右边的值中的每一部分都会被赋值给左边的表达式中的相应部分。例如:
@ -123,28 +166,23 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try
赋值运算符不返回任何值。
> 赋值运算符语法
>
#### assignment-operator {#assignment-operator}
> *赋值运算符* → **=**
>
### 三元条件运算符 {#ternary-conditional-operator}
*三元条件运算符*会根据条件来对两个给定表达式中的一个进行求值,形式如下:
> `条件` ? `表达式(条件为真则使用)` : `表达式(条件为假则使用)`
>
如果条件为真,那么对第一个表达式进行求值并返回结果。否则,对第二个表达式进行求值并返回结果。未使用的表达式不会进行求值。
关于使用三元条件运算符的例子,请参阅 [三元条件运算符](../02_language_guide/02_Basic_Operators.md#ternary-conditional-operator)。
> 三元条件运算符语法
>
#### conditional-operator {#conditional-operator}
> *三元条件运算符* → **?** [表达式](#expression) **:**
>
### 类型转换运算符 {#type-casting-operators}
有 4 种类型转换运算符:`is``as``as? ``as!`。它们有如下的形式:
@ -156,7 +194,6 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try
> `表达式` as? `类型`
>
> `表达式` as! `类型`
>
`is` 运算符在运行时检查表达式能否向下转化为指定的类型,如果可以则返回 `ture`,否则返回 `false`
@ -196,13 +233,11 @@ f(x as Any)
> *类型转换运算符* → **as** **?** [类型](./03_Types.md#type)
>
> *类型转换运算符* → **as** **!** [类型](./03_Types.md#type)
>
## 基本表达式 {#primary-expressions}
*基本表达式*是最基本的表达式。它们可以单独使用,也可以跟前缀表达式、二元表达式、后缀表达式组合使用。
*基本表达式*是最基本的表达式。它们可以单独使用,也可以跟前缀表达式、中置表达式、后缀表达式组合使用。
> 基本表达式语法
>
#### primary-expression {#primary-expression}
> *基本表达式* → [标识符](./02_Lexical_Structure.md#identifier) [泛型实参子句](./09_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub>
@ -224,7 +259,6 @@ f(x as Any)
> *基本表达式* → [选择器表达式](#selector-expression)
>
> *基本表达式* → [key-path字符串表达式](#key-patch-string-expression)
>
### 字面量表达式 {#literal-expression}
*字面量表达式*可由普通字面量(例如字符串或者数字),字典或者数组字面量,或者下面列表中的特殊字面量组成:
@ -261,7 +295,6 @@ myFunction() // 打印“myFunction()”
数组字面量是值的有序集合,形式如下:
> [`值 1`, `值 2`, `...`]
>
数组中的最后一个表达式可以紧跟一个逗号。数组字面量的类型是 `[T]`,这个 `T` 就是数组中元素的类型。如果数组中包含多种类型,`T` 则是跟这些类型最近的的公共父类型。空数组字面量由一组方括号定义,可用来创建特定类型的空数组。
@ -272,7 +305,6 @@ var emptyArray: [Double] = []
字典字面量是一个包含无序键值对的集合,形式如下:
> [`键 1` : `值 1`, `键 2` : `值 2`, `...`]
>
字典中的最后一个表达式可以紧跟一个逗号。字典字面量的类型是 `[Key : Value]``Key` 表示键的类型,`Value` 表示值的类型。如果字典中包含多种类型,那么 `Key` 表示的类型则为所有键最接近的公共父类型,`Value` 与之相似。一个空的字典字面量由方括号中加一个冒号组成(`[:]`),从而与空数组字面量区分开,可以使用空字典字面量来创建特定类型的字典。
@ -295,7 +327,6 @@ Xcode 使用 playground 字面量对程序编辑器中的颜色、文件或者
> *字面量表达式* → [数组字面量](#array-literal) | [字典字面量](#dictionary-literal) | [练习场字面量](#playground-literal)
>
> *字面量表达式* → **#file** **#filePath** | **#line** | **#column** | **#function**
>
>
@ -351,7 +382,6 @@ Xcode 使用 playground 字面量对程序编辑器中的颜色、文件或者
> self(`构造器参数`)
>
> self.init(`构造器参数`)
>
如果在构造器、下标、实例方法中,`self` 引用的是当前类型的实例。在一个类型方法中,`self` 引用的是当前的类型。
@ -378,7 +408,6 @@ struct Point {
```
> Self 表达式语法
>
#### self-expression {#self-expression}
> *self 表达式* → **self** | [self 方法表达式](#self-method-expression) [self 下标表达式](#self-subscript-expression) | [self 构造器表达式](#self-initializer-expression)
@ -387,15 +416,12 @@ struct Point {
#### self-method-expression {#self-method-expression}
> *self 方法表达式* → **self** **.** [标识符](./02_Lexical_Structure.md#identifier)
>
#### self-subscript-expression {#self-subscript-expression}
> *self 下标表达式* → **self** **[** [函数调用参数表](#function-call-argument-list­) **]**
>
#### self-initializer-expression {#self-initializer-expression}
> *self 构造器表达式* → **self** **.** **init**
>
### 父类表达式 {#superclass-expression}
*父类*表达式可以使我们在某个类中访问它的父类,它有如下形式:
@ -405,30 +431,24 @@ struct Point {
> super[`下标索引`]
>
> super.init(`构造器参数`)
>
第一种形式用来访问父类的某个成员,第二种形式用来访问父类的下标,第三种形式用来访问父类的构造器。
子类可以通过父类表达式在它们的成员、下标和构造器中使用父类中的实现。
> 父类表达式语法
>
#### superclass-expression {#superclass-expression}
> *父类表达式* → [父类方法表达式](#superclass-method-expression) | [父类下标表达式](#superclass-subscript-expression) | [父类构造器表达式](#superclass-initializer-expression)
>
#### superclass-method-expression {#superclass-method-expression}
> *父类方法表达式* → **super** **.** [标识符](./02_Lexical_Structure.md#identifier)
>
#### superclass-subscript-expression {#superclass-subscript-expression}
> *父类下标表达式* → **super** [[函数调用参数表](#function-call-argument-list­) **]**
>
#### superclass-initializer-expression {#superclass-initializer-expression}
> *父类构造器表达式* → **super** **.** **init**
>
### 闭包表达式 {#closure-expression}
*闭包表达式*会创建一个闭包,在其他语言中也叫 *lambda* 或*匿名*函数。跟函数一样,闭包包含了待执行的代码,不同的是闭包还会捕获所在环境中的常量和变量。它的形式如下:
@ -441,6 +461,16 @@ struct Point {
闭包的参数声明形式跟函数一样,请参阅 [函数声明](./06_Declarations.md#function-declaration)。
在闭包表达式中写入 `throws``async` 将显式地将闭包标记为丢掷或异步的。
```swift
{ (parameters) async throws -> return type in
statements
}
```
如果闭包的主体中含有 try 表达式,则认为该闭包会引发异常。同理,若闭包主体含有 await 表达式,则认为该闭包是异步的。
闭包还有几种特殊的形式,能让闭包使用起来更加简洁:
- 闭包可以省略它的参数和返回值的类型。如果省略了参数名和所有的类型,也要省略 `in` 关键字。如果被省略的类型无法被编译器推断,那么就会导致编译错误。
@ -530,86 +560,114 @@ myFunction { [weak parent = self.parent] in print(parent!.title) }
关于闭包表达式的更多信息和例子,请参阅 [闭包表达式](../02_language_guide/07_Closures.md#closure-expressions)。关于捕获列表的更多信息和例子,请参阅 [解决闭包引起的循环强引用](../02_language_guide/24_Automatic_Reference_Counting.md#resolving-strong-reference-cycles-for-closures)。
> 闭包表达式语法
>
>
>
#### closure-expression {#closure-expression}
>
> *闭包表达式* → **{** [闭包签名](#closure-signature)<sub>可选</sub> [语句](#statements) **}**
>
>
>
#### closure-signature {#closure-signature}
>
> #### closure-expression {#closure-expression}
>
> *闭包表达式* → **{** [特性](#attribute)<sub>可选</sub> [闭包签名](#closure-signature)<sub>可选</sub> [语句](#statements) **}**
>
> #### closure-signature {#closure-signature}
>
> *闭包签名* → [捕获列表](#capture-list)<sub>可选</sub> [闭包形参子句](#closure-parameter-clause) **async**<sub>可选</sub> **throws**<sub>可选</sub> [函数结果](./06_Declarations.md#function-result)<sub>可选</sub> **in**
>
> 闭包签名* → [参数子句](#parameter-clause) [函数结果](./06_Declarations.md#function-result)<sub>可选</sub> **in**
>
> *闭包签名* → [标识符列表](#identifier-list) [函数结果](./06_Declarations.md#function-result)<sub>可选</sub> **in**
>
> *闭包签名* → [捕获列表](#capture-list) [参数子句](./06_Declarations.md#parameter-clause) [函数结果](./06_Declarations.md#function-result)<sub>可选</sub> **in**
>
> *闭包签名* → [捕获列表](#capture-list) [标识符列表](./02_Lexical_Structure.md#identifier-list) [函数结果](./06_Declarations.md#function-result)<sub>可选</sub> **in**
>
> *闭包签名* → [捕获列表](#capture-list) **in**
>
>
>
> #### closure-parameter-clause {#closure-parameter-clause}
>
#### capture-list {#capture-list}
>
> *闭包形参子句* → ** ******[闭包形参列表](#closure-parameter-list)**)**|[标识符列表](./02_Lexical_Structure.md#identifier-list)
>
> 捕获列表* → [ [捕获列表项列表](#capture-list-items) **]**
> #### closure-parameter-list {#closure-parameter-list}
>
> *闭包形参列表* → [闭包形参](#closure-parameter) [闭包形参](#closure-parameter) **,** [闭包形参列表](#closure-parameter-list)
>
> #### closure-parameter{#closure-parameter}
>
#### capture-list-items {#capture-list-items}
> *闭包形参* → [闭包形参名字](#closure-parameter-name) [类型注解](./03_Types.md#type-annotation)<sub>可选</sub>
>
> *捕获列表项列表* → [捕获列表项](#capture-list-item) | [捕获列表项](#capture-list-item) **,** [捕获列表项列表](#capture-list-items)
> *闭包形参* → [闭包形参名字](#closure-parameter-name) [类型注解](./03_Types.md#type-annotation) **...**
>
> #### closure-parameter-name{#closure-parameter-name}
>
#### capture-list-item {#capture-list-item}
> *闭包形参名字* → [标识符](./02_Lexical_Structure.md#identifier)
>
> #### closure-list{#closure-list}
>
> *捕获列表* → [捕获说明符](#capture-specifier)<sub>可选</sub> [表达式](#expression)
> *捕获列表* → **[** [捕获列表项列表](#capture-list-items) **]**
>
>#### capture-list-items {#capture-list-items}
>
#### capture-specifier {#capture-specifier}
>*捕获列表项列表* → [捕获列表项](#capture-list-item) | [捕获列表项](#capture-list-item) **,** [捕获列表项列表](#capture-list-items)
>
> *捕获说明符* → **weak** | **unowned** | **unowned(safe)** | **unowned(unsafe)**
>#### capture-list-item {#capture-list-item}
>
>*捕获列表项* → [捕获说明符](#capture-specifier)<sub>可选</sub> [标识符](./02_Lexical_Structure.md#identifier)
>
> *捕获列表项* → [捕获说明符](#capture-specifier)<sub>可选</sub> [标识符](./02_Lexical_Structure.md#identifier) **=** [表达式](#expression)
>
> *捕获列表项* → [捕获说明符](#capture-specifier)<sub>可选</sub> [标识符](./02_Lexical_Structure.md#identifier) [Self 表达式](##self-expression)
>
>#### capture-specifier {#capture-specifier}
>
>*捕获说明符* → **weak** | **unowned** | **unowned(safe)** | **unowned(unsafe)**
### 隐式成员表达式 {#implicit-member-expression}
若类型可被推断出来,可以使用*隐式成员表达式*来访问某个类型的成员(例如某个枚举成员或某个类型方法),形式如下:
> .`成员名称`
>
例如:
```swift
var x = MyEnumeration.SomeValue
x = .AnotherValue
var x = MyEnumeration.someValue
x = .anotherValue
```
如果推断的是可选类型,可以在隐式成员表达式里使用不可选类型的成员。
```swift
var someOptional: MyEnumeration? = .someValue
```
隐式成员表达式可以跟在后缀运算符或者其他在 [后缀表达式](#postfix-expressions) 里介绍的语法后面。这被称为 *链式隐式成员表达式*。尽管链式后缀表达式大多都是相同类型,但其实只需要整个链式成员表达式可以转换为上下文的类型就行了。更具体的,如果隐式类型是可选的,则可以使用非可选类型的值,如果隐式类型是类类型,则可以使用其子类的值。例如:
```swift
class SomeClass {
static var shared = SomeClass()
static var sharedSubclass = SomeSubclass()
var a = AnotherClass()
}
class SomeSubclass: SomeClass { }
class AnotherClass {
static var s = SomeClass()
func f() -> SomeClass { return AnotherClass.s }
}
let x: SomeClass = .shared.a.f()
let y: SomeClass? = .shared
let z: SomeClass = .sharedSubclass
```
上面的代码中,`x` 的类型和上下文的隐式类型完全匹配,`y` 的类型是从 `SomeClass` 转换成 `SomeClass?``z` 的类型是从 `SomeSubclass` 转换成 `SomeClass`
> 隐式成员表达式语法
>
#### implicit-member-expression {#implicit-member-expression}
> *隐式成员表达式* → **.** [标识符](./02_Lexical_Structure.md#identifier)
>
### 圆括号表达式 {#parenthesized-expression}
*圆括号表达式*是由圆括号包围的表达式。你可以用圆括号说明成组的表达式的先后操作。成组的圆括号不会改变表达式的类型 - 例如 `(1)` 的类型就是简单的 `Int`
> 圆括号表达式语法
>
#### parenthesized-expression {#parenthesized-expression}
> *圆括号表达式* → **( [表达式](#expression) )**
>
### 元组表达式 {#Tuple-Expression}
*元组表达式*由圆括号和其中多个逗号分隔的子表达式组成。每个子表达式前面可以有一个标识符,用冒号隔开。元组表达式形式如下:
> (`标识符 1` : `表达式 1`, `标识符 2` : `表达式 2`, `...`)
>
元组表达式里的每一个标识符在表达式作用域里必须是唯一的。在嵌套的元组表达式中,同嵌套层级里的标识符也必须是唯一的。例如,`(a: 10, a: 20)` 是不合法的,因为标签 `a` 在同一层级出现了两次。然而,`(a: 10, b: (a: 1, x: 2))` 是合法的,尽管 `a` 出现了两次,但有一次在外层元组里,一次在内层元组里。
@ -619,23 +677,18 @@ x = .AnotherValue
>
>
> 在 Swift 中,空的元组表达式和空的元组类型都写作 `()`。由于 `Void` 是 `()` 的类型别名,因此可以使用它来表示空的元组类型。虽然如此,`Void` 就像所有的类型别名一样,永远是一个类型——不能表示空的元组表达式。
>
> 元组表达式语法
>
#### tuple-expression {#tuple-expression}
> *元组表达式* → **( )** | **(**[元组元素](#tuple-element) [元组元素列表](#tuple-element-list) **)**
>
#### tuple-element-list {#tuple-element-list}
> *元组元素列表* → [元组元素](#tuple-element) | [元组元素](#tuple-element) **,** [元组元素列表](#tuple-element-list)
>
#### tuple-element {#tuple-element}
> *元组元素* → [表达式](#expression) | [标识符](#identifier) **:** [表达式](#expression)
>
### 通配符表达式 {#wildcard-expression}
*通配符表达式*可以在赋值过程中显式忽略某个值。例如下面的代码中,`10` 被赋值给 `x`,而 `20` 则被忽略:
@ -646,18 +699,15 @@ x = .AnotherValue
```
> 通配符表达式语法
>
#### wildcard-expression {#wildcard-expression}
> *通配符表达式* → **_**
>
### Key-path 表达式 {#key-path-expression}
Key-path 表达式引用一个类型的属性或下标。在动态语言中使场景可以使用 Key-path 表达式,例如观察键值对。格式为:
> **\类型名.路径**
>
*类型名*是一个具体类型的名称,包含任何泛型参数,例如 `String``[Int]``Set<Int>`
@ -839,7 +889,6 @@ let someTask = toDoList[keyPath: taskKeyPath]
#### *多个 key-path 后缀* → [key-path 后缀](#key-path-postfix) [多个 key-path 后缀](#key-path-postfixes)<sub>可选<sub> key-path-postfixes {#key-path-postfixes}
>
> *key-path 后缀* → **?** | **!** | **self** | **\[** [函数调用参数表](#function-call-argument-list) **\]**
>
@ -884,12 +933,10 @@ let anotherSelector = #selector(SomeClass.doSomething(-:) as (SomeClass) -> (Str
> 注意
>
> 虽然方法名或者属性名是个表达式,但是它不会被求值。
>
更多关于如何在 Swift 代码中使用选择器来与 Objective-C API 进行交互的信息,请参阅 [在 Swift 中使用 Objective-C 运行时特性](https://developer.apple.com/documentation/swift/using_objective_c_runtime_features_in_swift)。
> 选择器表达式语法
>
#### selector-expression {#selector-expression}
> *选择器表达式* → __#selector-- **(** [表达式](#expression) **)**
@ -897,13 +944,11 @@ let anotherSelector = #selector(SomeClass.doSomething(-:) as (SomeClass) -> (Str
> *选择器表达式* → __#selector-- **(** [getter:表达式](#expression) **)**
>
> *选择器表达式* → __#selector-- **(** [setter:表达式](#expression) **)**
>
## Key-path 字符串表达式 {#key-path-string-expressions}
key-path 字符串表达式可以访问一个引用 Objective-C 属性的字符串,通常在 key-value 编程和 key-value 观察 APIs 中使用。其格式如下:
> `#keyPath` ( `属性名` )
>
属性名必须是一个可以在 Objective-C 运行时使用的属性的引用。在编译期key-path 字符串表达式会被一个字符串字面量替换。例如:
@ -945,7 +990,6 @@ print(keyPath == c.getSomeKeyPath())
>
>
> 尽管*属性名*是一个表达式,但它永远不会被求值
>
> key-path 字符串表达式语法
>
@ -953,7 +997,6 @@ print(keyPath == c.getSomeKeyPath())
#### key-path-string-expression {#key-path-string-expression}
>
> *key-path 字符串表达式* → **#keyPath (** [表达式](#expression) **)**
>
## 后缀表达式 {#postfix-expressions}
*后缀表达式*就是在某个表达式的后面运用后缀运算符或其他后缀语法。从语法构成上来看,基本表达式也是后缀表达式。
@ -963,7 +1006,6 @@ print(keyPath == c.getSomeKeyPath())
关于 Swift 标准库提供的运算符的更多信息,请参阅 [运算符定义](https://developer.apple.com/documentation/swift/operator_declarations)。
> 后缀表达式语法
>
#### postfix-expression {#postfix-expression}
> *后缀表达式* → [基本表达式](#primary-expression)
@ -985,21 +1027,19 @@ print(keyPath == c.getSomeKeyPath())
> *后缀表达式* → [强制取值表达式](#forced-value-expression)
>
> *后缀表达式* → [可选链表达式](#optional-chaining-expression)
>
### 函数调用表达式 {#function-call-expression}
*函数调用表达式*由函数名和参数列表组成,形式如下:
*函数调用表达式*由函数名和在括号里以逗号分隔的参数列表组成。函数调用表达式形式如下:
> `函数名`(`参数 1`, `参数 2`)
>
函数名可以是值为函数类型的任意表达式。
如果函数声明中指定了参的名字,那么在调用的时候也必须得写出来。这种函数调用表达式具有以下形式:
如果函数声明中指定了参的名字,那么在调用的时候也必须得写出来,并通过冒号(`:`)分隔。这种函数调用表达式具有以下形式:
> `函数名`(`参数名 1`: `参数 1`, `参数名 2`: `参数 2`)
如果函数的最后一个参数是函数类型,可以在函数调用表达式的尾部(右圆括号之后)加上一个闭包,该闭包会作为函数的最后一个参数。如下两种写法是等价的
函数调用表达式可以在函数调用表达式的尾部(右圆括号之后)加上多个尾随闭包,该闭包会作为函数的实参,在括号中最后一个实参后面添加。第一个闭包表达式时没有实参签名的,其他任意闭包表达式签名都有实参标签。如下两种写法是等价的,区别在是否使用尾随闭包语法
```swift
// someFunction 接受整型和闭包的实参
@ -1011,7 +1051,7 @@ anotherFunction(x: x, f: { $0 == 13 }, g: { print(99) })
anotherFunction(x: x) { $0 == 13 } g: { print(99) }
```
如果闭包是该函数的唯一参,那么圆括号可以省略。
如果闭包是该函数的唯一参,那么圆括号可以省略。
```swift
// someFunction 只接受一个闭包参数
@ -1019,49 +1059,118 @@ myData.someMethod() {$0 == 13}
myData.someMethod {$0 == 13}
```
为了支持实参中的尾随闭包,编译器从左到右检查形参列表,如下所示:
| 尾随闭包 | 形参 | 行为 |
| -------- | -------------- | ------------------------------------------------------------ |
| 有标签 | 有标签 | 如果标签相同,闭包和形参匹配,否则跳过该形参 |
| 有标签 | 无标签 | 跳过该形参 |
| 无标签 | 有标签或无标签 | 如果形参在结构上类似于下面定义的函数类型,和闭包匹配,否则跳过该形参 |
尾随闭包作为其匹配形参的实参传递。
在扫描过程被跳过的形参不传递实参——例如,它们使用的是默认形参。当匹配后,扫描会继续下一个尾随闭包和形参。匹配过程结束后,所有的尾随闭包必须有对应的匹配。
如果形参不是输入输出参数,并且类似下面的情况,则算是*结构上类似*函数类型:
* 函数类型的形参,例如 `(Bool) -> Int`
* 函数类型表达式的自动闭包形参,例如 `@autoclosure () -> ((Bool) -> Int)`
* 元素是函数类型的可变参数,例如 `((Bool) -> Int)...`
* 单层或多层可选类型的形参,例如 `Optional<(Bool) -> Int>`
* 由上面这些类型组合而成的形参,例如 `(Optional<(Bool) -> Int>)...`
尾随闭包和结构上类似函数类型的形参匹配,但它并不是函数,所以闭包会按需包装。例如,如果形参类是是可选类型,闭包会自动包装成 `Optional`
为了简化 Swift 5.3 之前版本(从右到左匹配)的代码迁移 —— 编译器会同时检查从左到右和从右到左的顺序。如果不同的扫描方向产生了不同的结果编译器则使用旧的从右到左的顺序并生成警告。Swift 的未来版本将都使用从左到右的顺序。
```swift
typealias Callback = (Int) -> Int
func someFunction(firstClosure: Callback? = nil,
secondClosure: Callback? = nil) {
let first = firstClosure?(10)
let second = secondClosure?(20)
print(first ?? "-", second ?? "-")
}
someFunction() // 打印 "- -"
someFunction { return $0 + 100 } // 歧义
someFunction { return $0 } secondClosure: { return $0 } // 打印 "10 20"
```
在上面的例子中Swift 5.3 中被标记为“歧义”的函数调用会打印”- 120“并产生一个编辑器警告。未来版本的 Swift 会打印”110 -“。
如 [特殊名称方法](./06_Declarations.md#methods-with-special-names) 所述,通过声明几种方法中的一种,类、结构体或枚举类型可以为函数调用语法启用语法糖。
### 指针类型的隐式转换{#implicit-conversion-to-a-pointer}
在函数调用表达式里,如果实参和形参的类型不一致,编译器会尝试通过下面的规则进行隐式转换来匹配类型:
* 输入输出的 `SomeType` 可以转换为 `UnsafePointer<SomeType>` 或者 `UnsafeMutablePointer<SomeType>`
* 输入输出的 `Array<SomeType>` 可以转换为 `UnsafePointer<SomeType>` 或者 `UnsafeMutablePointer<SomeType>`
* `Array<SomeType>` 可以转换为 `UnsafePointer<SomeType>`
* `String` 可以转换为 `UnsafePointer<CChar>`
下面两个函数调用是等价的:
```swift
func unsafeFunction(pointer: UnsafePointer<Int>) {
// ...
}
var myNumber = 1234
unsafeFunction(pointer: &myNumber)
withUnsafePointer(to: myNumber) { unsafeFunction(pointer: $0) }
```
隐式转换创建的指针仅在函数调用期间有效。为了避免发生未定义行为,确保代码在函数调用结束后没有继续持有这些指针。
> 注意
>
> 当将数组隐式转换为不安全指针时Swift 会按需转换或复制数组来确保数组的存储是连续的。例如,可以用此语法将没有约定存储 API 的 `NSArray` 子类桥接到 `Array` 的数组。如果能确保数组的存储是连续的,就不需要隐式转换来做这个工作,这种情况下可以用 `ContiguousArray` 代替 `Array`。
使用 `&` 代替类似 `withUnsafePointer(to:)` 的显式函数可以在调用底层 C 函数的可读性更高,特别是当函数传入多个指针实参时。如果是其他 Swift 代码调用函数,避免使用 `&` 代替显式的不安全 API。
> 函数调用表达式语法
>
>
#### function-call-expression {#function-call-expression}
>
>
>
> #### function-call-expression {#function-call-expression}
>
> *函数调用表达式* → [后缀表达式](#postfix-expression) [函数调用参数子句](#function-call-argument-clause)
>
>
> *函数调用表达式* → [后缀表达式](#postfix-expression) [函数调用参数子句](#function-call-argument-clause)<sub>可选</sub> [尾随闭包](#trailing-closure)
>
>#### function-call-argument-clause {#function-call-argument-clause}
>
> #### function-call-argument-clause {#function-call-argument-clause}
>
> *函数调用参数子句* → **(** **)** | **(** [函数调用参数表](#function-call-argument-list) **)**
>
>
> #### function-call-argument-list {#function-call-argument-list}
>
>
> *函数调用参数表* → [函数调用参数](#function-call-argument) | [函数调用参数](#function-call-argument) **,** [*函数调用参数表*](#function-call-argument-list)
>
>
>
>
> #### function-call-argument {#function-call-argument}
>
>
> *函数调用参数* → [表达式](#expression) | [标识符](./02_Lexical_Structure.md#identifier) **:** [表达式](#expression)
>
>
> *函数调用参数* → [运算符](./02_Lexical_Structure.md#operator) | [标识符](./02_Lexical_Structure.md#identifier) **:** [运算符](./02_Lexical_Structure.md#operator)
>
>
> #### trailing-closure {#trailing-closure}
>
> *尾随闭包* → [闭包表达式](#closure-expression)
>
> *尾随闭包* → [闭包表达式](#closure-expression) [标签尾随闭包集](#labeled-trailing-closures)<sub>可选</sub>
>
> #### labeled-trailing-closures {#labeled-trailing-closures}
>
>
> *标签尾随闭包集* → [标签尾随闭包](#labeled-trailing-closure) [标签尾随闭包集](#labeled-trailing-closures)<sub>可选</sub>
>
>
> #### labeled-trailing-closure {#labeled-trailing-closure}
>
> *标签尾随闭包* → [标识符](./02_Lexical_Structure.md#identifier) **:** [闭包表达式](#closure-expression)
### 构造器表达式 {#initializer-expression}
*构造器表达式*用于访问某个类型的构造器,形式如下:
> `表达式`.init(`构造器参数`)
>
你可以在函数调用表达式中使用构造器表达式来初始化某个类型的新实例。也可以使用构造器表达式来代理给父类构造器。
@ -1095,19 +1204,16 @@ let s3 = someValue.dynamicType.init(data: 7) // 有效
```
> 构造器表达式语法
>
#### initializer-expression {#initializer-expression}
> *构造器表达式* → [后缀表达式](#postfix-expression) **.** **init**
>
> *构造器表达式* → [后缀表达式](#postfix-expression) **.** **init** **(** [参数名称](#argument-names) **)**
>
### 显式成员表达式 {#explicit-member-expression}
*显式成员表达式*允许我们访问命名类型、元组或者模块的成员,其形式如下:
> `表达式`.`成员名`
>
命名类型的某个成员在原始实现或者扩展中定义,例如:
@ -1159,22 +1265,39 @@ let x = [10, 3, 20, 15, 4]
.map { $0 * 100 }
```
你可以将这种多行链式语法与编译器控制语句结合,以控制调用每个方法的时间。例如,以下代码在 iOS 上应用了不同的过滤规则:
```swift
let numbers = [10, 20, 33, 43, 50]
#if os(iOS)
.filter { $0 < 40}
#else
.filter {$0 > 25}
#endif
```
`#if``#endif` 和其它编译指令之间的条件编译块可以包含一个隐式成员表达式,后跟零个或多个后缀,以形成一个后缀表达式。这些条件编译块还可以包含另一个条件编译块,或者这些表达式和块的组合体。
除了顶级代码top-level code以外你还可以在任何能编写显式成员表达式的地方使用上述语法。
在条件编译块中,编译指令 `#if` 的分支必须包含至少一个表达式,其它分支可以为空。
> 显式成员表达式语法
>
#### explicit-member-expression {#explicit-member-expression}
> *显式成员表达式* → [后缀表达式](#postfix-expression) **.** [十进制数字](./02_Lexical_Structure.md#decimal-digit)
>
> *显式成员表达式* → [后缀表达式](#postfix-expression) **.** [标识符](./02_Lexical_Structure.md#identifier) [泛型实参子句](./09_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub><br/>
>
> *显式成员表达式* → [后缀表达式](#postfix-expression) **.** [标识符](./02_Lexical_Structure.md#identifier) **(** [参数名称](#argument-names) **)**
> *显式成员表达式* → [后缀表达式](#postfix-expression) **.** [标识符](./02_Lexical_Structure.md#identifier) **(** [参数名称](#argument-names) **)**
> *显示成员表达式* → [后缀表达式](#postfix-expression) [条件编译块](#conditional-compilation-block)
#### argument-names {#argument-names}
> *参数名称* → [参数名](#argument-name) [参数名称](#argument-names)<sub>可选</sub><br/>
#### argument-name {#argument-name}
> *参数名* → [标识符](./02_Lexical_Structure.md#identifier) **:**
>
### 后缀 self 表达式 {#postfix-self-expression}
后缀 `self` 表达式由某个表达式或类型名紧跟 `.self` 组成,其形式如下:
@ -1182,42 +1305,35 @@ let x = [10, 3, 20, 15, 4]
> `表达式`.self
>
> `类型`.self
>
第一种形式返回表达式的值。例如:`x.self` 返回 `x`
第二种形式返回相应的类型。我们可以用它来获取某个实例的类型作为一个值来使用。例如,`SomeClass.self` 会返回 `SomeClass` 类型本身,你可以将其传递给相应函数或者方法作为参数。
> 后缀 self 表达式语法
>
#### postfix-self-expression {#postfix-self-expression}
> *后缀 self 表达式* → [后缀表达式](#postfix-expression) **.** **self**
>
### 下标表达式 {#subscript-expression}
可通过*下标表达式*访问相应的下标,形式如下:
> `表达式`[`索引表达式`]
>
要获取下标表达式的值,可将索引表达式作为下标表达式的参数来调用下标 getter。下标 setter 的调用方式与之一样。
关于下标的声明,请参阅 [协议下标声明](./06_Declarations.md#protocol-subscript-declaration)。
> 下标表达式语法
>
#### subscript-expression {#subscript-expression}
> *下标表达式* → [后缀表达式](#postfix-expression) **[** [表达式列表](#expression-list) **]**
>
### 强制取值表达式 {#forced-Value-expression}
当你确定可选值不是 `nil` 时,可以使用*强制取值表达式*来强制解包,形式如下:
> `表达式`!
>
如果该表达式的值不是 `nil`,则返回解包后的值。否则,抛出运行时错误。
@ -1234,17 +1350,14 @@ someDictionary["a"]![0] = 100
```
> 强制取值语法
>
#### forced-value-expression {#forced-value-expression}
> *强制取值表达式* → [后缀表达式](#postfix-expression) **!**
>
### 可选链表达式 {#optional-chaining-expression}
*可选链表达式*提供了一种使用可选值的便捷方法,形式如下:
> `表达式`?
>
后缀 `?` 运算符会根据表达式生成可选链表达式而不会改变表达式的值。
@ -1286,7 +1399,6 @@ someDictionary["a"]?[0] = someFunctionWithSideEffects()
```
> 可选链表达式语法
>
#### optional-chaining-expression {#optional-chaining-expression}
> *可选链表达式* → [后缀表达式](#postfix-expression) **?**

View File

@ -43,7 +43,6 @@
> *循环语句* → [while 语句](#while-statement)
>
> *循环语句* → [repeat-while 语句](#repeat-while-statement)
>
### For-In 语句 {#for-in-statements}
@ -65,7 +64,6 @@ 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)
>
### While 语句 {#while-statements}
只要循环条件为真,`while` 语句就会重复执行代码块。
@ -93,12 +91,10 @@ while condition {
#### while-statement {#while-statement}
> *while 语句* → **while** [条件子句](#condition-clause) [代码块](./06_Declarations.md#code-block)
>
#### condition-clause {#condition-clause}
> *条件子句* → [表达式](./04_Expressions.md#expression) | [表达式](./04_Expressions.md#expression) **,** [条件列表](#condition-list)
>
#### condition {#condition}
> *条件* → [表达式](./04_Expressions.md#expression) |[可用性条件](#availability-condition) | [case 条件](#case-condition) | [可选绑定条件](#optional-binding-condition)
@ -107,11 +103,9 @@ while condition {
#### case-condition {#case-condition}
> *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) | **var** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer)
>
> *可选绑定条件* → **let** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer)<sub>可选</sub> | **var** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer)<sub>可选</sub>
### Repeat-While 语句 {#repeat-while-statements}
`repeat-while` 语句至少执行一次代码块,之后只要循环条件为真,就会重复执行代码块。
@ -139,7 +133,6 @@ repeat {
#### repeat-while-statement {#repeat-while-statement}
> *repeat-while 语句* → **repeat** [代码块](./06_Declarations.md#code-block) **while** [表达式](./04_Expressions.md#expression)
>
## 分支语句 {#branch-statements}
分支语句会根据一个或者多个条件来执行指定部分的代码。分支语句中的条件将会决定程序如何分支以及执行哪部分代码。Swift 提供三种类型的分支语句:`if` 语句、 `guard` 语句和 `switch` 语句。
@ -156,7 +149,6 @@ repeat {
> *分支语句* → [guard 语句](#guard-statement)
>
> *分支语句* → [switch 语句](#switch-statement)
>
### If 语句 {#if-statements}
`if` 语句会根据一个或多个条件来决定执行哪一块代码。
@ -201,7 +193,6 @@ if condition 1 {
#### if-statement {#if-statement}
> *if 语句* → **if** [条件子句](#condition-clause) [代码块](./06_Declarations.md#code-block) [else 子句](#else-clause)<sub>可选</sub>
>
#### else-clause {#else-clause}
> *else 子句* → **else** [代码块](./06_Declarations.md#code-block) | **else** [if 语句](#if-statement)
@ -309,24 +300,19 @@ case .suppressed:
#### switch-statement {#switch-statement}
> *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* → [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-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)
>
#### default-label {#default-label}
> *default 标签* → [属性](#switch-case-attributes-label)<sub>可选</sub> **default** **:**
@ -335,7 +321,6 @@ case .suppressed:
#### where-clause {#where-clause}
> *where-clause* → **where** [where 表达式](#where-expression)
>
#### where-expression {#where-expression}
> *where-expression* → [表达式](./04_Expressions.md#expression)
@ -344,23 +329,18 @@ case .suppressed:
#### 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-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-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)。
@ -381,15 +361,12 @@ case .suppressed:
> *带标签的语句* → [语句标签](#statement-label) [switch 语句](#switch-statement)
>
> > *带标签的语句* → [语句标签](#statement-label) [do 语句](#sdo-statement)
>
#### statement-label {#statement-label}
> *语句标签* → [标签名称](#label-name) **:**
>
#### label-name {#label-name}
> *标签名称* → [标识符](./02_Lexical_Structure.md#identifier)
>
## 控制转移语句 {#control-transfer-statements}
控制转移语句能够无条件地把控制权从一片代码转移到另一片代码从而改变代码执行的顺序。Swift 提供五种类型的控制转移语句:`break` 语句、`continue` 语句、`fallthrough` 语句、`return` 语句和 `throw` 语句。
@ -408,7 +385,6 @@ case .suppressed:
> *控制转移语句* → [return 语句](#return-statement)
>
> *控制转移语句* → [throw 语句](#throw-statement)
>
### Break 语句 {#break-statement}
`break` 语句用于终止循环语句、`if` 语句或 `switch` 语句的执行。使用 `break` 语句时,可以只写 `break` 这个关键词,也可以在 `break` 后面跟上标签名,像下面这样:
@ -416,7 +392,6 @@ case .suppressed:
> break
>
> break `label name`
>
`break` 语句后面带标签名时,可用于终止由这个标签标记的循环语句、`if` 语句或 `switch` 语句的执行。
@ -432,7 +407,6 @@ case .suppressed:
#### break-statement {#break-statement}
> *break 语句* → **break** [标签名称](#label-name)<sub>可选</sub>
>
### Continue 语句 {#continue-statement}
`continue` 语句用于终止循环中当前迭代的执行,但不会终止该循环的执行。使用 `continue` 语句时,可以只写 `continue` 这个关键词,也可以在 `continue` 后面跟上标签名,像下面这样:
@ -440,7 +414,6 @@ case .suppressed:
> continue
>
> continue `label name`
>
`continue` 语句后面带标签名时,可用于终止由这个标签标记的循环中当前迭代的执行。
@ -458,7 +431,6 @@ case .suppressed:
#### continue-statement {#continue-statement}
> *continue 语句* → **continue** [标签名称](#label-name)<sub>可选</sub>
>
### Fallthrough 语句 {#fallthrough-statements}
`fallthrough` 语句用于在 `switch` 语句中转移控制权。`fallthrough` 语句会把控制权从 `switch` 语句中的一个 `case` 转移到下一个 `case`。这种控制权转移是无条件的,即使下一个 `case` 的模式与 `switch` 语句的控制表达式的值不匹配。
@ -473,7 +445,6 @@ case .suppressed:
#### fallthrough-statement {#fallthrough-statement}
> *fallthrough 语句* → **fallthrough**
>
### Return 语句 {#return-statements}
`return` 语句用于在函数或方法的实现中将控制权转移到调用函数或方法,接着程序将会从调用位置继续向下执行。
@ -483,7 +454,6 @@ case .suppressed:
> return
>
> return `expression`
>
`return` 语句后面带表达式时表达式的值将会返回给调用函数或方法。如果表达式的值的类型与函数或者方法声明的返回类型不匹配Swift 则会在返回表达式的值之前将表达式的值的类型转换为返回类型。
@ -491,7 +461,6 @@ case .suppressed:
>
>
> 正如 [可失败构造器](./06_Declarations.md#failable-initializers) 中所描述的,`return nil` 在可失败构造器中用于表明构造失败。
>
而只写 `return` 时,仅仅是从该函数或方法中返回,而不返回任何值(也就是说,函数或方法的返回类型为 `Void` 或者说 `()`)。
@ -511,7 +480,6 @@ case .suppressed:
`throw` 语句由 `throw` 关键字紧跟一个表达式组成,如下所示:
> throw `expression`
>
表达式的结果必须符合 `ErrorType` 协议。
@ -523,7 +491,6 @@ case .suppressed:
#### throw-statement {#throw-statement}
> *throw 语句* → **throw** [表达式](./04_Expressions.md#expression)
>
## Defer 语句 {#defer-statements}
`defer` 语句用于在退出当前作用域之前执行代码。
@ -560,7 +527,6 @@ f()
#### defer-statement {#defer-statement}
> *延迟语句* → **defer** [代码块](./06_Declarations.md#code-block)
>
## Do 语句 {#do-statements}
`do` 语句用于引入一个新的作用域,该作用域中可以含有一个或多个 `catch` 子句,`catch` 子句中定义了一些匹配错误条件的模式。`do` 语句作用域内定义的常量和变量只能在 `do` 语句作用域内使用。
@ -600,11 +566,9 @@ do {
#### do-statement {#do-statement}
> *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-clause {#catch-clause}
> *catch 子句* → **catch** [模式](./08_Patterns.md#pattern)<sub>可选</sub> [where 子句](#where-clause)<sub>可选</sub> [代码块](./06_Declarations.md#code-block)
@ -628,7 +592,6 @@ do {
> *编译器控制语句* → [线路控制语句](#line-control-statement)
>
> *编译器控制语句* → [诊断语句](#grammar-diagnostic-statement)
>
### 条件编译代码块 {#Conditional-Compilation-Block}
@ -648,12 +611,12 @@ statements
| 函数 | 可用参数 |
| --- | --- |
| `os()` | `OSX`, `iOS`, `watchOS`, `tvOS`, `Linux` |
| `arch()` | `i386`, `x86_64`, `arm`, `arm64` |
| `os()` | `OSX``iOS``watchOS``tvOS``Linux``Windows` |
| `arch()` | `i386``x86_64``arm``arm64` |
| `swift()` | `>=``<` 后跟版本号 |
| `compiler()` | `>=``<` 后跟版本号 |
| `canImport()` | 模块名 |
| `targetEnvironment()` | `simulator``macCatalyst` |
| `targetEnvironment()` | `simulator``macCatalyst` |
`swift()``compiler()` 之后的版本号包含有主版本号,可选副版本号,可选补丁版本号类似,并且用(`.`)来分隔。在比较符和版本号之间不能有空格,版本号与前面的函数相对应,比如 `compiler()` 对应的就是这个编译器的版本号,`swift()` 对应的就是你要编译的 `Swift` 语言的版本号。举个简单的例子,如果你在使用 `Swift 5` 的编译器,想编译 `Swift 4.2` ,可以看下面的例子:
@ -672,15 +635,14 @@ 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`。
>
你可以使用逻辑操作符 `&&``||``!` 来组合多个编译配置,还可以使用圆括号来进行分组。
@ -699,34 +661,27 @@ statements to compile if both compilation conditions are false
> 注意
>
>
> 即使没有被编译,编译配置中的语句仍然会被解析。然而,唯一的例外是编译配置语句中包含语言版本检测函数:仅当 `Swift` 编译器版本和语言版本检测函数中指定的版本号匹配时,语句才会被解析。这种设定能确保旧的编译器不会尝试去解析新 Swift 版本的语法。
>
> 即使没有被编译,上面编译配置中的每个语句仍然会被解析。然而,唯一的例外是编译配置语句中包含 `swift()` 或 `compiler()` 条件:这时仅当编译器版本和语言版本匹配时,语句才会被解析。这种设定能确保旧的编译器不会尝试去解析新 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-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) [编译条件](#compilation-condition) [语句(复数)](#statements)<sub>可选</sub>
>
#### grammar-else-directive-clause {#grammar-else-directive-clause}
> *else-directive 语句* → [else-directive](#grammar-else-directive) [语句(复数)](#statements)<sub>可选</sub>
>
> *if-directive* → **#if**
@ -736,7 +691,6 @@ statements to compile if both compilation conditions are false
> *else-directive* → **#else**
>
> *endif-directive* → **#endif**
>
#### compilation-condition {#compilation-condition}
@ -753,7 +707,6 @@ statements to compile if both compilation conditions are false
> *编译条件* → [编译条件](#compilation-condition) **&&** [编译条件](#compilation-condition)
>
> *编译条件* → [编译条件](#compilation-condition) **||** [编译条件](#compilation-condition)
>
#### grammar-platform-condition {#grammar-platform-condition}
@ -777,12 +730,10 @@ statements to compile if both compilation conditions are false
> *平台条件* → **targetEnvironment ( [环境](#grammar-environment) )**
#### operating-system {#operating-system}
> *操作系统* → **macOS** | **iOS** | **watchOS** | **tvOS**
>
> *操作系统* → **macOS** | **iOS** | **watchOS** | **tvOS****Linux****Windows**
#### 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>
@ -821,7 +772,6 @@ statements to compile if both compilation conditions are false
#### line-number {#line-number}
> *行号* → 大于 0 的十进制整数
>
#### file-name {#file-name}
> *文件名* → [静态字符串字面量](./02_Lexical_Structure.md#static-string-literal)
@ -839,7 +789,6 @@ statements to compile if both compilation conditions are false
> 编译时诊断语句语法
>
>
#### grammar-compile-time-diagnostic-statement {#grammar-compile-time-diagnostic-statement}
> *诊断语句* → **#error** **(** [诊断消息](#grammar-diagnostic-message) **)**
@ -870,11 +819,11 @@ if #available(platform name version, ..., *) {
> 可用性条件语法
>
>
#### availability-condition {#availability-condition}
> *可用性条件* → **#available** **(** [可用性参数列表](#availability-arguments) **)**
>
> *可用性条件* → **#unavaliable** **** [可用性参数列表](#availability-arguments) **)**
#### availability-arguments {#availability-arguments}
> *可用性参数列表* → [可用性参数](#availability-argument) | [可用性参数](#availability-argument) **,** [可用性参数列表](#availability-arguments)
@ -882,8 +831,6 @@ if #available(platform name version, ..., *) {
> *可用性参数* → [平台名称](#platform-name) [平台版本](#platform-version)
>
> *可用性条件* → __*__
>
>
#### platform-name {#platform-name}
> *平台名称* → **iOS** | **iOSApplicationExtension**
@ -893,7 +840,6 @@ if #available(platform name version, ..., *) {
> *平台名称* → **watchOS**
>
> *平台名称* → **tvOS**
>
#### platform-version {#platform-version}
> *平台版本* → [十进制数字](./02_Lexical_Structure.md#decimal-digits)

View File

@ -25,6 +25,8 @@
>
> *声明* → [类声明](#class-declaration)
>
> *声明* → [actor 声明](#actor-declaration)
>
> *声明* → [协议声明](#protocol-declaration)
>
> *声明* → [构造器声明](#initializer-declaration)
@ -481,7 +483,7 @@ repeatGreeting("Hello, world!", count: 2) // count 有标签, greeting 没有
更多关于内存安全和内存独占权的讨论,请参阅 [内存安全](../02_language_guide/24_Memory_Safety.md)。
如果一个闭包或者嵌套函数捕获了一个输入输出参数,那么这个闭包或者嵌套函数必须是非逃逸的。如果你需要捕获一个输入输出参数,但并不对其进行修改或者在其他代码中观察其值变化,那么你可以使用捕获列表来显式地表明这是个不可变捕获。
如果一个闭包或者嵌套函数捕获了一个输入输出参数,那么这个闭包或者嵌套函数必须是非逃逸的。如果你需要捕获一个输入输出参数,但并不对其进行修改,那么你可以使用捕获列表来显式地表明这是个不可变捕获。
```swift
func someFunction(a: inout Int) -> () -> Int {
@ -518,7 +520,7 @@ _ : 参数类型
以下划线(`_`)命名的参数会被显式忽略,无法在函数内使用。
一个参数的基本类型名称如果紧跟着三个点(`...`),会被视为可变参数。一个函数至多可以拥有个可变参数,且必须是最后一个参数。可变参数会作为包含该参数类型元素的数组处理。举例来讲,可变参数 `Int...` 会作为 `[Int]` 来处理。关于使用可变参数的例子,请参阅 [可变参数](../02_language_guide/06_Functions.md#variadic-parameters)。
一个参数的基本类型名称如果紧跟着三个点(`...`),会被视为可变参数。紧随在可变参数后的参数必须拥有参数标签。一个函数可以拥有个可变参数。可变参数会作为包含该参数类型元素的数组处理。举例来讲,可变参数 `Int...` 会作为 `[Int]` 来处理。关于使用可变参数的例子,请参阅 [可变参数](../02_language_guide/06_Functions.md#variadic-parameters)。
如果在参数类型后面有一个以等号(`=`)连接的表达式,该参数会拥有默认值,即给定表达式的值。当函数被调用时,给定的表达式会被求值。如果参数在函数调用时被省略了,就会使用其默认值。
@ -526,8 +528,8 @@ _ : 参数类型
func f(x: Int = 42) -> Int { return x }
f() // 有效,使用默认值
f(7) // 有效,提供了
f(x: 7) // 无效,该参数没有外部名称
f(x: 7) // 有效,使用传入的
f(7) // 无效,缺少参数标签
```
### 特殊方法 {#special-kinds-of-methods}
@ -568,10 +570,10 @@ let someFunction1: (Int, Int) -> Void = callable(_:scale:) // Error
let someFunction2: (Int, Int) -> Void = callable.callAsFunction(_:scale:)
```
如 [dynamicmemberlookup](./07_Attributes.md#dynamicmemberlookup) 描述的一样,`subscript(dynamicMemberLookup:)` 下标允许成员查找的语法糖。
如 [dynamicmemberlookup](./07_Attributes.md#dynamicmemberlookup) 描述的一样,`subscript(dynamicMember:)` 下标允许成员查找的语法糖。
### 抛出错误的函数和方法 {#throwing-functions-and-methods}
可以抛出错误的函数或方法必须使用 `throws` 关键字标记。这类函数和方法被称为抛出函数和抛出方法。它们有着下面的形式:
可以抛出错误的函数或方法必须使用 `throws` 关键字标记。这类函数和方法被称为抛出函数和抛出方法。它们有着下面的形式
```swift
func 函数名称(参数列表) throws -> 返回类型 {
@ -612,7 +614,25 @@ func someFunction(callback: () throws -> Void) rethrows {
}
```
抛出方法不能重写重抛方法,而且抛出方法不能满足协议对于重抛方法的要求。也就是说,重抛方法可以重写抛出方法,而且重抛方法可以满足协议对于抛出方法的要求。
抛出方法不能重写重抛方法,而且抛出方法不能满足协议对于重抛方法的要求。也就是说,重抛方法可以重写抛出方法,而且重抛方法可以满足抛出方法的协议要求。
### 异步函数和方法 {#asynchronous-functions-and-methods}
异步运行的函数和方法必须用 `async` 关键字标记。这些函数和方法称为异步函数和异步方法。它们有着下面的形式:
```swift
func 函数名称(参数列表) async -> 返回类型 {
语句
}
```
对异步函数或异步方法的调用必须包含在 `await` 表达式中,即它们必须在 `await` 操作符的作用域内。
`async` 关键字是函数类型中的一部分。同步函数是异步函数的子类型。所以,你可以在使用异步函数的地方,使用同步函数。同步方法可以重写异步方法,且同步方法可以满足对异步方法的协议要求。
你可以根据函数是否异步来重写函数。在调用点,上下文决定了应用哪种函数进行重写:异步上下文使用异步函数;同步上下文使用同步函数。
异步方法无法重写同步方法,异步方法也无法满足同步方法的协议要求。反过来说,同步方法可以重写异步方法,并且同步方法可以满足异步方法的协议要求。
### 永不返回的函数 {#functions-that-never-return}
Swift 定义了 `Never` 类型,它表示函数或者方法不会返回给它的调用者。`Never` 返回类型的函数或方法可以称为不归,不归函数、方法要么引发不可恢复的错误,要么永远不停地运作,这会使调用后本应执行得代码就不再执行了。但即使是不归函数、方法,抛错函数和重抛出函数也可以将程序控制转移到合适的 `catch` 代码块。
@ -644,9 +664,9 @@ Swift 定义了 `Never` 类型,它表示函数或者方法不会返回给它
#### function-signature {#function-signature}
>
>
> *函数签名* → [参数子句列表](#parameter-clauses) **throws**<sub>可选</sub> [函数结果](#function-result)<sub>可选</sub>
> *函数签名* → [参数子句列表](#parameter-clauses) **async**<sub>可选</sub> **throws**<sub>可选</sub> [函数结果](#function-result)<sub>可选</sub>
>
> *函数签名* → [参数子句列表](#parameter-clauses) **rethrows** [函数结果](#function-result)<sub>可选</sub>
> *函数签名* → [参数子句列表](#parameter-clauses) **async**<sub>可选</sub> **rethrows** [函数结果](#function-result)<sub>可选</sub>
>
>
#### function-result {#function-result}
@ -697,7 +717,7 @@ Swift 定义了 `Never` 类型,它表示函数或者方法不会返回给它
## 枚举声明 {#enumeration-declaration}
在程序中使用*枚举声明enumeration declaration* 来引入一个枚举类型。
枚举声明有两种基本形式,使用关键字 `enum` 来声明。枚举声明体包含零个或多个值,称为枚举用例,还可包含任意数量的声明,包括计算型属性、实例方法、类型方法、构造器、类型别名,甚至其他枚举、结构体和类。枚举声明不能包含析构器或者协议声明。
枚举声明有两种基本形式,使用关键字 `enum` 来声明。枚举声明体包含零个或多个值,称为枚举用例,还可包含任意数量的声明,包括计算型属性、实例方法、类型方法、构造器、类型别名,甚至其他枚举、结构体、类和 actor。枚举声明不能包含析构器或者协议声明。
枚举类型可以采纳任意数量的协议,但是枚举不能从类、结构体和其他枚举继承。
@ -894,7 +914,7 @@ struct 结构体名称: 采纳的协议 {
}
```
结构体内可包含零个或多个声明。这些声明可以包括存储型和计算型属性、类型属性、实例方法、类型方法、构造器、下标、类型别名,甚至其他结构体、类、和枚举声明。结构体声明不能包含析构器或者协议声明。关于结构体的详细讨论和示例,请参阅 [类和结构体](../02_language_guide/09_Structures_And_Classes.md)。
结构体内可包含零个或多个声明。这些声明可以包括存储型和计算型属性、类型属性、实例方法、类型方法、构造器、下标、类型别名,甚至其他结构体、类、actor 和枚举声明。结构体声明不能包含析构器或者协议声明。关于结构体的详细讨论和示例,请参阅 [类和结构体](../02_language_guide/09_Structures_And_Classes.md)。
结构体可以采纳任意数量的协议,但是不能继承自类、枚举或者其他结构体。
@ -956,7 +976,7 @@ class 类名: 超类, 采纳的协议 {
}
```
类内可以包含零个或多个声明。这些声明可以包括存储型和计算型属性、实例方法、类型方法、构造器、唯一的析构器、下标、类型别名,甚至其他结构体、和枚举声明。类声明不能包含协议声明。关于类的详细讨论和示例,请参阅 [类和结构体](../02_language_guide/09_Structures_And_Classes.md)。
类内可以包含零个或多个声明。这些声明可以包括存储型和计算型属性、实例方法、类型方法、构造器、唯一的析构器、下标、类型别名,甚至其他类、结构体、actor 和枚举声明。类声明不能包含协议声明。关于类的详细讨论和示例,请参阅 [类和结构体](../02_language_guide/09_Structures_And_Classes.md)。
一个类只能继承自一个超类,但是可以采纳任意数量的协议。超类紧跟在类名和冒号后面,其后跟着采纳的协议。泛型类可以继承自其它泛型类和非泛型类,但是非泛型类只能继承自其它非泛型类。当在冒号后面写泛型超类的名称时,必须写上泛型类的全名,包括它的泛型形参子句。
@ -976,7 +996,7 @@ class 类名: 超类, 采纳的协议 {
类实例属性可以用点语法(`.`)来访问,请参阅 [访问属性](../02_language_guide/09_Structures_And_Classes.md#accessing-properties)。
类是引用类型。当被赋予常量或变量,或者传递给函数作为参数时,类的实例会被引用,而不是被复制。关于引用类型的更多信息,请参阅 [结构体和枚举是值类型](../02_language_guide/09_Structures_And_Classes.md#structures-and-enumerations-are-value-types)。
类是引用类型。当被赋予常量或变量,或者传递给函数作为参数时,类的实例会被引用,而不是被复制。关于引用类型的更多信息,请参阅 [类是引用类型](../02_language_guide/09_Structures_And_Classes.md#classes-are-reference-types)。
可以使用扩展声明来扩展类的行为,请参阅 [扩展声明](#extension-declaration)。
@ -1011,6 +1031,37 @@ class 类名: 超类, 采纳的协议 {
> *类成员* → [声明](#declaration) | [编译控制流语句](./05_Statements.md#compiler-control-statement)
>
## Actor 声明 {#actor-declaration}
可以在程序中使用 *actor 声明actor declaration* 来引入一个 actor。Actor 声明使用关键字 `actor`,遵循如下的形式:
```swift
actor 名称: 遵循的协议 {
多条声明
}
```
Actor 内可包含零个或多个声明。这些声明包括存储属性和计算属性、实例方法、类型方法、构造器、唯一的析构器、下标、类型别名,甚至其他类、结构体和枚举声明。关于包含多种声明的 actors 的几种例子,请参考 [Actors](../02_language_guide/28_Concurrency.md#Actors)。
Actor 类型可以遵循任意数量的协议,但是不能继承于其他类、枚举、结构体或者其他 actor。但是用 `@objc` 标记的 actor 隐性地遵循了 `NSObjectProtocol` 协议,且作为 `NSObject` 的子类型暴露给 Objective-C 运行时。
有两种方法来创建声明过的 actor
* 如 [构造器](../02_language_guide/14_Initialization.md#initializers) 中所述,调用 actor 的构造器。
* 如 [默认构造器](../02_language_guide/14_Initialization.md#default-initializers) 中所述,如果没有定义构造器,调用 actor 的默认构造器actor 声明的所有属性会有默认值。
默认情况下actor 里的成员变量(方法)是与 actor 隔离的。某个函数内的代码或者某个属性 getter 的代码是在 actor 中执行的。由于这些代码已经在同一个 actor 中运行,所以 actor 内部的代码可以与之同步地交互。但 actor 外部的代码必须用 `await` 标记 actor 内部的代码,以表示这段代码正在另一个 actor 中异步执行。关键路径无法引用 actor 中的隔离成员。Actor 隔离的存储属性能以输入输出参数的方式传递给同步函数,而不能传递给异步函数。
Actor 也有非隔离的成员变量(方法),这些成员变量(方法)以 `nonisolated` 关键字标记。非隔离的成员变量(方法)执行起来像 actor 外部的代码:它无法与 actor 任意一个隔离状态交互,而且使用时也无需用 `await` 标记。
Actor 的成员变量(方法)只有是异步状态或非隔离状态时, 才能用 `@objc` 修饰符标记。
初始化 actor 所声明的属性的过程在 [构造过程](../02_language_guide/14_Initialization.md) 一章中有说明。
如 [属性访问](../02_language_guide/09_Structures_And_Classes.md#accessing-properties) 中所述actor 实例的属性可以通过使用点语法(.)的方式来访问到。
Actor 是引用类型当被赋值给变量或常量以及作为参数传递给函数调用时actor 的实例会被引用,而不是被复制。关于引用类型的更多信息,请参阅 [类是引用类型](../02_language_guide/09_Structures_And_Classes.md#classes-are-reference-types)。
可以使用扩展声明来扩展 actor 类型的行为,请参阅 [扩展声明](#extension-declaration)。
## 协议声明 {#protocol-declaration}
*协议声明protocol declaration* 可以为程序引入一个命名的协议类型。协议声明只能在全局区域使用 `protocol` 关键字来进行声明,并遵循如下形式:
@ -1074,9 +1125,11 @@ protocol SomeProtocol: AnyObject {
> *协议主体* → **{** [协议成员声明列表](#protocol-member-declarations)<sub>可选</sub> **}**
>
>
#### protocol-members {#protocol-members}
>
> *协议多个成员* → [协议成员](#protocol-member) [协议多个成员](#protocol-members)<sub>可选</sub>
>
>
>
#### protocol-member {#protocol-member}
>
> *协议成员* → [协议成员声明](#protocol-member-declaration) | [编译控制流语句](./05_Statements.md#compiler-control-statement)
@ -1095,11 +1148,8 @@ protocol SomeProtocol: AnyObject {
>
> *协议成员声明* → [协议关联类型声明](#protocol-associated-type-declaration)
>
>
#### protocol-member-declarations {#protocol-member-declarations}
>
> *协议成员声明列表* → [协议成员声明](#protocol-member-declaration) [协议成员声明列表](#protocol-member-declarations)<sub>可选</sub>
>
> *协议成员声明* → [类型别名声明](#type-alias-declaration)
>
### 协议属性声明 {#protocol-property-declaration}
协议可以通过在协议声明主体中引入一个协议属性声明,来声明符合的类型必须实现的属性。协议属性声明有一种特殊的变量声明形式:
@ -1261,7 +1311,7 @@ convenience init(参数列表) {
> 如果使用 `required` 声明修饰符标记一个构造器,在子类中重写这种构造器时,无需使用 `override` 修饰符。
>
就像函数和方法,构造器也可以抛出或者重抛错误,你可以在构造器参数列表的圆括号之后使用 `throws` 或 `rethrows` 关键字来表明相应的抛出行为。
就像函数和方法,构造器也可以抛出或者重抛错误,你可以在构造器参数列表的圆括号之后使用 `throws` 或 `rethrows` 关键字来表明相应的抛出行为。同样,构造器也可以是异步的,你可以使用 `async` 关键字来表明这一点。
关于在不同类型中声明构造器的例子,请参阅 [构造过程](../02_language_guide/14_Initialization.md)。
@ -1304,7 +1354,6 @@ if let actualInstance = SomeStruct(input: "Hello") {
更多关于可失败构造器的信息和例子,请参阅 [可失败构造器](../02_language_guide/14_Initialization.md#failable-initializers)。
#### grammer-of-an-initializer-declaration {#grammer-of-an-initializer-declaration}
> 构造器声明语法
>
@ -1707,11 +1756,11 @@ Swift 定义了大量的优先级组来与标准库的运算符配合使用,
>
#### precedence-group-declaration {#precedence-group-declaration}
> *优先级组声明* → **precedence**[优先级组名称](#precedence-group-name){[多优先级组属性](#precedence-group-attributes)<sub>可选</sub> }
> *优先级组声明* → **precedencegroup** [优先级组名称](#precedence-group-name) **{** [多优先级组属性](#precedence-group-attributes)<sub>可选</sub> **}**
>
#### precedence-group-attributes {#precedence-group-attributes}
> *优先级组属性* → [优先级组属性](#precedence-group-attribute)[多优先级组属性](#precedence-group-attributes)<sub>可选</sub> **{** **}**
> *优先级组属性* → [优先级组属性](#precedence-group-attribute) [多优先级组属性](#precedence-group-attributes)<sub>可选</sub>
>
#### precedence-group-attribute {#precedence-group-attribute}
@ -1724,30 +1773,30 @@ Swift 定义了大量的优先级组来与标准库的运算符配合使用,
>
#### precedence-group-relation {#precedence-group-relation}
>
> *优先级组关系* → **higherThan:**[多优先级组名称](#precedence-group-names)
> *优先级组关系* → **higherThan** **:** [多优先级组名称](#precedence-group-names)
>
> *优先级组关系* → **lowerThan:**[多优先级组名称](#precedence-group-names)
> *优先级组关系* → **lowerThan** **:** [多优先级组名称](#precedence-group-names)
>
>
#### precedence-group-assignment {#precedence-group-assignment}
>
> *优先级组赋值* → **assignment:**[布尔字面值](./02_Lexical_Structure.md#boolean-literal)
> *优先级组赋值* → **assignment** **:** [布尔字面值](./02_Lexical_Structure.md#boolean-literal)
>
#### precedence-group-associativity {#precedence-group-associativity}
> *优先级组结合性* → **associativity:left**
> *优先级组结合性* → **associativity** **:** **left**
>
> *优先级组结合性* → **associativity:right**
> *优先级组结合性* → **associativity** **:** **right**
>
> *优先级组结合性* → **associativity:none**
> *优先级组结合性* → **associativity** **:** **none**
>
#### precedence-group-names {#precedence-group-names}
> *多优先级组名称* → [优先级组名称](#precedence-group-name) | [优先级组名称](#precedence-group-name) | [优先级组名称](#precedence-group-name)
> *多优先级组名称* → [优先级组名称](#precedence-group-name) | [优先级组名称](#precedence-group-name) **,** [优先级组名称](#precedence-group-names)
>
#### precedence-group-name {#precedence-group-name}
> *优先级组名称* →[标识符](./02_Lexical_Structure.md#identifier)
> *优先级组名称* → [标识符](./02_Lexical_Structure.md#identifier)
>
## 声明修饰符 {#Declaration-Modifiers}
@ -1802,20 +1851,30 @@ Swift 定义了大量的优先级组来与标准库的运算符配合使用,
该修饰符用于修饰变量或存储型变量属性,表示该变量或属性持有其存储的对象的弱引用。这种变量或属性的类型必须是可选的类类型。使用 `weak` 修饰符可避免强引用循环。关于 `weak` 修饰符的更多信息和例子,请参阅 [弱引用](../02_language_guide/24_Automatic_Reference_Counting.md#resolving-strong-reference-cycles-between-class-instances)。
### 访问控制级别 {#access-control-levels}
Swift 提供了个级别的访问控制:`public`、`internal` 和 `private`。可以使用以下任意一种访问级别修饰符来指定声明的访问级别。访问控制在 [访问控制](../02_language_guide/26_Access_Control.md) 中有详细讨论。
Swift 提供了个级别的访问控制:`open`、`public`、`internal`、`file private` 和 `private`。可以使用以下任意一种访问级别修饰符来指定声明的访问级别。访问控制在 [访问控制](../02_language_guide/26_Access_Control.md) 中有详细讨论。
`open`
该修饰符表示声明可被同模块的代码访问,且可作为基类使用,只要其他模块导入了声明所在的模块,也可以进行访问,且可作为基类使用。
`public`
该修饰符表示声明可被同模块的代码访问,只要其他模块导入了声明所在的模块,也可以进行访问。
该修饰符表示声明可被同模块的代码访问,且可作为基类使用,只要其他模块导入了声明所在的模块,也可以进行访问,但不可作为基类使用
`internal`
该修饰符表示声明只能被同模块的代码访问。默认情况下,绝大多数声明会被隐式标记 `internal` 访问级别修饰符。
`private`
`fileprivate`
该修饰符表示声明只能被所在源文件的代码访问。
`private`
该修饰符表示声明只能被声明所直接包含的作用域内的代码访问。
为了达到访问控制的目的,同一文件中相同类型的扩展共享访问控制作用域。如果这些扩展与它们扩展的类型也在同一个文件中,则这些扩展共享该类型的访问控制作用域。在类型声明中声明的私有成员可以从扩展中访问,在某个扩展中声明的私有成员可以从其他扩展和类型声明中访问。
以上访问级别修饰符都可以选择带上一个参数,该参数由一对圆括号和其中的 `set` 关键字组成(例如,`private(set)`)。使用这种形式的访问级别修饰符来限制某个属性或下标的 setter 的访问级别低于其本身的访问级别正如 [Getter 和 Setter](../02_language_guide/26_Access_Control.md#getters-and-setters) 中所讨论的
@ -1824,19 +1883,34 @@ Swift 提供了三个级别的访问控制:`public`、`internal` 和 `private`
>
#### declaration-modifier {#declaration-modifier}
> *声明修饰符* → **class** | **convenience**| **dynamic** | **final** | **infix** | **lazy** | **mutating** | **nonmutating** | **optional** | **override** | **postfix** | **prefix** | **required** | **static** | **unowned** | **unowned ( safe )** | **unowned ( unsafe )** | **weak**
> *声明修饰符* → **class** | **convenience** | **dynamic** | **final** | **infix** | **lazy** | **optional** | **override** | **postfix** | **prefix** | **required** | **static** | **unowned** | **unowned ( safe )** | **unowned ( unsafe )** | **weak**
>
> 声明修饰符 → [访问级别修饰符](#access-level-modifier)
>
> 声明修饰符 → [可变修饰符](#mutation-modifier)
>
> 声明修饰符 → [actor 隔离修饰符](#actor-isolation-modifier)
>
#### declaration-modifiers {#declaration-modifiers}
>
> *声明修饰符列表* → [声明修饰符](#declaration-modifier) [声明修饰符列表](#declaration-modifiers)<sub>可选</sub>
#### access-level-modifier {#access-level-modifier}
> 访问级别修饰符 → **internal** | **internal ( set )**
>
> 访问级别修饰符 → **private** | **private ( set )**
>
> 访问级别修饰符 → **fileprivate** | **fileprivate ( set )**
>
> 访问级别修饰符 → **internal** | **internal ( set )**
>
> 访问级别修饰符 → **public** | **public ( set )**
>
> 访问级别修饰符 → **open** | **open ( set )**
>
#### mutation-modifier {#mutation-modifier}
> 可变修饰符 → **mutating** | **nonmutating**
>
#### actor-isolation-modifier {#actor-isolation-modifier}
> actor 隔离修饰符 → **nonisolated**
>

View File

@ -24,6 +24,8 @@
- `iOSApplicationExtension`
- `macOS`
- `macOSApplicationExtension`
- `macCatalyst`
- `macCatalystApplicationExtension`
- `watchOS`
- `watchOSApplicationExtension`
- `tvOS`
@ -182,11 +184,11 @@ repeatLabels(a: "four") // Error
### `dynamicMemberLookup` {#dynamicmemberlookup}
该特性用于类、结构体、枚举或协议,让其能在运行时查找成员。该类型必须实现 `subscript(dynamicMemberLookup:)` 下标。
该特性用于类、结构体、枚举或协议,让其能在运行时查找成员。该类型必须实现 `subscript(dynamicMember:)` 下标。
在显式成员表达式中,如果指定成员没有相应的声明,则该表达式被理解为对该类型的 `subscript(dynamicMemberLookup:)` 下标调用,将有关该成员的信息作为参数传递。下标接收参数既可以是键路径,也可以是成员名称字符串;如果你同时实现这两种方式的下标调用,那么以键路径参数方式为准。
在显式成员表达式中,如果指定成员没有相应的声明,则该表达式被理解为对该类型的 `subscript(dynamicMember:)` 下标调用,将有关该成员的信息作为参数传递。下标接收参数既可以是键路径,也可以是成员名称字符串;如果你同时实现这两种方式的下标调用,那么以键路径参数方式为准。
`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`。下标返回值类型可以为任意类型。
`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` 时。例如:
@ -257,6 +259,29 @@ print(wrapper.x)
该特性不能用于嵌套在函数内的声明,也不能用于 `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 中表示。
@ -278,6 +303,8 @@ import AppKit
NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
```
编译生成可执行程序的 Swift 代码最多只能拥有一个顶级代码入口,请参阅[顶级代码](../03_language_reference/06_Declarations.md#top-level-code)。
### `NSCopying` {#nscopying}
该特性用于修饰一个类的存储型变量属性。该特性将使属性的设值方法使用传入值的`副本`进行赋值,这个值由传入值的 `copyWithZone(_:)` 方法返回,而不是传入值本身。该属性的类型必需符合 `NSCopying` 协议。
@ -328,7 +355,7 @@ class ExampleClass: NSObject {
### `propertyWrapper` {#propertywrapper}
在类、结构体或者枚举的声明时使用该特性,可以让其成为一个属性包装器。如果将该特性应用在一个类型上,将会创建一个与该类型同名的自定义特性。将这个新的特性用于类、结构体、枚举的属性,则可以通过包装器的实例封装对该属性的访问。局部和全局变量不能使用属性包装器。
在类、结构体或者枚举的声明时使用该特性,可以让其成为一个属性包装器。如果将该特性应用在一个类型上,将会创建一个与该类型同名的自定义特性。将这个新的特性用于类、结构体、枚举的属性,则可以通过包装器的实例封装对该属性的访问;本地存储变量声明也能利用这个特性完成对它的访问封装。局部和全局变量不能使用属性包装器。
包装器必须定义一个 `wrappedValue` 实例属性。该属性 _wrapped value_ 是该属性存取方法暴露的值。大多数时候,`wrappedValue` 是一个计算属性,但它可以是一个存储属性。包装器负责定义和管理其包装值所需的任何底层存储。编译器通过在包装属性的名称前加下划线(`_`)来为包装器的实例提供同步存储。例如,`someProperty` 的包装器存储为 `_someProperty`。包装器的同步存储具有 `private` 的访问控制级别。
@ -391,7 +418,258 @@ s.$x // SomeProjection value
s.$x.wrapper // WrapperWithProjection value
```
### `requires-stored-property-inits` {#requires-stored-property-inits}
### `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` 特性。
@ -405,6 +683,14 @@ s.$x.wrapper // WrapperWithProjection value
如果你不想使用这个特性,可以提供一个 `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` 的类型。
@ -453,6 +739,14 @@ Interface Builder 特性是 Interface Builder 用来与 Xcode 同步的声明特
在函数或者方法声明上使用该特性,它表示参数将不会被存储以供延迟执行。这将确保参数不会超出函数调用的生命周期。在使用 `escaping` 特性声明的函数类型中访问属性和方法时需要显式地使用 `self.`。关于如何使用 `escaping` 特性的例子,请参阅 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures)。
### `Sendable` {#Sendable}
在函数类型上使用该特性以指示该函数是闭包或可发送的。在函数类型上使用该特性与使非函数类型符合 `Sendable` 协议具有相同的意义。
当一个函数或闭包被用在需要可发送值的情况,且该函数或闭包满足可发送的要求,则可以在该函数或闭包上推断此特性。
可发送函数类型是对应的不可发送函数类型的一个子类型。
## Switch Case 特性 {#switch-case-attributes}
你只能在 switch cases 语句中使用 switch case 特性。

View File

@ -7,6 +7,8 @@
> *空白字符* → [空白字符项](./02_Lexical_Structure.md#whitespace-item) [空白字符](./02_Lexical_Structure.md#whitespace)<sub>可选</sub>
>
> *空白字符项* → [换行符](./02_Lexical_Structure.md#line-break)
>
> *空白字符项* → [内联空间](./02_Lexical_Structure.md#ginline-space)
>
> *空白字符项* → [注释](./02_Lexical_Structure.md#comment)
>
@ -108,7 +110,7 @@
> 字面量语法
>
> *字面量* → [数值型字面量](./02_Lexical_Structure.md#numeric-literal) | [字符串字面量](./02_Lexical_Structure.md#string-literal) | [布尔字面量](./02_Lexical_Structure.md#boolean-literal) | [空字面量](./02_Lexical_Structure.md#nil-literal)
> *字面量* → [数值型字面量](./02_Lexical_Structure.md#numeric-literal) | [字符串字面量](./02_Lexical_Structure.md#string-literal) | [正则表达式字面量](./02_Lexical_Structure.md#regular-expression-literal) | [布尔字面量](./02_Lexical_Structure.md#boolean-literal) | [空字面量](./02_Lexical_Structure.md#nil-literal)
>
> *数值型字面量* → **-**<sub>可选</sub>[整形字面量](./02_Lexical_Structure.md#integer-literal) | **-**<sub>可选</sub>[浮点型字面量](./02_Lexical_Structure.md#floating-point-literal)
>
@ -243,6 +245,19 @@
>
> *转义换行符* → [转义序列](./02_Lexical_Structure.md#escape-sequence) [空白](./02_Lexical_Structure.md#whitespace)<sub>可选</sub> [换行符](./02_Lexical_Structure.md#line-break)
<!-- -->
> 正则表达式字面量语法
>
> *正则表达式字面量* → [正则表达式字面量开分隔定界符](./02_Lexical_Structure.md#regular-expression-literal-opening-delimiter) [正则表达式](./02_Lexical_Structure.md#regular-expression) [正则表达式字面量闭分隔定界符](./02_Lexical_Structure.md#regular-expression-literal-closing-delimiter)
>
> *正则表达式* → 任何正则表达式
>
> *正则表达式字面量开分隔定界符* → [正则表达式扩展分隔符](./02_Lexical_Structure.md#grammar_extended-regular-expression-literal-delimiter)<sub>可选</sub> **/**
>
> *正则表达式字面量闭分割定界符* → **/** [正则表达式扩展分隔符](./02_Lexical_Structure.md#grammar_extended-regular-expression-literal-delimiter)<sub>可选</sub>
>
> *正则表达式扩展分隔符* → **#** [正则表达式扩展分隔符](./02_Lexical_Structure.md#grammar_extended-regular-expression-literal-delimiter)<sub>可选</sub>
<!-- -->
@ -329,11 +344,15 @@
>
> *类型* → [隐式解析可选类型](./03_Types.md#implicitly-unwrapped-optional-type)
>
> *类型* → [协议合成类型](./03_Types.md#protocol-composition-type)
> *类型* → [协议合成类型](./03_Types.md#protocol-composition-type)
>
> *类型* → [隐含类型](./03_Types.md#opaque-type)
>
> *类型* → [元类型](./03_Types.md#metatype-type)
>
> *类型* → **Any**
> *类型* → [任意类型](./03_Types.md#any-type)
>
> *类型* → **Self**
> *类型* → [自身类型](./03_Types.md#self-type)
>
> *类型* → **** [type](./03_Types.md#metatype-type) ****
@ -368,7 +387,7 @@
> 函数类型语法
>
> *函数类型* → [类型](./03_Types.md#type) **throws**<sub>可选</sub>**->** [类型](./03_Types.md#type)
> *函数类型* → [类型](./03_Types.md#type) [函数类型子句](.03/Types.md#function-type-argument-clause) **async**<sub>可选</sub> **throws**<sub>可选</sub>**->** [类型](./03_Types.md#type)
>
> *函数类型* → [类型](./03_Types.md#) **rethrows** **->** [类型](./03_Types.md#)
>
@ -427,7 +446,7 @@
>
> *类型继承从句* → **:** [类型继承集](./03_Types.md#type-inheritance-list)
>
> *类型继承集* → [类型标识符](./03_Types.md#type-identifier) | [类型标识符](./03_Types.md#type-identifier) **,** [类型继承集](./03_Types.md#type-inheritance-list)
> *类型继承集* → [特性](./07_Attributes.md#attributes)<sub>可选</sub> [类型标识符](./03_Types.md#type-identifier) | [特性](./07_Attributes.md#attributes)<sub>可选</sub> [类型标识符](./03_Types.md#type-identifier) **,** [类型继承集](./03_Types.md#type-inheritance-list)
>
> *类条件* → **class**
@ -460,17 +479,23 @@
<!-- -->
> 二元表达式语法
> await 表达式语法
>
> *await 操作符* → **await**
<!-- -->
> 中缀表达式语法
>
> *二元表达式* → [二元运算符](./02_Lexical_Structure.md#binary-operator) [前缀表达式](./04_Expressions.md#prefix-expression)
> *中缀表达式* → [中缀运算符](./02_Lexical_Structure.md#infix-operator) [前缀表达式](./04_Expressions.md#prefix-expression)
>
> *二元表达式* → [赋值操作符](./06_Declarations.md#class-declaration) [try 运算符](./04_Expressions.md#try-operator)<sub>可选</sub> [前缀表达式](./04_Expressions.md#prefix-expression)
> *中缀表达式* → [赋值操作符](./06_Declarations.md#class-declaration) [try 运算符](./04_Expressions.md#try-operator)<sub>可选</sub> [前缀表达式](./04_Expressions.md#prefix-expression)
>
> *二元表达式* → [条件运算符](./04_Expressions.md#conditional-operator) [try 运算符](./04_Expressions.md#try-operator)<sub>可选</sub> [前缀表达式](./04_Expressions.md#prefix-expression)
> *中缀表达式* → [条件运算符](./04_Expressions.md#conditional-operator) [try 运算符](./04_Expressions.md#try-operator)<sub>可选</sub> [前缀表达式](./04_Expressions.md#prefix-expression)
>
> *二元表达式* → [类型转换运算符](./04_Expressions.md#type-casting-operator)
> *中缀表达式* → [类型转换运算符](./04_Expressions.md#type-casting-operator)
>
> *二元表达式* → [二元表达式](./04_Expressions.md#binary-expression) [二元表达式列表](./04_Expressions.md#binary-expressions)<sub>可选</sub>
> *中缀表达式* → [中缀表达式](./04_Expressions.md#infix-expression) [中缀表达式列表](./04_Expressions.md#infix-expressions)<sub>可选</sub>
>
<!-- -->
@ -588,7 +613,7 @@
> *闭包表达式* → **{** [闭包签名](./04_Expressions.md#closure-signature)<sub>可选</sub> [语句](./04_Expressions.md#statements) **}**
>
>
> 闭包签名* → [参数子句](./04_Expressions.md#parameter-clause) [函数结果](./06_Declarations.md#function-result)<sub>可选</sub> **in**
> *闭包签名* → [捕获列表](./04_Expressions.md#capture-list) [参数子句](./04_Expressions.md#parameter-clause) **async**<sub>可选</sub> **throws**<sub>可选</sub> [函数结果](./06_Declarations.md#function-result)<sub>可选</sub> **in**
>
> *闭包签名* → [标识符列表](./04_Expressions.md#identifier-list) [函数结果](./06_Declarations.md#function-result)<sub>可选</sub> **in**
>
@ -655,7 +680,7 @@
> *key-path 组件* → [标识符](./02_Lexical_Structure.md#identifier) [多个 key-path 后缀](./04_Expressions.md#key-path-postfixes)<sub>可选<sub> | [多个 key-path 后缀](./04_Expressions.md#key-path-postfixes)
> *多个 key-path 后缀* → [key-path 后缀](./04_Expressions.md#key-path-postfix) [多个 key-path 后缀](./04_Expressions.md#key-path-postfixes)<sub>可选<sub> key-path-postfixes {./04_Expressions.md#key-path-postfixes}
>
> *key-path 后缀* → **?** | **!** | **self** | **\[** [函数调用参数表](./04_Expressions.md#function-call-argument-list) **\]**
> *key-path 后缀* → **?** | **!** | **self** | **[** [函数调用参数表](./04_Expressions.md#function-call-argument-list) **]**
>
@ -734,12 +759,14 @@
> 显式成员表达式语法
>
> *显式成员表达式* → [后缀表达式](./04_Expressions.md#postfix-expression) **.** [十进制数字] (02_Lexical_Structure.md#decimal-digit)
> *显式成员表达式* → [后缀表达式](./04_Expressions.md#postfix-expression) **.** [十进制数字](02_Lexical_Structure.md#decimal-digit)
>
> *显式成员表达式* → [后缀表达式](./04_Expressions.md#postfix-expression) **.** [标识符](02_Lexical_Structure.md#identifier) [泛型实参子句](./09_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub><br/>
>
> *显式成员表达式* → [后缀表达式](./04_Expressions.md#postfix-expression) **.** [标识符] (02_Lexical_Structure.md#identifier) **(** [参数名称](./04_Expressions.md#argument-names) **)**
>
> *显式成员表达式* → [后缀表达式](./04_Expressions.md#postfix-expression) **.** [标识符](02_Lexical_Structure.md#identifier) **(** [参数名称](./04_Expressions.md#argument-names) **)**
>
> *显式成员表达式* → [后缀表达式](./04_Expressions.md#postfix-expression) [条件编译块](./05_Statements.md#conditional-compilation-block)
>
> *参数名称* → [参数名](./04_Expressions.md#argument-name) [参数名称](./04_Expressions.md#argument-names)<sub>可选</sub><br/>
>
> *参数名* → [标识符](./02_Lexical_Structure.md#identifier) **:**
@ -821,12 +848,13 @@
>
> *while 语句* → **while** [条件集](./05_Statements.md#condition-list) [代码块](./06_Declarations.md#code-block)
>
> *条件集* → [条件](./05_Statements.md#condition) | [条件](./05_Statements.md#condition) **,** [条件集](./05_Statements.md#condition-list)
> *条件集* → [条件](./05_Statements.md#condition) | [条件](./05_Statements.md#condition) **,** [条件集](./05_Statements.md#condition-list)
>
> *条件* → [表达式](./04_Expressions.md#expression) | [可用性条件](./05_Statements.md#availability-condition) | [case 条件](./05_Statements.md#case-condition) | [可选绑定条件](./05_Statements.md#optional-binding-condition)
>
> *case 条件* → **case** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer)
>
> *可选绑定条件* → **let** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer) | **var** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer)
> *可选绑定条件* → **let** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer)<sub>可选</sub> | **var** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer)<sub>可选</sub>
>
<!-- -->
@ -978,7 +1006,7 @@
>
> *catch 子句集* → [catch 子句](./05_Statements.md#catch-clause) [catch 子句集](05_Statements.md#catch-clauses)<sub>可选</sub>
>
> *catch 子句* → **catch** [catch 模式列表]()<sub>可选</sub> [代码块](./06_Declarations.md#code-block)<sub>可选</sub>
> *catch 子句* → **catch** [catch 模式列表](./05_Statements.md#catch-pattern-list)<sub>可选</sub> [代码块](./06_Declarations.md#code-block)<sub>可选</sub>
>
> *catch 模式列表* → [catch 模式](./05_Statements.md#catch-pattern) | [catch 模式](./05_Statements.md#catch-pattern) [catch 模式列表](./05_Statements.md#catch-pattern-list)
>
@ -1120,6 +1148,8 @@
> *声明* → [结构体声明](./06_Declarations.md#struct-declaration)
>
> *声明* → [类声明](./06_Declarations.md#class-declaration)
>
> *声明* → [actor 声明](./06_Declarations.md#actor-declaration)
>
> *声明* → [协议声明](./06_Declarations.md#protocol-declaration)
>
@ -1249,10 +1279,10 @@
>
> *函数名* → [标识符](./02_Lexical_Structure.md#identifier) | [运算符](./02_Lexical_Structure.md#operator)
>
> *函数签名* → [参数子句](./06_Declarations.md#parameter-clause) **throws**<sub>可选</sub> [函数结果](./06_Declarations.md#function-result)<sub>可选</sub>
> *函数签名* → [参数子句](./06_Declarations.md#parameter-clause) **async**<sub>可选</sub> **throws**<sub>可选</sub> [函数结果](./06_Declarations.md#function-result)<sub>可选</sub>
>
> *函数签名* → [参数子句](./06_Declarations.md#parameter-clause) **rethrows** [函数结果](./06_Declarations.md#function-result)<sub>可选</sub>
> *函数签名* → [参数子句](./06_Declarations.md#parameter-clause) **async**<sub>可选</sub> **rethrows** [函数结果](./06_Declarations.md#function-result)<sub>可选</sub>
>
> *函数结果* → **->** [特性](./07_Attributes.md#attributes)<sub>可选</sub> [类型](./03_Types.md#type)
>
@ -1516,6 +1546,8 @@
> *声明修饰符* → [访问级别修饰符](./07_Attributes.md#access-level-modifier)
>
> *声明修饰符* → [可变性修饰符](./07_Attributes.md#mutation-modifier)
>
> *声明修饰符* → [actor 隔离修饰符](./06_Deeclarations.md#actor-isolation-modifier)
>
> *声明修饰符集* → [声明修饰符](./06_Declarations.md#declaration-modifier) [声明修饰符集](./06_Declarations.md#declaration-modifiers)<sub>可选</sub>
>
@ -1531,6 +1563,8 @@
>
> *可变性修饰符* → **mutating** | **nonmutating**
>
> *actor 隔离修饰符* → **nonisolated**
>
## 属性 {#attributes}

View File

@ -1,11 +1,60 @@
# Swift 文档修订历史
### 2020-02-05
### 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

View File

@ -1,11 +1,47 @@
# Swift 文档修订历史
### 2020-02-05
### 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

View File

@ -23,6 +23,7 @@
* [析构过程](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)

View File

@ -24,7 +24,8 @@ Swift 官方文档中文翻译由 [numbbbbb](https://github.com/numbbbbb) 发起
- [SunsetWan](https://github.com/SunsetWan)
- [WAMaker](https://github.com/WAMaker)
- [YiYiZheng](https://github.com/YiYiZheng)
- [Yousanflics](https://github.com/Yousanflics)
- [Yousanflics](https://github.com/Yousanflics)
- [CyberHex](https://cyberhex.me/)
## Swift 4.x 主要贡献者