diff --git a/document/TheSwiftProgrammingLanguageSwift57.epub b/document/TheSwiftProgrammingLanguageSwift57.epub new file mode 100644 index 00000000..d9b76bd0 Binary files /dev/null and b/document/TheSwiftProgrammingLanguageSwift57.epub differ diff --git a/source/01_welcome_to_swift/02_version_compatibility.md b/source/01_welcome_to_swift/02_version_compatibility.md index f0128808..4e5fb756 100755 --- a/source/01_welcome_to_swift/02_version_compatibility.md +++ b/source/01_welcome_to_swift/02_version_compatibility.md @@ -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。 diff --git a/source/01_welcome_to_swift/03_a_swift_tour.md b/source/01_welcome_to_swift/03_a_swift_tour.md index 8ecf1268..422e52c2 100755 --- a/source/01_welcome_to_swift/03_a_swift_tour.md +++ b/source/01_welcome_to_swift/03_a_swift_tour.md @@ -154,6 +154,14 @@ let fullName: String = "John Appleseed" let informalGreeting = "Hi \(nickName ?? fullName)" ``` +你还可以使用较短的代码解包一个值,并且对该被包装值使用相同的名称。 + +```swift +if let nickname { + print("Hey, \(nickName)") +} +``` + `switch` 支持任意类型的数据以及各种比较操作——不仅仅是整数以及测试相等。 ```swift @@ -613,6 +621,51 @@ 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` 来声明一个协议。 diff --git a/source/02_language_guide/01_The_Basics.md b/source/02_language_guide/01_The_Basics.md index 203f7f81..f02ef6e1 100755 --- a/source/02_language_guide/01_The_Basics.md +++ b/source/02_language_guide/01_The_Basics.md @@ -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 \(muNumber)") +} +// 输出 "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 { diff --git a/source/02_language_guide/02_Basic_Operators.md b/source/02_language_guide/02_Basic_Operators.md index de370cb6..ae81cbd3 100755 --- a/source/02_language_guide/02_Basic_Operators.md +++ b/source/02_language_guide/02_Basic_Operators.md @@ -16,7 +16,7 @@ Swift 还提供了 C 语言没有的区间运算符,例如 `a.. 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` 能提升程序整体的可读性。 \ No newline at end of file diff --git a/source/02_language_guide/06_Functions.md b/source/02_language_guide/06_Functions.md index f31d5234..ba23878b 100755 --- a/source/02_language_guide/06_Functions.md +++ b/source/02_language_guide/06_Functions.md @@ -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} diff --git a/source/02_language_guide/07_Closures.md b/source/02_language_guide/07_Closures.md index 5cc2c54b..5afabdea 100755 --- a/source/02_language_guide/07_Closures.md +++ b/source/02_language_guide/07_Closures.md @@ -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} 闭包可以在其被定义的上下文中*捕获*常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。 diff --git a/source/02_language_guide/10_Properties.md b/source/02_language_guide/10_Properties.md index 89c09628..9dc618da 100755 --- a/source/02_language_guide/10_Properties.md +++ b/source/02_language_guide/10_Properties.md @@ -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)” @@ -302,7 +303,7 @@ struct TwelveOrLess { } ``` -这个 setter 确保新值小于 12,而且返回被存储的值。 +这个 setter 确保新值小于或等于 12,而且返回被存储的值。 > 注意 > > 上面例子以 `private` 的方式声明 `number` 变量,这使得 `number` 仅在 `TwelveOrLess` 的实现中使用。写在其他地方的代码通过使用 `wrappedValue` 的 getter 和 setter 来获取这个值,但不能直接使用 `number`。有关 `private` 的更多信息,请参考 [访问控制](./26_Access_Control.md) diff --git a/source/02_language_guide/14_Initialization.md b/source/02_language_guide/14_Initialization.md index f744bda5..c944e552 100755 --- a/source/02_language_guide/14_Initialization.md +++ b/source/02_language_guide/14_Initialization.md @@ -535,7 +535,7 @@ print("Bicycle: \(bicycle.description)") // 打印“Bicycle: 2 wheel(s)” ``` -如果子类的构造器没有在阶段 2 过程中做自定义操作,并且父类有一个无参数的指定构造器,你可以在所有子类的存储属性赋值之后省略 `super.init()` 的调用。 +如果子类的构造器没有在阶段 2 过程中做自定义操作,并且父类有一个同步、无参数的指定构造器,你可以在所有子类的存储属性赋值之后省略 `super.init()` 的调用。若父类有一个异步的构造器,你就需要明确地写入 `await super.init()`。 这个例子定义了另一个 `Vehicle` 的子类 `Hoverboard` ,只设置它的 `color` 属性。这个构造器依赖隐式调用父类的构造器来完成,而不是显示调用 `super.init()`。 diff --git a/source/02_language_guide/27_Advanced_Operators.md b/source/02_language_guide/27_Advanced_Operators.md index 7501dae2..3926620a 100644 --- a/source/02_language_guide/27_Advanced_Operators.md +++ b/source/02_language_guide/27_Advanced_Operators.md @@ -276,7 +276,7 @@ signedOverflow = signedOverflow &- 1 类和结构体可以为现有的运算符提供自定义的实现。这通常被称为运算符*重载*。 -下面的例子展示了如何让自定义的结构体支持加法运算符(`+`)。算术加法运算符是一个*二元运算符*,因为它是对两个值进行运算,同时它还可以称为*中缀*运算符,因为它出现在两个值中间。 +下面的例子展示了如何让自定义的结构体支持加法运算符(`+`)。算术加法运算符是一个二元运算符,因为它是对两个值进行运算,同时它还可以称为中缀运算符,因为它出现在两个值中间。 例子中定义了一个名为 `Vector2D` 的结构体用来表示二维坐标向量 `(x, y)`,紧接着定义了一个可以将两个 `Vector2D` 结构体实例进行相加的*运算符函数*: diff --git a/source/02_language_guide/28_Concurrency.md b/source/02_language_guide/28_Concurrency.md index 0a126310..e30a0f7f 100644 --- a/source/02_language_guide/28_Concurrency.md +++ b/source/02_language_guide/28_Concurrency.md @@ -7,7 +7,7 @@ Swift 对于结构化的编写异步和并行代码有着原生的支持。异 > 注意 > -> 如果你曾经写过并发的代码的话,那可能使用过线程。Swift 中的并发模型是基于线程的,但你不会直接和线程打交道。在 Swift 中,一个异步函数可以交出它在某个线程上的运行权,这样另一个异步函数在这个函数被阻塞时就能获得此线程的运行权。 +> 如果你曾经写过并发的代码的话,那可能使用过线程。Swift 中的并发模型是基于线程的,但你不会直接和线程打交道。在 Swift 中,一个异步函数可以交出它在某个线程上的运行权,这样另一个异步函数在这个函数被阻塞时就能获得此线程的运行权。但是,Swift并不能确定当异步函数恢复运行时其将在哪条线程上运行。 你当然也可以不用 Swift 原生支持去写并发的代码,只不过代码的可读性会下降。比如,下面的这段代码会拉取一系列图片名称的列表,下载列表中的图片然后展示给用户: @@ -64,19 +64,42 @@ show(photo) * 异步函数,方法或变量内部的代码 * 静态函数 `main()` 中被打上 `@main` 标记的结构体、类或者枚举中的代码 -* 游离的子任务中的代码,之后会在[非结构化并行](#Unstructured-Concurrency)中说明 +* 非结构化的子任务中的代码,之后会在 [非结构化并行](#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()` 方法模拟网络请求实现 `listPhotos(inGallery:)` 的一个版本: +> 学习并行的过程中,[Task.sleep(_:)](https://developer.apple.com/documentation/swift/task/3814836-sleep) 方法非常有用。这个方法什么都没有做,只是等待不少于指定的时间(单位纳秒)后返回。下面是使用 `sleep(until:clock:)` 方法模拟网络请求实现 `listPhotos(inGallery:)` 的一个版本: > -```Swift -func listPhotos(inGallery name: String) async -> [String] { - await Task.sleep(2 * 1_000_000_000) // 两秒 - return ["IMG001", "IMG99", "IMG0404"] +```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} @@ -142,7 +165,7 @@ show(photos) await withTaskGroup(of: Data.self) { taskGroup in let photoNames = await listPhotos(inGallery: "Summer Vacation") for name in photoNames { - taskGroup.async { await downloadPhoto(named: name) } + taskGroup.addTask { await downloadPhoto(named: name) } } } ``` @@ -151,15 +174,16 @@ await withTaskGroup(of: Data.self) { taskGroup in ### 非结构化并发 {#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 还支持非结构化并发。与任务组中的任务不同的是,*非结构化任务(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 +```swift let newPhoto = // ... 图片数据 ... let handle = Task { - return await add(newPhoto, toGalleryNamed: "Spring Adventures") + return await add(newPhoto, toGalleryNamed: "Spring Adventures") } let result = await handle.value ``` + 如果你想更多的了解游离任务,可以参考 [Task](https://developer.apple.com/documentation/swift/task)。 ### 任务取消 {#Task-Cancellation} @@ -176,6 +200,8 @@ Swift 中的并发使用合作取消模型。每个任务都会在执行中合 ## Actors {#Actors} +你可以使用任务来将自己的程序分割为孤立、并发的部分。任务间相互孤立,这也使得它们能够安全地同时运行。但有时你需要在任务间共享信息。Actors便能够帮助你安全地在并发代码间分享信息。 + 跟类一样,actor 也是一个引用类型,所以 [类是引用类型](https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html#ID89) 中关于值类型和引用类型的比较同样适用于 actor 和类。不同于类的是,actor 在同一时间只允许一个任务访问它的可变状态,这使得多个任务中的代码与一个 actor 交互时更加安全。比如,下面是一个记录温度的 actor: ```Swift @@ -231,3 +257,42 @@ 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 +} +``` \ No newline at end of file diff --git a/source/03_language_reference/02_Lexical_Structure.md b/source/03_language_reference/02_Lexical_Structure.md index 8794bcc3..5f5144b7 100755 --- a/source/03_language_reference/02_Lexical_Structure.md +++ b/source/03_language_reference/02_Lexical_Structure.md @@ -1,647 +1,714 @@ -# 词法结构(Lexical Structure) - -Swift 的*“词法结构(lexical structure)”* 描述了能构成该语言中有效符号(token)的字符序列。这些合法符号组成了语言中最底层的构建基块,并在之后的章节中用于描述语言的其他部分。一个合法符号由一个标识符(identifier)、关键字(keyword)、标点符号(punctuation)、字面量(literal)或运算符(operator)组成。 - -通常情况下,符号是考虑了输入文本中最长可能的子字符串,并被随后将介绍的语法约束,根据 Swift 源文件的字符生成的。这种方法称为*“最长匹配(longest match)”*,或者*“最大适合(maximal munch)”*。 - -## 空白与注释 {#whitespace} - -空白(whitespace)有两个用途:分隔源文件中的符号和区分前缀、后缀和二元运算符(参见 [运算符](#operators)),在其他情况下空白则会被忽略。以下的字符会被当作空白:空格(U+0020)、换行符(U+000A)、回车符(U+000D)、水平制表符(U+0009)、垂直制表符(U+000B)、换页符(U+000C)以及空字符(U+0000)。 - -注释被编译器当作空白处理。单行注释由 `//` 开始直至遇到换行符(U+000A)或者回车符(U+000D)。多行注释由 `/*` 开始,以 `*/` 结束。多行注释允许嵌套,但注释标记必须成对出现。 - -注释可以包含其他的格式和标记,如 [标记格式参考](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/index.html)中所述。 - -> 空白语法 -> -> *空白* → [空白项](#whitespace-item) [空白](#whitespace)可选 -#### whitespace-item {#whitespace-item} -> -> *空白项* → [断行符](#line-break) -> -> *空白项* → [注释](#comment) -> -> *空白项* → [多行注释](#multiline-comment) -> -> *空白项* → U+0000,U+0009,U+000B,U+000C 或者 U+0020 -> -> -#### line-break {#line-break} -> -> *断行符* → U+000A -> -> *断行符* → U+000D -> -> *断行符* → U+000D 接着是 U+000A -> -> -#### comment {#comment} -> -> *注释* → // [注释内容](#comment-text) [断行符](#line-break) -> -> -#### multiline-comment {#multiline-comment} -> -> *多行注释* → `/*` [多行注释内容](#multiline-commnet-text) `*/` -> -> -#### comment-text {#comment-text} -> -> *注释内容* → [注释内容项](#comment-text-item) [注释内容](#comment-text)可选 -> -> -#### comment-text-item {#comment-text-item} -> -> *注释内容项* → 任何 Unicode 标量值,除了 U+000A 或者 U+000D -> -> -#### multiline-commnet-text {#multiline-commnet-text} -> -> *多行注释内容* → [多行注释内容项](#multiline-comment-text-item) [多行注释内容](#multiline-comment-text)可选 -> -> *多行注释内容项* → [多行注释](#multiline-comment). -> -> *多行注释内容项* → [注释内容项](#comment-text-item) -> -> *多行注释内容项* → 任何 Unicode 标量值,除了 `/*` 或者 `*/` - -## 标识符 {#identifiers} - -*标识符(identifier)* 可以由以下的字符开始:大写或小写的字母 `A` 到 `Z`、下划线(`_`)、基本多文种平面(Basic Multilingual Plane)中非字符数字组合的 Unicode 字符以及基本多文种平面以外的非个人专用区字符。在首字符之后,允许使用数字和组合 Unicode 字符。 - -以下划线开头的标识视为内部标识符,即使声明了 `public` 访问级别。这个约定允许框架作者标记一部分不能被使用方调用或依赖的 API,即便因为某些限制原因导致它的声明必须是公开的。另外,双下划线开头的标识符是为 Swift 编译器和标准库预留的。 - -使用保留字作为标识符,需要在其前后增加反引号(`` ` ``)。例如,`class` 不是合法的标识符,但可以使用 `` `class` ``。反引号不属于标识符的一部分,`` `x` `` 和 `x` 表示同一标识符。 - -闭包中如果没有明确指定参数名称,参数将被隐式命名为 `$0`、`$1`、`$2` 等等。这些命名在闭包作用域范围内是合法的标识符。 - -编译器给含有属性包装器呈现值的属性自动合成以美元符号(*$*)开头的标识符。你的代码可以与这些标识符进行交互,,但是不能使用该前缀声明标识符。更详细的介绍,请查看 [特性](./07_Attributes.md) 章节中的 [属性包装器](./07_Attributes.md#propertywrapper) 部分。 - -> 标识符语法 -> -> #### identifier {#identifier} -> -> *标识符* → [头部标识符](#identifier-head) [标识符字符组](#identifier-characters)可选 -> -> *标识符* → \`[头部标识符](#identifier-head) [标识符字符组](#identifier-characters)可选\` -> -> *标识符* → [隐式参数名](#implicit-parameter-name) -> -> #### identifier-list {#identifier-list} -> -> *标识符列表* → [标识符](#identifier) | [标识符](#identifier) **,** [标识符列表](#identifier) -> -> -#### identifier-head {#identifier-head} -> -> *头部标识符* → 大写或小写字母 A - Z -> -> *头部标识符* → **_** -> -> *头部标识符* → U+00A8,U+00AA,U+00AD,U+00AF,U+00B2–U+00B5,或者 U+00B7–U+00BA -> -> *头部标识符* → U+00BC–U+00BE,U+00C0–U+00D6,U+00D8–U+00F6,或者 U+00F8–U+00FF -> -> *头部标识符* → U+0100–U+02FF,U+0370–U+167F,U+1681–U+180D,或者 U+180F–U+1DBF -> -> *头部标识符* → U+1E00–U+1FFF -> -> *头部标识符* → U+200B–U+200D,U+202A–U+202E,U+203F–U+2040,U+2054,或者 U+2060–U+206F -> -> *头部标识符* → U+2070–U+20CF,U+2100–U+218F,U+2460–U+24FF,或者 U+2776–U+2793 -> -> *头部标识符* → U+2C00–U+2DFF 或者 U+2E80–U+2FFF -> -> *头部标识符* → U+3004–U+3007,U+3021–U+302F,U+3031–U+303F,或者 U+3040–U+D7FF -> -> *头部标识符* → U+F900–U+FD3D,U+FD40–U+FDCF,U+FDF0–U+FE1F,或者 U+FE30–U+FE44 -> -> *头部标识符* → U+FE47–U+FFFD -> -> *头部标识符* → U+10000–U+1FFFD,U+20000–U+2FFFD,U+30000–U+3FFFD,或者 U+40000–U+4FFFD -> -> *头部标识符* → U+50000–U+5FFFD,U+60000–U+6FFFD,U+70000–U+7FFFD,或者 U+80000–U+8FFFD -> -> *头部标识符* → U+90000–U+9FFFD,U+A0000–U+AFFFD,U+B0000–U+BFFFD,或者 U+C0000–U+CFFFD -> -> *头部标识符* → U+D0000–U+DFFFD 或者 U+E0000–U+EFFFD -#### identifier-character {#identifier-character} -> -> *标识符字符* → 数值 0 - 9 -> -> *标识符字符* → U+0300–U+036F,U+1DC0–U+1DFF,U+20D0–U+20FF,或者 U+FE20–U+FE2F -> -> *标识符字符* → [头部标识符](#identifier-head) -> -> -#### identifier-characters {#identifier-characters} -> -> *标识符字符组* → [标识符字符](#identifier-character) [标识符字符组](#identifier-characters)可选 -> -> -#### implicit-parameter-name {#implicit-parameter-name} -> -> *隐式参数名* → **$** [十进制数字列表](#decimal-digit) -#### property-wrapper-projection {#property-wrapper-projection} -> -> *属性包装器呈现值* → **$** [标识符字符组](#identifier-characters) - -## 关键字和标点符号 {#keywords-and-punctuation} - -下面这些被保留的关键字不允许用作标识符,除非使用反引号转义,具体描述请参考 [标识符](#identifiers)。除了 `inout`、`var` 以及 `let` 之外的关键字可以用作某个函数声明或者函数调用当中的外部参数名,无需添加反引号转义。当一个成员与一个关键字具有相同的名称时,不需要使用反引号来转义对该成员的引用,除非在引用该成员和使用该关键字之间存在歧义 - 例如,`self`,`Type` 和 `Protocol` 在显式的成员表达式中具有特殊的含义,因此它们必须在该上下文中使用反引号进行转义。 - -* 用在声明中的关键字:`associatedtype`、`class`、`deinit`、`enum`、`extension`、`fileprivate `、`func`、`import`、`init`、`inout`、`internal`、`let`、`open`、`operator`、`private`、`precedencegroup`、`protocol`、`public`、`rethrows`、`static`、`struct`、`subscript`、`typealias` 以及 `var`。 -* 用在语句中的关键字:`break`、`case`、`continue`、`default`、`defer`、`do`、`else`、`fallthrough`、`for`、`guard`、`if`、`in`、`repeat`、`return`、`throw`、`switch`、`where` 以及 `while`。 -* 用在表达式和类型中的关键字:`Any`、`as`、`catch`、`false`、`is`、`nil`、`rethrows`、`self`、`Self`、`super`、`throw`、`throws`、`true` 以及 `try `。 -* 用在模式中的关键字:`_`。 -* 以井字号(`#`)开头的关键字:`#available`、`#colorLiteral`、`#column`、`#dsohandle`、`#elseif`、`#else`、`#endif`、`#error`、`#fileID`、`#fileLiteral`、`#filePath`、`#file`、`#function`、`#if`、`#imageLiteral`、`#keyPath`、`#line`、`#selector`、`#sourceLocation` 以及 `#warning`。 -* 特定上下文中被保留的关键字:`associativity`、`convenience`、`didSet`、`dynamic`、`final`、`get`、`indirect`、`infix`、`lazy`、`left`、`mutating`、`none`、`nonmutating`、`optional`、`override`、`postfix`、`precedence`、`prefix`、`Protocol`、`required`、`right`、`set`、`some`、`Type`、`unowned`、`weak` 以及 `willSet`。这些关键字在特定上下文之外可以被用做标识符。 - -以下符号被保留为标点符号,不能用于自定义运算符:`(`、`)`、`{`、`}`、`[`、`]`、`.`、`,`、`:`、`;`、`=`、`@`、`#`、`&`(作为前缀运算符)、`->`、`` ` ``、`?`、以及 `!`(作为后缀运算符)。 - -## 字面量 {#literal} - -*字面量(literal)* 用来表示源码中某种特定类型的值,比如一个数字或字符串。 - -下面是字面量的一些示例: - -```swift -42 // 整数字面量 -3.14159 // 浮点数字面量 -"Hello, world!" // 字符串字面量 -true // 布尔值字面量 -``` - -字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中,Swift 使用了显式类型注解(`: Int8`)来推导出 `42` 这个整数字面量的类型是 `Int8`。如果没有可用的类型信息,Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整数字面量的默认类型是 `Int`,浮点数字面量的默认类型是 `Double`,字符串字面量的默认类型是 `String`,布尔值字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"` 的默认推导类型就是 `String`。 - -当为一个字面量值指定了类型注解的时候,这个注解类型必须能通过这个字面量值实例化。也就是说,这个类型必须符合这些 Swift 标准库协议中的一个:整数字面量的 `ExpressibleByIntegerLiteral` 协议、浮点数字面量的 `ExpressibleByFloatLiteral` 协议、字符串字面量的 `ExpressibleByStringLiteral` 协议、布尔值字面量的 `ExpressibleByBooleanLiteral` 协议、只包含单个 Unicode 标量字符串文本的 `ExpressibleByUnicodeScalarLiteral` 协议以及只包含单个扩展字形簇(grapheme cluster)字符串文字的 `ExpressibleByExtendedGraphemeClusterLiteral` 协议。比如,`Int8` 符合 `ExpressibleByIntegerLiteral` 协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整数字面量 `42` 的类型注解。 - - -> 字面量语法 -> -> *字面量* → [数值字面量](#integer-literal) | [字符串字面量](#string-literal) | [布尔值字面量](#integer-literal) | [nil 字面量](#integer-literal) -> -> *数值字面量* → **-**可选 [整数字面量](#integer-literal) | **-**可选 [浮点数字面量](#floating-point-literal) -> -> *布尔值字面量* → **true** | **false** -> -> *nil 字面量* → **nil** - - -### 整数字面量{#integer-literal} - -*整数字面量(Integer Literals)* 表示未指定精度的整数值。整数字面量默认用十进制表示;可以加前缀来指定其他的进制。二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。 - -十进制字面量包含数字 `0` 至 `9`。二进制字面量包含 `0` 和 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F`(大小写均可)。 - -负整数字面量的表示方式为在整数字面量前加负号 `-`,比如 `-42`。 - -整型字面面可以使用下划线(`_`)来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,这同样也会被系统所忽略,并不会影响字面量的值。 - -除非特别指定,整数字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数](../02_language_guide/01_The_Basics.md#integers)。 - -> 整数字面量语法 -> -> -#### integer-literal {#integer-literal} -> -> *整数字面量* → [二进制字面量](#binary-literal) -> -> *整数字面量* → [八进制字面量](#octal-literal) -> -> *整数字面量* → [十进制字面量](#decimal-literal) -> -> *整数字面量* → [十六进制字面量](#hexadecimal-literal) -> -> -#### binary-literal {#binary-literal} -> -> *二进制字面量* → **0b** [二进制数字](#binary-digit) [二进制字面量字符组](#binary-literal-characters)可选 -> -> -#### binary-digit {#binary-digit} -> -> *二进制数字* → 数值 0 或 1 -> -> *二进制字面量字符* → [二进制数字](#binary-digit) | **_** -> -> -#### binary-literal-characters {#binary-literal-characters} -> -> *二进制字面量字符组* → [二进制字面量字符](#binary-literal-character) [二进制字面量字符组](#binary-literal-characters)可选 -> -> -#### octal-literal {#octal-literal} -> -> *八进制字面量* → **0o** [八进字数字](#octal-digit) [八进制字符组](#octal-literal-characters)可选 -> -> -#### octal-digit {#octal-digit} -> -> *八进字数字* → 数值 0 到 7 -> -> *八进制字符* → [八进字数字](#octal-digit) | **_** -> -> -#### octal-literal-characters {#octal-literal-characters} -> -> *八进制字符组* → [八进制字符](#octal-literal-character) [八进制字符组](#octal-literal-characters)可选 -> -> -#### decimal-literal {#decimal-literal} -> -> *十进制字面量* → [十进制数字](#decimal-digit) [十进制字符组](#decimal-literal-characters)可选 -> -> -#### decimal-digit {#decimal-digit} -> -> *十进制数字* → 数值 0 到 9 -> -> -#### decimal-literal-characters {#decimal-literal-characters} -> -> *十进制数字组* → [十进制数字](#decimal-digit) [十进制数字组](#decimal-literal-characters)可选 -> -> *十进制字符* → [十进制数字](#decimal-digit) | **_** -> -> *十进制字符组* → [十进制字符](#decimal-literal-characters) [十进制字符组](#decimal-literal-characters)可选 -> -> -#### hexadecimal-literal {#hexadecimal-literal} -> -> *十六进制字面量* → **0x** [十六进制数字](#hexadecimal-digit) [十六进制字面量字符组](#hexadecimal-literal-characters)可选 -> -> -#### hexadecimal-digit {#hexadecimal-digit} -> -> *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F -> -> *十六进制字符* → [十六进制数字](#hexadecimal-digit) | **-** -> -> -#### hexadecimal-literal-characters {#hexadecimal-literal-characters} -> -> *十六进制字面量字符组* → [十六进制字符](#hexadecimal-literal-characters) [十六进制字面量字符组](#hexadecimal-literal-characters)可选 - -### 浮点数字面量{#floating-point-literal} - -*浮点数字面量(Floating-point literals)*表示未指定精度浮点数的值。 - -浮点数字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。 - -十进制浮点数字面量由十进制数字串后跟十进制小数部分或十进制指数部分(或两者皆有)组成。十进制小数部分由小数点(`.`)后跟十进制数字串组成。指数部分由大写或小写字母 `e` 为前缀后跟十进制数字串组成,这串数字表示 `e` 之前的数值乘以 10 的几次方。例如:`1.25e2` 表示 1.25 x 10²,也就是 `125.0`。同样,`1.25e-2` 表示 1.25 x 10¯²,也就是 `0.0125`。 - -十六进制浮点数字面量由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 为前缀后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 15 x 2²,也就是 `60`。同样,`0xFp-2` 表示 15 x 2¯²,也就是 `3.75`。 - -负数的浮点数字面量由负号(`-`)和浮点数字面量组成,例如 `-42.5`。 - -浮点数字面量允许使用下划线(`_`)来增强数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。 - -除非特别指定,浮点数字面量的默认推导类型为 Swift 标准库类型中的 `Double`,表示 64 位浮点数。Swift 标准库也定义了 `Float` 类型,表示 32 位浮点数。 - -> 浮点数字面量语法 -> -> -#### floating-point-literal {#floating-point-literal} -> -> *浮点数字面量* → [十进制字面量](#decimal-literal) [十进制分数](#decimal-fraction)可选 [十进制指数](#decimal-exponent)可选 -> -> *浮点数字面量* → [十六进制字面量](#hexadecimal-literal) [十六进制分数](#hexadecimal-fraction)可选 [十六进制指数](#hexadecimal-exponent) -> -> -#### decimal-fraction {#decimal-fraction} -> -> *十进制分数* → **.** [十进制字面量](#decimal-literal) -> -> -#### decimal-exponent {#decimal-exponent} -> -> *十进制指数* → [十进制指数 e](#floating-point-e) [正负号](#sign)可选 [十进制字面量](#decimal-literal) -> -> -#### hexadecimal-fraction {#hexadecimal-fraction} -> -> *十六进制分数* → **.** [十六进制数字](#hexadecimal-digit) [十六进制字面量字符组](#hexadecimal-literal-characters)可选 -> -> -#### hexadecimal-exponent {#hexadecimal-exponent} -> -> *十六进制指数* → [十六进制指数 p](#floating-point-p) [正负号](#sign)可选 [十进制字面量](#decimal-literal) -> -> -#### floating-point-e {#floating-point-e} -> -> *十进制指数 e* → **e** | **E** -> -> -#### floating-point-p {#floating-point-p} -> -> *十六进制指数 p* → **p** | **P** -> -> -#### sign {#sign} -> -> *正负号* → **+** | **-** - -### 字符串字面量 {#string-literal} - -字符串字面量是被引号包括的一串字符组成。单行字符串字面量是被包在双引号中的一串字符组成,形式如下: - -> "`字符`" - -字符串字面量中不能包含未转义的双引号(`"`)、未转义的反斜线(`\`)、回车符、换行符。 - -多行字符串字面量被包在三个双引号中的一串字符组成,形式如下: -> """ -> `字符` -> """ - -与单行字符串字面量不同的是,多行字符串字面量可以包含不转义的双引号("),回车以及换行。它不能包含三个未转义的连续双引号。 - -`"""` 之后的回车或者换行开始多行字符串字面量,它们不是字符串的一部分。结束部分的 `"""` 以及它之前的回车或者换行,也不是字符串的一部分。要让多行字符串字面量的开始或结束带有换行,就在第一行或者最后一行写一个空行。 - -多行字符串字面量可以使用任何空格或制表符组合进行缩进;这些缩进不会包含在字符串中。`"""` 的结束符号决定了缩进:字面量中的每一个非空行开头都必须与结束符 `"""` 之前出现的缩进完全一致;空格和制表符不会被转换。你可以在缩进后包含额外的空格和制表符;这些空格和制表符会在字符串中出现。 - -多行字符串字面量中的一行结束使用规范化的换行符号。尽管你的源代码混用了回车和换行符,字符串中所有的行结束都必须一样. - -在多行字符串字面量里,在行末用反斜线(`\`)可以省略字符串行间中断。反斜线和换行符之间的空白也将被忽略。你可以在你的代码里用这种语法硬包裹多行字符串字面量,而不改变结果字符串的值。 - -可以在字符串字面量中使用的转义特殊符号如下: - -* 空字符 (`\0`) -* 反斜线 (`\\`) -* 水平制表符 (`\t`) -* 换行符 (`\n`) -* 回车符 (`\r`) -* 双引号 (`\"`) -* 单引号 (`\'`) -* Unicode 标量 (`\u{`n`}`),n 为一到八位的十六进制数字 - -字符串字面量允许在反斜杠(`\`)后的括号 `()` 中插入表达式的值。插入表达式可以包含字符串字面量,但不能包含未转义的反斜线(`\`)、回车符以及换行符。 - -例如,以下所有字符串字面量的值都是相同的: - -```swift -"1 2 3" -"1 2 \("3")" -"1 2 \(3)" -"1 2 \(1 + 2)" -let x = 3; "1 2 \(x)" -``` - -由扩展分隔符包裹的字符串,它是由引号以及成对出现的数字符号(`#`)包裹的字符串序列。由扩展分隔符包裹的字符串形式如下所示: - -> \#"`characters`"# -> -> \#""" -> -> `characters` -> -> """# - -特殊字符在被扩展分隔符分隔的结果字符串中会展示为普通字符,而不是特殊字符。你可以使用扩展分隔符来创建一些通常情况下具有特殊效果的字符串。例如,生成字符串插值,启动转义序列或终止字符串。 - -以下所示,由字符串字面量和扩展分隔符所创建的字符串是等价的: - -```swift -let string = #"\(x) \ " \u{2603}"# -let escaped = "\\(x) \\ \" \\u{2603}" -print(string) -// Prints "\(x) \ " \u{2603}" -print(string == escaped) -// Prints "true" - -``` - -如果在一个字符串中使用多对扩展分隔符,请不要在分隔符之间使用空格。 - -```swift -print(###"Line 1\###nLine 2"###) // OK -print(# # #"Line 1\# # #nLine 2"# # #) // Error - -``` - -使用扩展分隔符创建的多行字符串字面量与普通多行字符串字面量具有相同的缩进要求。 - -字符串字面量的默认推导类型为 `String`。更多有关 `String` 类型的信息请参考 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 以及 [*字符串结构参考*](https://developer.apple.com/documentation/swift/string)。 - -用 `+` 操作符连接的字符型字面量是在编译时进行连接的。比如下面的 `textA` 和 `textB` 是完全一样的,`textA` 没有任何运行时的连接操作。 - -```swift -let textA = "Hello " + "world" -let textB = "Hello world" -``` - -> 字符串字面量语法 -> -> *字符串字面量* → [静态字符串字面量](#static-string-literal) | [插值字符串字面量](#interpolated-string-literal) -> -> *字符串开分隔定界符* → [字符串扩展分隔符](#extended-string-literal-delimiter) **"** -> -> *字符串闭分隔定界符* → **"** [字符串扩展分隔符](#extended-string-literal-delimiter)可选 -> -> -#### static-string-literal {#static-string-literal} -> -> *静态字符串字面量* → [字符串开分隔定界符](#extended-string-literal-delimiter) [引用文本](#quoted-text)可选 [字符串闭分隔定界符](#extended-string-literal-delimiter) -> -> *静态字符串字面量* → [多行字符串开分隔定界符](#extended-string-literal-delimiter) [多行引用文本](#multiline-quoted-text)可选 [多行字符串闭分隔定界符](#extended-string-literal-delimiter) -> -> *多行字符串开分隔定界符* → [字符串扩展分隔符](#extended-string-literal-delimiter) **"""** -> -> *多行字符串闭分隔定界符* → **"""** [字符串扩展分隔符](#extended-string-literal-delimiter) -> -> -#### extended-string-literal-delimiter {#extended-string-literal-delimiter} -> -> *字符串扩展分隔符* → **#** [字符串扩展分隔符](#extended-string-literal-delimiter)可选 -> -> -#### quoted-text {#quoted-text} -> -> *引用文本* → [引用文本项](#quoted-text-item) [引用文本](#quoted-text)可选 -> -> -#### quoted-text-item {#quoted-text-item} -> -> *引用文本项* → [转义字符](#escaped-character) -> -> *引用文本项* → 除了 **"**、**\\**、U+000A、U+000D 以外的所有 Unicode 字符 -> -> -#### multiline-quoted-text {#multiline-quoted-text} -> -> *多行引用文本* → [多行引用文本项](#multiline-quoted-text-item) [多行引用文本](#multiline-quoted-text)可选 -> -> -#### multiline-quoted-text-item {#multiline-quoted-text-item} -> -> *多行引用文本项* [转义字符](#escaped-character)可选 -> -> -#### multiline-quoted-text {#multiline-quoted-text} -> -> *多行引用文本* → 除了 **\\** 以外的任何 Unicode 标量值 -> -> *多行引用文本* → [转义换行](#escaped-newline) -> -> -#### interpolated-string-literal {#interpolated-string-literal} -> -> *插值字符串字面量* → [字符串开分隔定界符](#extended-string-literal-delimiter) [插值文本](#interpolated-text)可选 [字符串闭分隔定界符](#extended-string-literal-delimiter) -> -> *插值字符串字面量* → [多行字符串开分隔定界符](#extended-string-literal-delimiter) [插值文本](#interpolated-text)可选 [多行字符串闭分隔定界符](#extended-string-literal-delimiter) -> -> -#### interpolated-text {#interpolated-text} -> -> *插值文本* → [插值文本项](#interpolated-text-item) [插值文本](#interpolated-text)可选 -> -> -#### interpolated-text-item {#interpolated-text-item} -> -> *插值文本项* → **\\(**[ 表达式 ](./04_Expressions.md)**)** | [引用文本项](#quoted-text-item) -> -> *多行插值文本* → [多行插值文本项](#multiline-quoted-text-item) [多行插值文本](#multiline-quoted-text)可选 -> -> *多行插值文本项* → **\\(** [表达式](./04_Expressions.md) **)** | [多行引用文本项](#multiline-quoted-text-item) -> -> -#### escape-sequence {#escape-sequence} -> -> *转义序列* → **\\** [字符串扩展分隔符](#extended-string-literal-delimiter) -> -> -#### escaped-character {#escaped-character} -> -> *转义字符* → [转义序列](#escape-sequence) **0** | [转义序列](#escape-sequence) **\\** | [转义序列](#escape-sequence) **t** | [转义序列](#escape-sequence) **n** | [转义序列](#escape-sequence) **r** | [转义序列](#escape-sequence) **\"** | [转义序列](#escape-sequence) **'** -> -> *转义字符* → [转义序列](#escape-sequence) **u {** [unicode 标量数字](#unicode-scalar-digits) **}** -> -> -#### unicode-scalar-digits {#unicode-scalar-digits} -> -> *unicode 标量数字* → 一到八位的十六进制数字 -> -> -#### escaped-newline {#escaped-newline} -> -> *转义换行符* → [转义序列](#escape-sequence) [空白](#whitespace)可选 [断行符](#line-break) - - -## 运算符 {#operator} - -Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](../02_language_guide/02_Basic_Operators.md) 和 [高级运算符](../02_language_guide/27_Advanced_Operators.md) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。 - -自定义运算符可以由以下其中之一的 ASCII 字符 `/`、`=`、`-`、`+`、`!`、`*`、`%`、`<`、`>`、`&`、`|`、`^`、`?` 以及 `~`,或者后面语法中规定的任一个 Unicode 字符(其中包含了*数学运算符*、*零散符号(Miscellaneous Symbols)* 以及*印刷符号(Dingbats)*之类的 Unicode 块)开始。在第一个字符之后,允许使用组合型 Unicode 字符。 - -您也可以以点号(`.`)开头来定义自定义运算符。这些运算符可以包含额外的点。例如 `.+.` 会被看作一个单独的运算符。如果某个运算符不是以点号开头的,那么它就无法再包含另外的点号了。例如,`+.+` 就会被看作为一个 `+` 运算符后面跟着一个 `.+` 运算符。 - -虽然您可以用问号 `(?)` 来自定义运算符,但是这个运算符不能只包含单独的一个问号。此外,虽然运算符可以包含一个惊叹号 `(!)`,但是前缀运算符不能够以问号或者惊叹号开头。 - -> 注意 -> -> 以下这些标记 `=`、`->`、`//`、`/*`、`*/`、`.`,前缀运算符 `<`、`&` 和 `?`,中缀运算符 `?`,后缀运算符 `>`、`!` 和 `?` 是被系统保留的。这些符号不能被重载,也不能用作自定义运算符。 - -运算符两侧的空白被用来区分该运算符是否为前缀运算符、后缀运算符或二元运算符。规则总结如下: - -* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+++b` 和 `a +++ b` 当中的 `+++` 运算符会被看作二元运算符。 -* 如果运算符只有左侧空白,将被看作一元前缀运算符。例如 `a +++b` 中的 `+++` 运算符会被看做是一元前缀运算符。 -* 如果运算符只有右侧空白,将被看作一元后缀运算符。例如 `a+++ b` 中的 `+++` 运算符会被看作是一元后缀运算符。 -* 如果运算符左侧没有空白并紧跟 `(.)`,将被看作一元后缀运算符。例如 `a+++.b` 中的 `+++` 运算符会被视为一元后缀运算符(即上式被视为 `a+++ .b` 而不是 `a +++ .b`)。 - -鉴于这些规则,`(`、`[` 和 `{` 是在运算符前面,`)`、`]` 和 `}` 是在运算符后面,以及字符 `,`、`;` 和 `:` 都被视为空白。 - -以上规则需注意一点。如果预定义运算符 `!` 或 `?` 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将 `?` 用作可选链式调用运算符,左侧必须无空白。如果用于条件运算符 `(? :)`,必须两侧都有空白。 - -在某些特定的设计中,以 `<` 或 `>` 开头的运算符会被分离成两个或多个符号。剩余部分可能会以同样的方式被再次分离。因此,在 `Dictionary>` 中没有必要添加空白来消除闭合字符 `>` 的歧义。在这个例子中,闭合字符 `>` 不会被视为单独的符号,因而不会被错误解析为 `>>` 运算符。 - -要学习如何自定义运算符,请参考 [自定义运算符](../02_language_guide/27_Advanced_Operators.md#custom-operators) 和 [运算符声明](./06_Declarations.md#operator-declaration)。要学习如何重载运算符,请参考 [运算符函数](../02_language_guide/27_Advanced_Operators.md#operator-functions)。 - -> 运算符语法 -> -> *运算符* → [头部运算符](#operator-head) [运算符字符组](#operator-characters)可选 -> -> *运算符* → [头部点运算符](#dot-operator-head) [点运算符字符组](#dot-operator-characters) -> -> -#### operator-head {#operator-head} -> -> *头部运算符* → **/** | **=** | **-** | **+** | **!** | __*__ | **%** | **<** | **>** | **&** | **|** | **^** | **~** | **?** -> -> *头部运算符* → U+00A1–U+00A7 -> -> *头部运算符* → U+00A9 或 U+00AB -> -> *头部运算符* → U+00AC 或 U+00AE -> -> *头部运算符* → U+00B0–U+00B1 -> -> *头部运算符* → U+00B6,U+00BB,U+00BF,U+00D7,或 U+00F7 -> -> *头部运算符* → U+2016–U+2017 -> -> *头部运算符* → U+2020–U+2027 -> -> *头部运算符* → U+2030–U+203E -> -> *头部运算符* → U+2041–U+2053 -> -> *头部运算符* → U+2055–U+205E -> -> *头部运算符* → U+2190–U+23FF -> -> *头部运算符* → U+2500–U+2775 -> -> *头部运算符* → U+2794–U+2BFF -> -> *头部运算符* → U+2E00–U+2E7F -> -> *头部运算符* → U+3001–U+3003 -> -> *头部运算符* → U+3008–U+3020 -> -> *头部运算符* → U+3030 -> -> -#### operator-character {#operator-character} -> -> *运算符字符* → [头部运算符](#operator-head) -> -> *运算符字符* → U+0300–U+036F -> -> *运算符字符* → U+1DC0–U+1DFF -> -> *运算符字符* → U+20D0–U+20FF -> -> *运算符字符* → U+FE00–U+FE0F -> -> *运算符字符* → U+FE20–U+FE2F -> -> *运算符字符* → U+E0100–U+E01EF -> -> -#### operator-characters {#operator-characters} -> -> *运算符字符组* → [运算符字符](#operator-character) [运算符字符组](#operator-characters)可选 -> -> -#### dot-operator-head {#dot-operator-head} -> -> *头部点运算符* → **.** -> -> -#### dot-operator-character {#dot-operator-character} -> -> *点运算符字符* → **.** | [运算符字符](#operator-character) -> -> -#### dot-operator-characters {#dot-operator-characters} -> -> *点运算符字符组* → [点运算符字符](#dot-operator-character) [点运算符字符组](#dot-operator-characters)可选 -> -> *二元运算符* → [运算符](#operator) -> -> *前缀运算符* → [运算符](#operator) -> -> *后缀运算符* → [运算符](#operator) +# 词法结构(Lexical Structure) + +Swift 的*“词法结构(lexical structure)”* 描述了能构成该语言中有效符号(token)的字符序列。这些合法符号组成了语言中最底层的构建基块,并在之后的章节中用于描述语言的其他部分。一个合法符号由一个标识符(identifier)、关键字(keyword)、标点符号(punctuation)、字面量(literal)或运算符(operator)组成。 + +通常情况下,符号是考虑了输入文本中最长可能的子字符串,并被随后将介绍的语法约束,根据 Swift 源文件的字符生成的。这种方法称为*“最长匹配(longest match)”*,或者*“最大适合(maximal munch)”*。 + +## 空白与注释 {#whitespace} + +空白(whitespace)有两个用途:分隔源文件中的符号和区分前缀、后缀和中缀运算符(参见 [运算符](#operators)),在其他情况下空白则会被忽略。以下的字符会被当作空白:空格(U+0020)、换行符(U+000A)、回车符(U+000D)、水平制表符(U+0009)、垂直制表符(U+000B)、换页符(U+000C)以及空字符(U+0000)。 + +注释被编译器当作空白处理。单行注释由 `//` 开始直至遇到换行符(U+000A)或者回车符(U+000D)。多行注释由 `/*` 开始,以 `*/` 结束。多行注释允许嵌套,但注释标记必须成对出现。 + +注释可以包含其他的格式和标记,如 [标记格式参考](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/index.html)中所述。 + +> 空白语法 +> +> *空白* → [空白项](#whitespace-item) [空白](#whitespace)可选 +#### whitespace-item {#whitespace-item} +> +> *空白项* → [断行符](#line-break) +> +> *空白项* → [行内空白](#inline-space) +> +> *空白项* → [注释](#comment) +> +> *空白项* → [多行注释](#multiline-comment) +> +> *空白项* → U+0000,U+0009,U+000B,U+000C 或者 U+0020 +> +> +#### line-break {#line-break} +> +> *断行符* → U+000A +> +> *断行符* → U+000D +> +> *断行符* → U+000D 接着是 U+000A +> + +#### inline-spaces {#inline-spaces} +> +> *行内空白组* → [行内空白](#inline-space) [行内空白组](#inline-spaces)可选 + +#### inline-space {#inline-space} +> *行内空白* → U+0009 或 U+0020 + +#### comment {#comment} +> +> *注释* → // [注释内容](#comment-text) [断行符](#line-break) +> +> +#### multiline-comment {#multiline-comment} +> +> *多行注释* → `/*` [多行注释内容](#multiline-commnet-text) `*/` +> +> +#### comment-text {#comment-text} +> +> *注释内容* → [注释内容项](#comment-text-item) [注释内容](#comment-text)可选 +> +> +#### comment-text-item {#comment-text-item} +> +> *注释内容项* → 任何 Unicode 标量值,除了 U+000A 或者 U+000D +> +> +#### multiline-commnet-text {#multiline-commnet-text} +> +> *多行注释内容* → [多行注释内容项](#multiline-comment-text-item) [多行注释内容](#multiline-comment-text)可选 +> +> *多行注释内容项* → [多行注释](#multiline-comment). +> +> *多行注释内容项* → [注释内容项](#comment-text-item) +> +> *多行注释内容项* → 任何 Unicode 标量值,除了 `/*` 或者 `*/` + +## 标识符 {#identifiers} + +*标识符(identifier)* 可以由以下的字符开始:大写或小写的字母 `A` 到 `Z`、下划线(`_`)、基本多文种平面(Basic Multilingual Plane)中非字符数字组合的 Unicode 字符以及基本多文种平面以外的非个人专用区字符。在首字符之后,允许使用数字和组合 Unicode 字符。 + +以下划线开头的标识视为内部标识符,即使声明了 `public` 访问级别。这个约定允许框架作者标记一部分不能被使用方调用或依赖的 API,即便因为某些限制原因导致它的声明必须是公开的。另外,双下划线开头的标识符是为 Swift 编译器和标准库预留的。 + +使用保留字作为标识符,需要在其前后增加反引号(`` ` ``)。例如,`class` 不是合法的标识符,但可以使用 `` `class` ``。反引号不属于标识符的一部分,`` `x` `` 和 `x` 表示同一标识符。 + +闭包中如果没有明确指定参数名称,参数将被隐式命名为 `$0`、`$1`、`$2` 等等。这些命名在闭包作用域范围内是合法的标识符。 + +编译器给含有属性包装器呈现值的属性自动合成以美元符号(*$*)开头的标识符。你的代码可以与这些标识符进行交互,,但是不能使用该前缀声明标识符。更详细的介绍,请查看 [特性](./07_Attributes.md) 章节中的 [属性包装器](./07_Attributes.md#propertywrapper) 部分。 + +> 标识符语法 +> +> #### identifier {#identifier} +> +> *标识符* → [头部标识符](#identifier-head) [标识符字符组](#identifier-characters)可选 +> +> *标识符* → \`[头部标识符](#identifier-head) [标识符字符组](#identifier-characters)可选\` +> +> *标识符* → [隐式参数名](#implicit-parameter-name) +> +> *标识符* → [属性包装器呈现值](#property-wrapper-projection) +> +> #### identifier-list {#identifier-list} +> +> *标识符列表* → [标识符](#identifier) | [标识符](#identifier) **,** [标识符列表](#identifier) +> +> +#### identifier-head {#identifier-head} +> +> *头部标识符* → 大写或小写字母 A - Z +> +> *头部标识符* → **_** +> +> *头部标识符* → U+00A8,U+00AA,U+00AD,U+00AF,U+00B2–U+00B5,或者 U+00B7–U+00BA +> +> *头部标识符* → U+00BC–U+00BE,U+00C0–U+00D6,U+00D8–U+00F6,或者 U+00F8–U+00FF +> +> *头部标识符* → U+0100–U+02FF,U+0370–U+167F,U+1681–U+180D,或者 U+180F–U+1DBF +> +> *头部标识符* → U+1E00–U+1FFF +> +> *头部标识符* → U+200B–U+200D,U+202A–U+202E,U+203F–U+2040,U+2054,或者 U+2060–U+206F +> +> *头部标识符* → U+2070–U+20CF,U+2100–U+218F,U+2460–U+24FF,或者 U+2776–U+2793 +> +> *头部标识符* → U+2C00–U+2DFF 或者 U+2E80–U+2FFF +> +> *头部标识符* → U+3004–U+3007,U+3021–U+302F,U+3031–U+303F,或者 U+3040–U+D7FF +> +> *头部标识符* → U+F900–U+FD3D,U+FD40–U+FDCF,U+FDF0–U+FE1F,或者 U+FE30–U+FE44 +> +> *头部标识符* → U+FE47–U+FFFD +> +> *头部标识符* → U+10000–U+1FFFD,U+20000–U+2FFFD,U+30000–U+3FFFD,或者 U+40000–U+4FFFD +> +> *头部标识符* → U+50000–U+5FFFD,U+60000–U+6FFFD,U+70000–U+7FFFD,或者 U+80000–U+8FFFD +> +> *头部标识符* → U+90000–U+9FFFD,U+A0000–U+AFFFD,U+B0000–U+BFFFD,或者 U+C0000–U+CFFFD +> +> *头部标识符* → U+D0000–U+DFFFD 或者 U+E0000–U+EFFFD +#### identifier-character {#identifier-character} +> +> *标识符字符* → 数值 0 - 9 +> +> *标识符字符* → U+0300–U+036F,U+1DC0–U+1DFF,U+20D0–U+20FF,或者 U+FE20–U+FE2F +> +> *标识符字符* → [头部标识符](#identifier-head) +> +> +#### identifier-characters {#identifier-characters} +> +> *标识符字符组* → [标识符字符](#identifier-character) [标识符字符组](#identifier-characters)可选 +> +> +#### implicit-parameter-name {#implicit-parameter-name} +> +> *隐式参数名* → **$** [十进制数字列表](#decimal-digit) +#### property-wrapper-projection {#property-wrapper-projection} +> +> *属性包装器呈现值* → **$** [标识符字符组](#identifier-characters) + +## 关键字和标点符号 {#keywords-and-punctuation} + +下面这些被保留的关键字不允许用作标识符,除非使用反引号转义,具体描述请参考 [标识符](#identifiers)。除了 `inout`、`var` 以及 `let` 之外的关键字可以用作某个函数声明或者函数调用当中的外部参数名,无需添加反引号转义。当一个成员与一个关键字具有相同的名称时,不需要使用反引号来转义对该成员的引用,除非在引用该成员和使用该关键字之间存在歧义 - 例如,`self`,`Type` 和 `Protocol` 在显式的成员表达式中具有特殊的含义,因此它们必须在该上下文中使用反引号进行转义。 + +* 用在声明中的关键字:`associatedtype`、`class`、`deinit`、`enum`、`extension`、`fileprivate `、`func`、`import`、`init`、`inout`、`internal`、`let`、`open`、`operator`、`private`、`precedencegroup`、`protocol`、`public`、`rethrows`、`static`、`struct`、`subscript`、`typealias` 以及 `var`。 +* 用在语句中的关键字:`break`、`case`、`catch`、`continue`、`default`、`defer`、`do`、`else`、`fallthrough`、`for`、`guard`、`if`、`in`、`repeat`、`return`、`throw`、`switch`、`where` 以及 `while`。 +* 用在表达式和类型中的关键字:`Any`、`as`、`catch`、`false`、`is`、`nil`、`rethrows`、`self`、`Self`、`super`、`throw`、`throws`、`true` 以及 `try `。 +* 用在模式中的关键字:`_`。 +* 以井字号(`#`)开头的关键字:`#available`、`#colorLiteral`、`#column`、`#dsohandle`、`#elseif`、`#else`、`#endif`、`#error`、`#fileID`、`#fileLiteral`、`#filePath`、`#file`、`#function`、`#if`、`#imageLiteral`、`#keyPath`、`#line`、`#selector`、`#sourceLocation` 以及 `#warning`。 +* 特定上下文中被保留的关键字:`associativity`、`convenience`、`didSet`、`dynamic`、`final`、`get`、`indirect`、`infix`、`lazy`、`left`、`mutating`、`none`、`nonmutating`、`optional`、`override`、`postfix`、`precedence`、`prefix`、`Protocol`、`required`、`right`、`set`、`some`、`Type`、`unowned`、`weak` 以及 `willSet`。这些关键字在特定上下文之外可以被用做标识符。 + +以下符号被保留为标点符号,不能用于自定义运算符:`(`、`)`、`{`、`}`、`[`、`]`、`.`、`,`、`:`、`;`、`=`、`@`、`#`、`&`(作为前缀运算符)、`->`、`` ` ``、`?`、以及 `!`(作为后缀运算符)。 + +## 字面量 {#literal} + +*字面量(literal)* 用来表示源码中某种特定类型的值,比如一个数字或字符串。 + +下面是字面量的一些示例: + +```swift +42 // 整数字面量 +3.14159 // 浮点数字面量 +"Hello, world!" // 字符串字面量 +/Hello, .*/ // 正则表达式字面量 +true // 布尔值字面量 +``` + +字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中,Swift 使用了显式类型注解(`: Int8`)来推导出 `42` 这个整数字面量的类型是 `Int8`。如果没有可用的类型信息,Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整数字面量的默认类型是 `Int`,浮点数字面量的默认类型是 `Double`,字符串字面量的默认类型是 `String`,布尔值字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"` 的默认推导类型就是 `String`。 + +当为一个字面量值指定了类型注解的时候,这个注解类型必须能通过这个字面量值实例化。也就是说,这个类型必须符合这些 Swift 标准库协议中的一个:整数字面量的 `ExpressibleByIntegerLiteral` 协议、浮点数字面量的 `ExpressibleByFloatLiteral` 协议、字符串字面量的 `ExpressibleByStringLiteral` 协议、布尔值字面量的 `ExpressibleByBooleanLiteral` 协议、只包含单个 Unicode 标量字符串文本的 `ExpressibleByUnicodeScalarLiteral` 协议以及只包含单个扩展字形簇(grapheme cluster)字符串文字的 `ExpressibleByExtendedGraphemeClusterLiteral` 协议。比如,`Int8` 符合 `ExpressibleByIntegerLiteral` 协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整数字面量 `42` 的类型注解。 + + +> 字面量语法 +> +> *字面量* → [数值字面量](#integer-literal) | [字符串字面量](#string-literal) | [布尔值字面量](#integer-literal) | [nil 字面量](#integer-literal) +> +> *数值字面量* → **-**可选 [整数字面量](#integer-literal) | **-**可选 [浮点数字面量](#floating-point-literal) +> +> *布尔值字面量* → **true** | **false** +> +> *nil 字面量* → **nil** + + +### 整数字面量{#integer-literal} + +*整数字面量(Integer Literals)* 表示未指定精度的整数值。整数字面量默认用十进制表示;可以加前缀来指定其他的进制。二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。 + +十进制字面量包含数字 `0` 至 `9`。二进制字面量包含 `0` 和 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F`(大小写均可)。 + +负整数字面量的表示方式为在整数字面量前加负号 `-`,比如 `-42`。 + +整型字面面可以使用下划线(`_`)来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,这同样也会被系统所忽略,并不会影响字面量的值。 + +除非特别指定,整数字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数](../02_language_guide/01_The_Basics.md#integers)。 + +> 整数字面量语法 +> +> +#### integer-literal {#integer-literal} +> +> *整数字面量* → [二进制字面量](#binary-literal) +> +> *整数字面量* → [八进制字面量](#octal-literal) +> +> *整数字面量* → [十进制字面量](#decimal-literal) +> +> *整数字面量* → [十六进制字面量](#hexadecimal-literal) +> +> +#### binary-literal {#binary-literal} +> +> *二进制字面量* → **0b** [二进制数字](#binary-digit) [二进制字面量字符组](#binary-literal-characters)可选 +> +> +#### binary-digit {#binary-digit} +> +> *二进制数字* → 数值 0 或 1 +> +> *二进制字面量字符* → [二进制数字](#binary-digit) | **_** +> +> +#### binary-literal-characters {#binary-literal-characters} +> +> *二进制字面量字符组* → [二进制字面量字符](#binary-literal-character) [二进制字面量字符组](#binary-literal-characters)可选 +> +> +#### octal-literal {#octal-literal} +> +> *八进制字面量* → **0o** [八进字数字](#octal-digit) [八进制字符组](#octal-literal-characters)可选 +> +> +#### octal-digit {#octal-digit} +> +> *八进字数字* → 数值 0 到 7 +> +> *八进制字符* → [八进字数字](#octal-digit) | **_** +> +> +#### octal-literal-characters {#octal-literal-characters} +> +> *八进制字符组* → [八进制字符](#octal-literal-character) [八进制字符组](#octal-literal-characters)可选 +> +> +#### decimal-literal {#decimal-literal} +> +> *十进制字面量* → [十进制数字](#decimal-digit) [十进制字符组](#decimal-literal-characters)可选 +> +> +#### decimal-digit {#decimal-digit} +> +> *十进制数字* → 数值 0 到 9 +> +> +#### decimal-literal-characters {#decimal-literal-characters} +> +> *十进制数字组* → [十进制数字](#decimal-digit) [十进制数字组](#decimal-literal-characters)可选 +> +> *十进制字符* → [十进制数字](#decimal-digit) | **_** +> +> *十进制字符组* → [十进制字符](#decimal-literal-characters) [十进制字符组](#decimal-literal-characters)可选 +> +> +#### hexadecimal-literal {#hexadecimal-literal} +> +> *十六进制字面量* → **0x** [十六进制数字](#hexadecimal-digit) [十六进制字面量字符组](#hexadecimal-literal-characters)可选 +> +> +#### hexadecimal-digit {#hexadecimal-digit} +> +> *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F +> +> *十六进制字符* → [十六进制数字](#hexadecimal-digit) | **-** +> +> +#### hexadecimal-literal-characters {#hexadecimal-literal-characters} +> +> *十六进制字面量字符组* → [十六进制字符](#hexadecimal-literal-characters) [十六进制字面量字符组](#hexadecimal-literal-characters)可选 + +### 浮点数字面量{#floating-point-literal} + +*浮点数字面量(Floating-point literals)*表示未指定精度浮点数的值。 + +浮点数字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。 + +十进制浮点数字面量由十进制数字串后跟十进制小数部分或十进制指数部分(或两者皆有)组成。十进制小数部分由小数点(`.`)后跟十进制数字串组成。指数部分由大写或小写字母 `e` 为前缀后跟十进制数字串组成,这串数字表示 `e` 之前的数值乘以 10 的几次方。例如:`1.25e2` 表示 1.25 x 10²,也就是 `125.0`。同样,`1.25e-2` 表示 1.25 x 10¯²,也就是 `0.0125`。 + +十六进制浮点数字面量由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 为前缀后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 15 x 2²,也就是 `60`。同样,`0xFp-2` 表示 15 x 2¯²,也就是 `3.75`。 + +负数的浮点数字面量由负号(`-`)和浮点数字面量组成,例如 `-42.5`。 + +浮点数字面量允许使用下划线(`_`)来增强数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。 + +除非特别指定,浮点数字面量的默认推导类型为 Swift 标准库类型中的 `Double`,表示 64 位浮点数。Swift 标准库也定义了 `Float` 类型,表示 32 位浮点数。 + +> 浮点数字面量语法 +> +> +#### floating-point-literal {#floating-point-literal} +> +> *浮点数字面量* → [十进制字面量](#decimal-literal) [十进制分数](#decimal-fraction)可选 [十进制指数](#decimal-exponent)可选 +> +> *浮点数字面量* → [十六进制字面量](#hexadecimal-literal) [十六进制分数](#hexadecimal-fraction)可选 [十六进制指数](#hexadecimal-exponent) +> +> +#### decimal-fraction {#decimal-fraction} +> +> *十进制分数* → **.** [十进制字面量](#decimal-literal) +> +> +#### decimal-exponent {#decimal-exponent} +> +> *十进制指数* → [十进制指数 e](#floating-point-e) [正负号](#sign)可选 [十进制字面量](#decimal-literal) +> +> +#### hexadecimal-fraction {#hexadecimal-fraction} +> +> *十六进制分数* → **.** [十六进制数字](#hexadecimal-digit) [十六进制字面量字符组](#hexadecimal-literal-characters)可选 +> +> +#### hexadecimal-exponent {#hexadecimal-exponent} +> +> *十六进制指数* → [十六进制指数 p](#floating-point-p) [正负号](#sign)可选 [十进制字面量](#decimal-literal) +> +> +#### floating-point-e {#floating-point-e} +> +> *十进制指数 e* → **e** | **E** +> +> +#### floating-point-p {#floating-point-p} +> +> *十六进制指数 p* → **p** | **P** +> +> +#### sign {#sign} +> +> *正负号* → **+** | **-** + +### 字符串字面量 {#string-literal} + +字符串字面量是被引号包括的一串字符组成。单行字符串字面量是被包在双引号中的一串字符组成,形式如下: + +> "`字符`" + +字符串字面量中不能包含未转义的双引号(`"`)、未转义的反斜线(`\`)、回车符、换行符。 + +多行字符串字面量被包在三个双引号中的一串字符组成,形式如下: +> """ +> `字符` +> """ + +与单行字符串字面量不同的是,多行字符串字面量可以包含不转义的双引号("),回车以及换行。它不能包含三个未转义的连续双引号。 + +`"""` 之后的回车或者换行开始多行字符串字面量,它们不是字符串的一部分。结束部分的 `"""` 以及它之前的回车或者换行,也不是字符串的一部分。要让多行字符串字面量的开始或结束带有换行,就在第一行或者最后一行写一个空行。 + +多行字符串字面量可以使用任何空格或制表符组合进行缩进;这些缩进不会包含在字符串中。`"""` 的结束符号决定了缩进:字面量中的每一个非空行开头都必须与结束符 `"""` 之前出现的缩进完全一致;空格和制表符不会被转换。你可以在缩进后包含额外的空格和制表符;这些空格和制表符会在字符串中出现。 + +多行字符串字面量中的一行结束使用规范化的换行符号。尽管你的源代码混用了回车和换行符,字符串中所有的行结束都必须一样. + +在多行字符串字面量里,在行末用反斜线(`\`)可以省略字符串行间中断。反斜线和换行符之间的空白也将被忽略。你可以在你的代码里用这种语法硬包裹多行字符串字面量,而不改变结果字符串的值。 + +可以在字符串字面量中使用的转义特殊符号如下: + +* 空字符 (`\0`) +* 反斜线 (`\\`) +* 水平制表符 (`\t`) +* 换行符 (`\n`) +* 回车符 (`\r`) +* 双引号 (`\"`) +* 单引号 (`\'`) +* Unicode 标量 (`\u{`n`}`),n 为一到八位的十六进制数字 + +字符串字面量允许在反斜杠(`\`)后的括号 `()` 中插入表达式的值。插入表达式可以包含字符串字面量,但不能包含未转义的反斜线(`\`)、回车符以及换行符。 + +例如,以下所有字符串字面量的值都是相同的: + +```swift +"1 2 3" +"1 2 \("3")" +"1 2 \(3)" +"1 2 \(1 + 2)" +let x = 3; "1 2 \(x)" +``` + +由扩展分隔符包裹的字符串,它是由引号以及成对出现的数字符号(`#`)包裹的字符串序列。由扩展分隔符包裹的字符串形式如下所示: + +> \#"`characters`"# +> +> \#""" +> +> `characters` +> +> """# + +特殊字符在被扩展分隔符分隔的结果字符串中会展示为普通字符,而不是特殊字符。你可以使用扩展分隔符来创建一些通常情况下具有特殊效果的字符串。例如,生成字符串插值,启动转义序列或终止字符串。 + +以下所示,由字符串字面量和扩展分隔符所创建的字符串是等价的: + +```swift +let string = #"\(x) \ " \u{2603}"# +let escaped = "\\(x) \\ \" \\u{2603}" +print(string) +// Prints "\(x) \ " \u{2603}" +print(string == escaped) +// Prints "true" + +``` + +如果在一个字符串中使用多对扩展分隔符,请不要在分隔符之间使用空格。 + +```swift +print(###"Line 1\###nLine 2"###) // OK +print(# # #"Line 1\# # #nLine 2"# # #) // Error + +``` + +使用扩展分隔符创建的多行字符串字面量与普通多行字符串字面量具有相同的缩进要求。 + +字符串字面量的默认推导类型为 `String`。更多有关 `String` 类型的信息请参考 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 以及 [*字符串结构参考*](https://developer.apple.com/documentation/swift/string)。 + +用 `+` 操作符连接的字符型字面量是在编译时进行连接的。比如下面的 `textA` 和 `textB` 是完全一样的,`textA` 没有任何运行时的连接操作。 + +```swift +let textA = "Hello " + "world" +let textB = "Hello world" +``` + +> 字符串字面量语法 +> +> *字符串字面量* → [静态字符串字面量](#static-string-literal) | [插值字符串字面量](#interpolated-string-literal) +> +> *字符串开分隔定界符* → [字符串扩展分隔符](#extended-string-literal-delimiter) **"** +> +> *字符串闭分隔定界符* → **"** [字符串扩展分隔符](#extended-string-literal-delimiter)可选 +> +> +#### static-string-literal {#static-string-literal} +> +> *静态字符串字面量* → [字符串开分隔定界符](#extended-string-literal-delimiter) [引用文本](#quoted-text)可选 [字符串闭分隔定界符](#extended-string-literal-delimiter) +> +> *静态字符串字面量* → [多行字符串开分隔定界符](#extended-string-literal-delimiter) [多行引用文本](#multiline-quoted-text)可选 [多行字符串闭分隔定界符](#extended-string-literal-delimiter) +> +> *多行字符串开分隔定界符* → [字符串扩展分隔符](#extended-string-literal-delimiter) **"""** +> +> *多行字符串闭分隔定界符* → **"""** [字符串扩展分隔符](#extended-string-literal-delimiter) +> +> +#### extended-string-literal-delimiter {#extended-string-literal-delimiter} +> +> *字符串扩展分隔符* → **#** [字符串扩展分隔符](#extended-string-literal-delimiter)可选 +> +> +#### quoted-text {#quoted-text} +> +> *引用文本* → [引用文本项](#quoted-text-item) [引用文本](#quoted-text)可选 +> +> +#### quoted-text-item {#quoted-text-item} +> +> *引用文本项* → [转义字符](#escaped-character) +> +> *引用文本项* → 除了 **"**、**\\**、U+000A、U+000D 以外的所有 Unicode 字符 +> +> +#### multiline-quoted-text {#multiline-quoted-text} +> +> *多行引用文本* → [多行引用文本项](#multiline-quoted-text-item) [多行引用文本](#multiline-quoted-text)可选 +> +> +#### multiline-quoted-text-item {#multiline-quoted-text-item} +> +> *多行引用文本项* → [转义字符](#escaped-character)可选 +> +#### multiline-quoted-text {#multiline-quoted-text} +> +> *多行引用文本* → 除了 **\\** 以外的任何 Unicode 标量值 +> +> *多行引用文本* → [转义换行](#escaped-newline) +> +> +#### interpolated-string-literal {#interpolated-string-literal} +> +> *插值字符串字面量* → [字符串开分隔定界符](#extended-string-literal-delimiter) [插值文本](#interpolated-text)可选 [字符串闭分隔定界符](#extended-string-literal-delimiter) +> +> *插值字符串字面量* → [多行字符串开分隔定界符](#extended-string-literal-delimiter) [插值文本](#interpolated-text)可选 [多行字符串闭分隔定界符](#extended-string-literal-delimiter) +> +> +#### interpolated-text {#interpolated-text} +> +> *插值文本* → [插值文本项](#interpolated-text-item) [插值文本](#interpolated-text)可选 +> +> +#### interpolated-text-item {#interpolated-text-item} +> +> *插值文本项* → **\\(**[ 表达式 ](./04_Expressions.md)**)** | [引用文本项](#quoted-text-item) +> +> *多行插值文本* → [多行插值文本项](#multiline-quoted-text-item) [多行插值文本](#multiline-quoted-text)可选 +> +> *多行插值文本项* → **\\(** [表达式](./04_Expressions.md) **)** | [多行引用文本项](#multiline-quoted-text-item) +> +> +#### escape-sequence {#escape-sequence} +> +> *转义序列* → **\\** [字符串扩展分隔符](#extended-string-literal-delimiter) +> +> +#### escaped-character {#escaped-character} +> +> *转义字符* → [转义序列](#escape-sequence) **0** | [转义序列](#escape-sequence) **\\** | [转义序列](#escape-sequence) **t** | [转义序列](#escape-sequence) **n** | [转义序列](#escape-sequence) **r** | [转义序列](#escape-sequence) **\"** | [转义序列](#escape-sequence) **'** +> +> *转义字符* → [转义序列](#escape-sequence) **u {** [unicode 标量数字](#unicode-scalar-digits) **}** +> +> +#### unicode-scalar-digits {#unicode-scalar-digits} +> +> *unicode 标量数字* → 一到八位的十六进制数字 +> +> +#### escaped-newline {#escaped-newline} +> +> *转义换行符* → [转义序列](#escape-sequence) [空白](#whitespace)可选 [断行符](#line-break) + +### 正则表达式字面量 {#regular-expression-literals} + +正则表达式字面量是被符号 `/` 以如下形式包围的一串字符: + +```swift +/ 正则表达式 / +``` + +正则表达式字面量不能以未转义的制表符或空格开头,且正则表达式字面量内不能包含未转义的 `/` 符号、回车或是换行符。 + +在正则表达式字面量内部,反斜杠符号 (`/`) 被视作正则表达式的一部分,而非如字符串表达式中一样被视为一个转义符。反斜杠符号提示其后的特殊字符应当按照其字面义进行解读,其后的非特殊字符则应按照特殊方式进行解读。例如,`/\(/` 匹配了一个左括号 (`(`), 而 `/\d/` 匹配了一个单个数字。 + +被一对反斜杠 `/` 和一对称数字符 `#` 包围的一串字符被称为由扩展分隔符分割的正则表达式字面量,它有以下两种形式: + +```swift +`#/ 正则表达式 /#` +``` +```swift +`#/ +正则表达式 +/#` +``` + +由扩展分隔符分割的正则表达式字面量可以以未转义的制表符或是空格开头,也可以包含未转义的反斜杠符号 (`/`) 并占据多行代码。若需要占用多行,正则表达式字面量开头的扩展分隔符 `#` 应位于行末,末尾的扩展分隔符 `#` 应独占一行。在多行正则表达式字面量内默认启用了扩展正则表达式的语法规则——尤其是关于忽略空格并允许注释的部分。 + +如果你使用多个数字符号(`#`)来形成扩展分隔符分隔的正则表达式,数字符号不应使用空格分隔。 + +```swift +let regex1 = ##/abc/## //正常运行 +let regex2 = # #/abc/# # //报错 +``` + +若需要创建一个空白的正则表达式字面量,必须使用扩展分隔符语法。 + +> 正则表达式字面量语法 +> +> 正则表达式字面量 → [正则表达式字面量开分隔定界符](#regular-expression-literal-opening-delimiter) [正则表达式字面量](#regular-expression) [正则表达式字面量闭分隔定界符](#regular-expression-literal-closing-delimiter) +> +#### 正则表达式字面量 {#regular-expression} +> +> 正则表达式字面量 → 任何正则表达式 +> +#### 正则表达式字面量开分隔定界符 {#regular-expression-literal-opening-delimiter} +> +> 正则表达式字面量开分隔定界符 → [扩展正则表达式分隔符](#extended-regular-expression-literal-delimiter)可选 **/** +> +#### 正则表达式字面量闭分隔定界符 {#regular-expression-literal-closing-delimiter} +> +> 正则表达式字面量闭分隔定界符 → **/** [扩展正则表达式分隔符](#extended-regular-expression-literal-delimiter)可选 +> +#### 扩展正则表达式分隔符 {#extended-regular-expression-literal-delimiter} +> +> 扩展正则表达式分隔符 → **#** [扩展正则表达式分隔定界符](#extended-regular-expression-literal-delimiter)可选 +> + +## 运算符 {#operator} + +Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](../02_language_guide/02_Basic_Operators.md) 和 [高级运算符](../02_language_guide/27_Advanced_Operators.md) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。 + +自定义运算符可以由以下其中之一的 ASCII 字符 `/`、`=`、`-`、`+`、`!`、`*`、`%`、`<`、`>`、`&`、`|`、`^`、`?` 以及 `~`,或者后面语法中规定的任一个 Unicode 字符(其中包含了*数学运算符*、*零散符号(Miscellaneous Symbols)* 以及*印刷符号(Dingbats)*之类的 Unicode 块)开始。在第一个字符之后,允许使用组合型 Unicode 字符。 + +您也可以以点号(`.`)开头来定义自定义运算符。这些运算符可以包含额外的点。例如 `.+.` 会被看作一个单独的运算符。如果某个运算符不是以点号开头的,那么它就无法再包含另外的点号了。例如,`+.+` 就会被看作为一个 `+` 运算符后面跟着一个 `.+` 运算符。 + +虽然您可以用问号 `(?)` 来自定义运算符,但是这个运算符不能只包含单独的一个问号。此外,虽然运算符可以包含一个惊叹号 `(!)`,但是前缀运算符不能够以问号或者惊叹号开头。 + +> 注意 +> +> 以下这些标记 `=`、`->`、`//`、`/*`、`*/`、`.`,前缀运算符 `<`、`&` 和 `?`,中缀运算符 `?`,后缀运算符 `>`、`!` 和 `?` 是被系统保留的。这些符号不能被重载,也不能用作自定义运算符。 + +运算符两侧的空白被用来区分该运算符是否为前缀运算符、后缀运算符或二元运算符。规则总结如下: + +* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+++b` 和 `a +++ b` 当中的 `+++` 运算符会被看作二元运算符。 +* 如果运算符只有左侧空白,将被看作一元前缀运算符。例如 `a +++b` 中的 `+++` 运算符会被看做是一元前缀运算符。 +* 如果运算符只有右侧空白,将被看作一元后缀运算符。例如 `a+++ b` 中的 `+++` 运算符会被看作是一元后缀运算符。 +* 如果运算符左侧没有空白并紧跟 `(.)`,将被看作一元后缀运算符。例如 `a+++.b` 中的 `+++` 运算符会被视为一元后缀运算符(即上式被视为 `a+++ .b` 而不是 `a +++ .b`)。 + +鉴于这些规则,`(`、`[` 和 `{` 是在运算符前面,`)`、`]` 和 `}` 是在运算符后面,以及字符 `,`、`;` 和 `:` 都被视为空白。 + +以上规则需注意一点。如果预定义运算符 `!` 或 `?` 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将 `?` 用作可选链式调用运算符,左侧必须无空白。如果用于条件运算符 `(? :)`,必须两侧都有空白。 + +如果中缀操作符的参数之一是正则表达式字面量,则操作符两侧都必须有空格。 + +在某些特定的设计中,以 `<` 或 `>` 开头的运算符会被分离成两个或多个符号。剩余部分可能会以同样的方式被再次分离。因此,在 `Dictionary>` 中没有必要添加空白来消除闭合字符 `>` 的歧义。在这个例子中,闭合字符 `>` 不会被视为单独的符号,因而不会被错误解析为 `>>` 运算符。 + +要学习如何自定义运算符,请参考 [自定义运算符](../02_language_guide/27_Advanced_Operators.md#custom-operators) 和 [运算符声明](./06_Declarations.md#operator-declaration)。要学习如何重载运算符,请参考 [运算符函数](../02_language_guide/27_Advanced_Operators.md#operator-functions)。 + +> 运算符语法 +> +> *运算符* → [头部运算符](#operator-head) [运算符字符组](#operator-characters)可选 +> +> *运算符* → [头部点运算符](#dot-operator-head) [点运算符字符组](#dot-operator-characters) +> +> +#### operator-head {#operator-head} +> +> *头部运算符* → **/** | **=** | **-** | **+** | **!** | __*__ | **%** | **<** | **>** | **&** | **|** | **^** | **~** | **?** +> +> *头部运算符* → U+00A1–U+00A7 +> +> *头部运算符* → U+00A9 或 U+00AB +> +> *头部运算符* → U+00AC 或 U+00AE +> +> *头部运算符* → U+00B0–U+00B1 +> +> *头部运算符* → U+00B6,U+00BB,U+00BF,U+00D7,或 U+00F7 +> +> *头部运算符* → U+2016–U+2017 +> +> *头部运算符* → U+2020–U+2027 +> +> *头部运算符* → U+2030–U+203E +> +> *头部运算符* → U+2041–U+2053 +> +> *头部运算符* → U+2055–U+205E +> +> *头部运算符* → U+2190–U+23FF +> +> *头部运算符* → U+2500–U+2775 +> +> *头部运算符* → U+2794–U+2BFF +> +> *头部运算符* → U+2E00–U+2E7F +> +> *头部运算符* → U+3001–U+3003 +> +> *头部运算符* → U+3008–U+3020 +> +> *头部运算符* → U+3030 +> +> +#### operator-character {#operator-character} +> +> *运算符字符* → [头部运算符](#operator-head) +> +> *运算符字符* → U+0300–U+036F +> +> *运算符字符* → U+1DC0–U+1DFF +> +> *运算符字符* → U+20D0–U+20FF +> +> *运算符字符* → U+FE00–U+FE0F +> +> *运算符字符* → U+FE20–U+FE2F +> +> *运算符字符* → U+E0100–U+E01EF +> +> +#### operator-characters {#operator-characters} +> +> *运算符字符组* → [运算符字符](#operator-character) [运算符字符组](#operator-characters)可选 +> +> +#### dot-operator-head {#dot-operator-head} +> +> *头部点运算符* → **.** +> +> +#### dot-operator-character {#dot-operator-character} +> +> *点运算符字符* → **.** | [运算符字符](#operator-character) +> +> +#### dot-operator-characters {#dot-operator-characters} +> +> *点运算符字符组* → [点运算符字符](#dot-operator-character) [点运算符字符组](#dot-operator-characters)可选 +> +> *二元运算符* → [运算符](#operator) +> +> *前缀运算符* → [运算符](#operator) +> +> *后缀运算符* → [运算符](#operator) diff --git a/source/03_language_reference/03_Types.md b/source/03_language_reference/03_Types.md index fb726c43..8e2529fa 100644 --- a/source/03_language_reference/03_Types.md +++ b/source/03_language_reference/03_Types.md @@ -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` 类型的常量中,因为这可能导致值的逃逸。 @@ -195,7 +197,7 @@ func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) { > #### function-type {#function-type} -> *函数类型* → [特性列表](./07_Attributes.md#attributes)可选 [函数类型子句](#function-type-argument-clause) **throws**可选 **->** [类型](#type) +> *函数类型* → [特性列表](./07_Attributes.md#attributes)可选 [函数类型子句](#function-type-argument-clause) **async**可选**throws**可选 **->** [类型](#type) #### function-type-argument-clause {#function-type-argument-clause} > *函数类型子句* → **(**­ **)**­ @@ -539,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)可选 [类型标识符](#type-identifier) | [属性](./07_Attributes.md#attributes)可选 [类型标识符](#type-identifier) **,** [类型继承列表](#type-inheritance-list) ## 类型推断 diff --git a/source/03_language_reference/04_Expressions.md b/source/03_language_reference/04_Expressions.md index 86b4e4e7..21c4ca98 100644 --- a/source/03_language_reference/04_Expressions.md +++ b/source/03_language_reference/04_Expressions.md @@ -1,13 +1,13 @@ # 表达式(Expressions) -Swift 中存在四种表达式:前缀表达式,二元表达式,基本表达式和后缀表达式。表达式在返回一个值的同时还可以引发副作用。 +Swift 中存在四种表达式:前缀表达式,中缀表达式,基本表达式和后缀表达式。表达式在返回一个值的同时还可以引发副作用。 -通过前缀表达式和二元表达式可以对简单表达式使用各种运算符。基本表达式从概念上讲是最简单的一种表达式,它是一种访问值的方式。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。 +通过前缀表达式和中缀表达式可以对简单表达式使用各种运算符。基本表达式从概念上讲是最简单的一种表达式,它是一种访问值的方式。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。 > 表达式语法 #### expression {#expression} -> *表达式* → [try 运算符](#try-operator)可选 [await 运算符](#await-operator)可选 [前缀表达式](#prefix-expression) [二元表达式列表](#binary-expressions)可选 +> *表达式* → [try 运算符](#try-operator)可选 [await 运算符](#await-operator)可选 [前缀表达式](#prefix-expression) [中缀表达式列表](#infix-expressions)可选 #### expression-list {#expression-list} @@ -62,7 +62,7 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表 强制 try 表达式的返回值是该*表达式*的值。如果该*表达式*抛出了错误,将会引发运行时错误。 -在二元运算符左侧的表达式被标记上 `try`、`try?` 或者 `try!` 时,这个运算符对整个二元表达式都产生作用。也就是说,你可以使用括号来明确运算符的作用范围。 +在中缀运算符左侧的表达式被标记上 `try`、`try?` 或者 `try!` 时,这个运算符对整个中缀表达式都产生作用。也就是说,你可以使用括号来明确运算符的作用范围。 ```swift // try 对两个函数调用都产生作用 @@ -75,7 +75,7 @@ sum = try (someThrowingFunction() + anotherThrowingFunction()) sum = (try someThrowingFunction()) + anotherThrowingFunction() ``` -`try` 表达式不能出现在二元运算符的的右侧,除非二元运算符是赋值运算符或者 `try` 表达式是被圆括号括起来的。 +`try` 表达式不能出现在中缀运算符的的右侧,除非中缀运算符是赋值运算符或者 `try` 表达式是被圆括号括起来的。 如果表达式中同时包含 `try` 和 `await` 运算符,`try` 运算符必须在前面。 @@ -99,7 +99,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() `await` 表达式只能在异步的上下文中出现,比如传入 `async(priority:operation:)` 函数的尾随闭包中。它不能在 `defer` 语句的闭包中,或者在同步函数的自动闭包中出现。 -在二元运算符左侧的表达式被标记上 `await` 运算符时,这个运算符对整个二元表达式都产生作用。也就是说,你可以使用括号来明确运算符的作用范围。 +在中缀运算符左侧的表达式被标记上 `await` 运算符时,这个运算符对整个中缀表达式都产生作用。也就是说,你可以使用括号来明确运算符的作用范围。 ```swift // await 对两个函数调用都产生作用 @@ -112,7 +112,7 @@ sum = await (someAsyncFunction() + anotherAsyncFunction()) sum = (await someAsyncFunction()) + anotherAsyncFunction() ``` -`await` 表达式不能出现在二元运算符的的右侧,除非二元运算符是赋值运算符或者 `await` 表达式是被圆括号括起来的。 +`await` 表达式不能出现在中缀运算符的的右侧,除非中缀运算符是赋值运算符或者 `await` 表达式是被圆括号括起来的。 如果表达式中同时包含 `try` 和 `await` 运算符,`try` 运算符必须在前面。 @@ -122,11 +122,11 @@ sum = (await someAsyncFunction()) + anotherAsyncFunction() > *await 运算符* → **await** -## 二元表达式 {#binary-expressions} +## 中缀表达式 {#infix-expressions} -*二元表达式*由中缀运算符和左右参数表达式组成。形式如下: +*中缀表达式*由中缀运算符和左右参数表达式组成。形式如下: -> `左侧参数` `二元运算符` `右侧参数` +> `左侧参数` `中缀运算符` `右侧参数` 关于这些运算符的更多信息,请参阅 [基本运算符](../02_language_guide/02_Basic_Operators.md) 和 [高级运算符](../02_language_guide/27_Advanced_Operators.md)。 @@ -134,22 +134,22 @@ sum = (await someAsyncFunction()) + anotherAsyncFunction() > 注意 > -> 在解析时,一个二元表达式将作为一个扁平列表表示,然后根据运算符的优先级,再进一步进行组合。例如,`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)可选 [前缀表达式](#prefix-expression) +> *中置表达式* → [赋值运算符](#assignment-operator) [try 运算符](#try-operator)可选 [前缀表达式](#prefix-expression) > -> *二元表达式* → [条件运算符](#conditional-operator) [try 运算符](#try-operator)可选 [前缀表达式](#prefix-expression) +> *中置表达式* → [条件运算符](#conditional-operator) [try 运算符](#try-operator)可选 [前缀表达式](#prefix-expression) > -> *二元表达式* → [类型转换运算符](#type-casting-operator) +> *中置表达式* → [类型转换运算符](#type-casting-operator) -#### binary-expressions {#binary-expressions} -> *二元表达式列表* → [二元表达式](#binary-expression) [二元表达式列表](#binary-expressions)可选 +#### infix-expressions {#infix-expressions} +> *中置表达式列表* → [中置表达式](#infix-expression) [中置表达式列表](#infix-expressions)可选 ### 赋值表达式 {#assignment-operator} 赋值表达式会为某个给定的表达式赋值,形式如下; @@ -235,7 +235,7 @@ f(x as Any) > *类型转换运算符* → **as** **!** [类型](./03_Types.md#type) ## 基本表达式 {#primary-expressions} -*基本表达式*是最基本的表达式。它们可以单独使用,也可以跟前缀表达式、二元表达式、后缀表达式组合使用。 +*基本表达式*是最基本的表达式。它们可以单独使用,也可以跟前缀表达式、中置表达式、后缀表达式组合使用。 > 基本表达式语法 @@ -461,6 +461,16 @@ struct Point { 闭包的参数声明形式跟函数一样,请参阅 [函数声明](./06_Declarations.md#function-declaration)。 +在闭包表达式中写入 `throws` 或 `async` 将显式地将闭包标记为丢掷或异步的。 + +```swift +{ (parameters) async throws -> return type in + statements +} +``` + +如果闭包的主体中含有 try 表达式,则认为该闭包会引发异常。同理,若闭包主体含有 await 表达式,则认为该闭包是异步的。 + 闭包还有几种特殊的形式,能让闭包使用起来更加简洁: - 闭包可以省略它的参数和返回值的类型。如果省略了参数名和所有的类型,也要省略 `in` 关键字。如果被省略的类型无法被编译器推断,那么就会导致编译错误。 @@ -555,11 +565,11 @@ myFunction { [weak parent = self.parent] in print(parent!.title) } > > #### closure-expression {#closure-expression} > -> *闭包表达式* → **{** [闭包签名](#closure-signature)可选 [语句](#statements) **}** +> *闭包表达式* → **{** [特性](#attribute)可选 [闭包签名](#closure-signature)可选 [语句](#statements) **}** > > #### closure-signature {#closure-signature} > -> *闭包签名* → [捕获列表](#capture-list)可选 [闭包形参子句](#closure-parameter-clause) **throws**可选 [函数结果](./06_Declarations.md#function-result)可选 **in** +> *闭包签名* → [捕获列表](#capture-list)可选 [闭包形参子句](#closure-parameter-clause) **async**可选 **throws**可选 [函数结果](./06_Declarations.md#function-result)可选 **in** > > *闭包签名* → [捕获列表](#capture-list) **in** > @@ -1255,6 +1265,23 @@ 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} @@ -1262,7 +1289,9 @@ let x = [10, 3, 20, 15, 4] > > *显式成员表达式* → [后缀表达式](#postfix-expression) **.** [标识符](./02_Lexical_Structure.md#identifier) [泛型实参子句](./09_Generic_Parameters_and_Arguments.md#generic-argument-clause)可选
> -> *显式成员表达式* → [后缀表达式](#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)可选
diff --git a/source/03_language_reference/05_Statements.md b/source/03_language_reference/05_Statements.md index 4237d6a9..075e8983 100755 --- a/source/03_language_reference/05_Statements.md +++ b/source/03_language_reference/05_Statements.md @@ -105,7 +105,7 @@ while 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)可选 | **var** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer)可选 ### Repeat-While 语句 {#repeat-while-statements} `repeat-while` 语句至少执行一次代码块,之后只要循环条件为真,就会重复执行代码块。 @@ -635,7 +635,7 @@ 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` 。 @@ -663,6 +663,7 @@ statements to compile if both compilation conditions are false > > 即使没有被编译,上面编译配置中的每个语句仍然会被解析。然而,唯一的例外是编译配置语句中包含 `swift()` 或 `compiler()` 条件:这时仅当编译器版本和语言版本匹配时,语句才会被解析。这种设定能确保旧的编译器不会尝试去解析新 Swift 版本的语法。 +关于在条件编译块中如何包装显式成员表达式,请参见 [显式成员表达式](#Expressions.xhtml#ID400) 章节。 #### build-config-statement {#build-config-statement} > 条件编译代码块语法 @@ -821,7 +822,8 @@ 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) diff --git a/source/03_language_reference/06_Declarations.md b/source/03_language_reference/06_Declarations.md index 5544ec6d..1bdeb48a 100755 --- a/source/03_language_reference/06_Declarations.md +++ b/source/03_language_reference/06_Declarations.md @@ -630,6 +630,10 @@ func 函数名称(参数列表) async -> 返回类型 { `async` 关键字是函数类型中的一部分。同步函数是异步函数的子类型。所以,你可以在使用异步函数的地方,使用同步函数。同步方法可以重写异步方法,且同步方法可以满足对异步方法的协议要求。 +你可以根据函数是否异步来重写函数。在调用点,上下文决定了应用哪种函数进行重写:异步上下文使用异步函数;同步上下文使用同步函数。 + +异步方法无法重写同步方法,异步方法也无法满足同步方法的协议要求。反过来说,同步方法可以重写异步方法,并且同步方法可以满足异步方法的协议要求。 + ### 永不返回的函数 {#functions-that-never-return} Swift 定义了 `Never` 类型,它表示函数或者方法不会返回给它的调用者。`Never` 返回类型的函数或方法可以称为不归,不归函数、方法要么引发不可恢复的错误,要么永远不停地运作,这会使调用后本应执行得代码就不再执行了。但即使是不归函数、方法,抛错函数和重抛出函数也可以将程序控制转移到合适的 `catch` 代码块。 @@ -1307,7 +1311,7 @@ convenience init(参数列表) { > 如果使用 `required` 声明修饰符标记一个构造器,在子类中重写这种构造器时,无需使用 `override` 修饰符。 > -就像函数和方法,构造器也可以抛出或者重抛错误,你可以在构造器参数列表的圆括号之后使用 `throws` 或 `rethrows` 关键字来表明相应的抛出行为。 +就像函数和方法,构造器也可以抛出或者重抛错误,你可以在构造器参数列表的圆括号之后使用 `throws` 或 `rethrows` 关键字来表明相应的抛出行为。同样,构造器也可以是异步的,你可以使用 `async` 关键字来表明这一点。 关于在不同类型中声明构造器的例子,请参阅 [构造过程](../02_language_guide/14_Initialization.md)。 @@ -1350,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} > 构造器声明语法 > diff --git a/source/03_language_reference/07_Attributes.md b/source/03_language_reference/07_Attributes.md index 00e2ebb3..47c09d95 100755 --- a/source/03_language_reference/07_Attributes.md +++ b/source/03_language_reference/07_Attributes.md @@ -685,6 +685,12 @@ struct ArrayBuilder { 编译生成可执行程序的 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` 的类型。 @@ -733,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 特性。 diff --git a/source/03_language_reference/10_Summary_of_the_Grammar.md b/source/03_language_reference/10_Summary_of_the_Grammar.md index 511521d2..85d8c3b7 100755 --- a/source/03_language_reference/10_Summary_of_the_Grammar.md +++ b/source/03_language_reference/10_Summary_of_the_Grammar.md @@ -7,6 +7,8 @@ > *空白字符* → [空白字符项](./02_Lexical_Structure.md#whitespace-item) [空白字符](./02_Lexical_Structure.md#whitespace)可选 > > *空白字符项* → [换行符](./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) > > *数值型字面量* → **-**可选[整形字面量](./02_Lexical_Structure.md#integer-literal) | **-**可选[浮点型字面量](./02_Lexical_Structure.md#floating-point-literal) > @@ -243,6 +245,19 @@ > > *转义换行符* → [转义序列](./02_Lexical_Structure.md#escape-sequence) [空白](./02_Lexical_Structure.md#whitespace)可选 [换行符](./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)可选 **/** +> +> *正则表达式字面量闭分割定界符* → **/** [正则表达式扩展分隔符](./02_Lexical_Structure.md#grammar_extended-regular-expression-literal-delimiter)可选 +> +> *正则表达式扩展分隔符* → **#** [正则表达式扩展分隔符](./02_Lexical_Structure.md#grammar_extended-regular-expression-literal-delimiter)可选 @@ -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**可选**->** [类型](./03_Types.md#type) +> *函数类型* → [类型](./03_Types.md#type) [函数类型子句](.03/Types.md#function-type-argument-clause) **async**可选 **throws**可选**->** [类型](./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)可选 [类型标识符](./03_Types.md#type-identifier) | [特性](./07_Attributes.md#attributes)可选 [类型标识符](./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)可选 [前缀表达式](./04_Expressions.md#prefix-expression) +> *中缀表达式* → [赋值操作符](./06_Declarations.md#class-declaration) [try 运算符](./04_Expressions.md#try-operator)可选 [前缀表达式](./04_Expressions.md#prefix-expression) > -> *二元表达式* → [条件运算符](./04_Expressions.md#conditional-operator) [try 运算符](./04_Expressions.md#try-operator)可选 [前缀表达式](./04_Expressions.md#prefix-expression) +> *中缀表达式* → [条件运算符](./04_Expressions.md#conditional-operator) [try 运算符](./04_Expressions.md#try-operator)可选 [前缀表达式](./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)可选 +> *中缀表达式* → [中缀表达式](./04_Expressions.md#infix-expression) [中缀表达式列表](./04_Expressions.md#infix-expressions)可选 > @@ -588,7 +613,7 @@ > *闭包表达式* → **{** [闭包签名](./04_Expressions.md#closure-signature)可选 [语句](./04_Expressions.md#statements) **}** > > -> 闭包签名* → [参数子句](./04_Expressions.md#parameter-clause) [函数结果](./06_Declarations.md#function-result)可选 **in** +> *闭包签名* → [捕获列表](./04_Expressions.md#capture-list) [参数子句](./04_Expressions.md#parameter-clause) **async**可选 **throws**可选 [函数结果](./06_Declarations.md#function-result)可选 **in** > > *闭包签名* → [标识符列表](./04_Expressions.md#identifier-list) [函数结果](./06_Declarations.md#function-result)可选 **in** > @@ -655,7 +680,7 @@ > *key-path 组件* → [标识符](./02_Lexical_Structure.md#identifier) [多个 key-path 后缀](./04_Expressions.md#key-path-postfixes)可选 | [多个 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)可选 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)可选
> -> *显式成员表达式* → [后缀表达式](./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)可选
> > *参数名* → [标识符](./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)可选 | **var** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer)可选 > @@ -978,7 +1006,7 @@ > > *catch 子句集* → [catch 子句](./05_Statements.md#catch-clause) [catch 子句集](05_Statements.md#catch-clauses)可选 > -> *catch 子句* → **catch** [catch 模式列表]()可选 [代码块](./06_Declarations.md#code-block)可选 +> *catch 子句* → **catch** [catch 模式列表](./05_Statements.md#catch-pattern-list)可选 [代码块](./06_Declarations.md#code-block)可选 > > *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**可选 [函数结果](./06_Declarations.md#function-result)可选 +> *函数签名* → [参数子句](./06_Declarations.md#parameter-clause) **async**可选 **throws**可选 [函数结果](./06_Declarations.md#function-result)可选 > -> *函数签名* → [参数子句](./06_Declarations.md#parameter-clause) **rethrows** [函数结果](./06_Declarations.md#function-result)可选 +> *函数签名* → [参数子句](./06_Declarations.md#parameter-clause) **async**可选 **rethrows** [函数结果](./06_Declarations.md#function-result)可选 > > *函数结果* → **->** [特性](./07_Attributes.md#attributes)可选 [类型](./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)可选 > @@ -1531,6 +1563,8 @@ > > *可变性修饰符* → **mutating** | **nonmutating** > +> *actor 隔离修饰符* → **nonisolated** +> ## 属性 {#attributes} diff --git a/source/04_revision_history/04_revision_history.md b/source/04_revision_history/04_revision_history.md index ffab1389..a2df4075 100644 --- a/source/04_revision_history/04_revision_history.md +++ b/source/04_revision_history/04_revision_history.md @@ -1,5 +1,18 @@ # Swift 文档修订历史 +### 2022-06-06 + +* 更新至 Swift 5.7。 +* 新增 [可发送类型](../02_language_guide/28_Concurrency.md#Sendable-Types) 章节,其中包含在 actors 和任务间发送数据的内容。在 [可发送](03_language_reference/07_Attributes.md#Sendable) 章节和 [unchecked](03_language_reference/07_Attributes.md#unchecked) 章节新增了有关特性 `@Sendable` 和 `@unchecked` 的内容。 +* 新增了 [正则表达式字面量](03_language_reference/02_Lexical_Structure.md#regular-expression-literals) 章节,其中包含新建正则表达式的内容。 +* 在 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding) 章节新增了 `if-let` 结构更简短的一种形式。 +* 在 [检查 API 可用性](../02_language_guide/05_Control_Flow.md#checking-api-availability) 章节新增了与不可用性检查 `#unavaliable` 有关的内容。 + +### 2022-03-14 + +* 更新至 Swift 5.6 +* 更新了 [显式成员表达式](../03_language_reference/04_Expressions.md#explicit-member-expression) 章节,现在可以在链式调用方法和其它后缀表达式周围使用 `#if` 了。 + ### 2021-07-14 * 更新 [并发](../02_language_guide/28_Concurrency.md) 篇章里的示例,使用当前的并发 API。