# 表達式(Expressions)
Swift 中存在四種表達式:前綴表達式,二元表達式,基本表達式和后綴表達式。表達式在返回一個值的同時還可以引發副作用。
通過前綴表達式和二元表達式可以對簡單表達式使用各種運算符。基本表達式從概念上講是最簡單的一種表達式,它是一種訪問值的方式。后綴表達式則允許你建立復雜的表達式,例如函數調用和成員訪問。每種表達式都在下面有詳細論述。
> 表達式語法
>
#### expression {#expression}
> *表達式* → [try 運算符](#try-operator)<sub>可選</sub> [前綴表達式](#prefix-expression) [二元表達式列表](#binary-expressions)<sub>可選</sub>
#### expression-list {#expression-list}
> *表達式列表* → [表達式](#expression) | [表達式](#expression) **,** [表達式列表](#expression-list)
>
## 前綴表達式 {#prefix-expressions}
前綴表達式由可選的前綴運算符和表達式組成。前綴運算符只接收一個參數,表達式則緊隨其后。
關于這些運算符的更多信息,請參閱 [基本運算符](../02_language_guide/02_Basic_Operators.md) 和 [高級運算符](../02_language_guide/27_Advanced_Operators.md)。
關于 Swift 標準庫提供的運算符的更多信息,請參閱 [*Operators Declarations*](https://developer.apple.com/documentation/swift/operator_declarations)。
除了標準庫運算符,你也可以對某個變量使用 `&` 運算符,從而將其傳遞給函數的輸入輸出參數。更多信息,請參閱 [輸入輸出參數](../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)
>
#### in-out-expression {#in-out-expression}
> *輸入輸出表達式* → **&** [標識符](./02_Lexical_Structure.md#identifier)
>
### Try 運算符 {#try-operator}
try 表達式由 `try` 運算符加上緊隨其后的可拋出錯誤的表達式組成,形式如下:
> try `可拋出錯誤的表達式`
>
可選的 try 表達式由 `try?` 運算符加上緊隨其后的可拋出錯誤的表達式組成,形式如下:
> try? `可拋出錯誤的表達式`
>
如果可拋出錯誤的表達式沒有拋出錯誤,整個表達式返回的可選值將包含可拋出錯誤的表達式的返回值,否則,該可選值為 `nil`。
強制的 try 表達式由 `try!` 運算符加上緊隨其后的可拋出錯誤的表達式組成,形式如下:
> try! `可拋出錯誤的表達式`
>
如果可拋出錯誤的表達式拋出了錯誤,將會引發運行時錯誤。
在二元運算符左側的表達式被標記上 `try`、`try?` 或者 `try!` 時,這個運算符對整個二元表達式都產生作用。也就是說,你可以使用括號來明確運算符的作用范圍。
```swift
sum = try someThrowingFunction() + anotherThrowingFunction() // try 對兩個函數調用都產生作用
sum = try (someThrowingFunction() + anotherThrowingFunction()) // try 對兩個函數調用都產生作用
sum = (try someThrowingFunction()) + anotherThrowingFunction() // 錯誤:try 只對第一個函數調用產生作用
```
`try` 表達式不能出現在二元運算符的的右側,除非二元運算符是賦值運算符或者 `try` 表達式是被圓括號括起來的。
關于 `try`、`try?` 和 `try!` 的更多信息,以及該如何使用的例子,請參閱 [錯誤處理](../02_language_guide/17_Error_Handling.md)。
> Try 表達式語法
>
#### try-operator {#try-operator}
> *try 運算符* → **try** | **try?** | **try!**
>
## 二元表達式 {#binary-expressions}
*二元表達式*由中綴運算符和左右參數表達式組成。形式如下:
> `左側參數` `二元運算符` `右側參數`
>
關于這些運算符的更多信息,請參閱 [基本運算符](../02_language_guide/02_Basic_Operators.md) 和 [高級運算符](../02_language_guide/27_Advanced_Operators.md)。
關于 Swift 標準庫提供的運算符的更多信息,請參閱 [*Swift Standard Library Operators Reference*](https://developer.apple.com/documentation/swift/operator_declarations)。
> 注意
>
> 在解析時,一個二元表達式將作為一個扁平列表表示,然后根據運算符的優先級,再進一步進行組合。例如,`2 + 3 * 5` 首先被看作具有五個元素的列表,即 `2`、`+`、`3`、`*`、`5`,隨后根據運算符優先級組合為 `(2 + (3 * 5))`。
>
#### binary-expression {#binary-expression}
> 二元表達式語法
>
> *二元表達式* → [二元運算符](./02_Lexical_Structure.md#binary-operator) [前綴表達式](#prefix-expression)
>
> *二元表達式* → [賦值運算符](#assignment-operator) [try 運算符](#try-operator)<sub>可選</sub> [前綴表達式](#prefix-expression)
>
> *二元表達式* → [條件運算符](#conditional-operator) [try 運算符](#try-operator)<sub>可選</sub> [前綴表達式](#prefix-expression)
>
> *二元表達式* → [類型轉換運算符](#type-casting-operator)
>
#### binary-expressions {#binary-expressions}
> *二元表達式列表* → [二元表達式](#binary-expression) [二元表達式列表](#binary-expressions)<sub>可選</sub>
>
### 賦值表達式 {#assignment-operator}
賦值表達式會為某個給定的表達式賦值,形式如下;
> `表達式` = `值`
>
右邊的值會被賦值給左邊的表達式。如果左邊表達式是一個元組,那么右邊必須是一個具有同樣元素個數的元組。(嵌套元組也是允許的。)右邊的值中的每一部分都會被賦值給左邊的表達式中的相應部分。例如:
```swift
(a, _, (b, c)) = ("test", 9.45, (12, 3))
// a 為 "test",b 為 12,c 為 3,9.45 會被忽略
```
賦值運算符不返回任何值。
> 賦值運算符語法
>
#### 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!`。它們有如下的形式:
> `表達式` is `類型`
>
> `表達式` as `類型`
>
> `表達式` as? `類型`
>
> `表達式` as! `類型`
>
`is` 運算符在運行時檢查表達式能否向下轉化為指定的類型,如果可以則返回 `ture`,否則返回 `false`。
`as` 運算符在編譯時執行向上轉換和橋接。向上轉換可將表達式轉換成父類的實例而無需使用任何中間變量。以下表達式是等價的:
```swift
func f(any: Any) { print("Function for Any") }
func f(int: Int) { print("Function for Int") }
let x = 10
f(x)
// 打印“Function for Int”
let y: Any = x
f(y)
// 打印“Function for Any”
f(x as Any)
// 打印“Function for Any”
```
橋接可將 Swift 標準庫中的類型(例如 `String`)作為一個與之相關的 Foundation 類型(例如 `NSString`)來使用,而不需要新建一個實例。關于橋接的更多信息,請參閱 [*Working with Foundation Types*](https://developer.apple.com/documentation/swift/imported_c_and_objective_c_apis/working_with_foundation_types)。
`as?` 運算符有條件地執行類型轉換,返回目標類型的可選值。在運行時,如果轉換成功,返回的可選值將包含轉換后的值,否則返回 `nil`。如果在編譯時就能確定轉換一定會成功或是失敗,則會導致編譯報錯。
`as!` 運算符執行強制類型轉換,返回目標類型的非可選值。如果轉換失敗,則會導致運行時錯誤。表達式 `x as! T` 效果等同于 `(x as? T)!`。
關于類型轉換的更多內容和例子,請參閱 [類型轉換](../02_language_guide/18_Type_Casting.md)。
#### type-casting-operator {#type-casting-operator}
> 類型轉換運算符語法
>
> *類型轉換運算符* → **is** [類型](./03_Types.md#type)
>
> *類型轉換運算符* → **as** [類型](./03_Types.md#type)
>
> *類型轉換運算符* → **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>
>
> *基本表達式* → [字面量表達式](#literal-expression)
>
> *基本表達式* → [self 表達式](#self-expression)
>
> *基本表達式* → [父類表達式](#superclass-expression)
>
> *基本表達式* → [閉包表達式](#closure-expression)
>
> *基本表達式* → [圓括號表達式](#parenthesized-expression)
>
> *基本表達式* → [隱式成員表達式](#implicit-member-expression)
>
> *基本表達式* → [通配符表達式](#wildcard-expression)
>
> *基本表達式* → [選擇器表達式](#selector-expression)
>
> *基本表達式* → [key-path字符串表達式](#key-patch-string-expression)
>
### 字面量表達式 {#literal-expression}
*字面量表達式*可由普通字面量(例如字符串或者數字),字典或者數組字面量,或者下面列表中的特殊字面量組成:
字面量 | 類型 | 值
:------------- | :---------- | :----------
`#file` | `String` | 所在的文件名及模塊
`#filePath` | `String` | 所在的文件路徑
`#line` | `Int` | 所在的行數
`#column` | `Int` | 所在的列數
`#function` | `String` | 所在的聲明的名字
`#dsohandle` | `UnsafeRawPointer` | 所使用的 DSO(動態共享對象)句柄
`#file` 表達式的值的格式是 *module*/*file*,*file* 是表達式所在的文件名,*module* 是文件所所在的模塊名。`#filePath` 表達式的字符串值是表達式所在的文件在整個文件系統里的路徑。所有這些值可以被 `#sourceLocation` 改變,詳見 [行控制語句](./05_Statements.md#line-control-statements)。
> 注意
>
> 要解析 `#file` 表達式,第一個斜杠(/)之前的文本作為模塊名,最后一個斜杠之后的文本作為文件名。將來,該字符串可能包含多個斜杠,例如 `MyModule/some/disambiguation/MyFile.swift`。
對于 `#function`,在函數中會返回當前函數的名字,在方法中會返回當前方法的名字,在屬性的存取器中會返回屬性的名字,在特殊的成員如 `init` 或 `subscript` 中會返回這個關鍵字的名字,在某個文件中會返回當前模塊的名字。
當其作為函數或者方法的默認參數值時,該字面量的值取決于函數或方法的調用環境。
```swift
func logFunctionName(string: String = #function) {
print(string)
}
func myFunction() {
logFunctionName()
}
myFunction() // 打印“myFunction()”
```
數組字面量是值的有序集合,形式如下:
> [`值 1`, `值 2`, `...`]
>
數組中的最后一個表達式可以緊跟一個逗號。數組字面量的類型是 `[T]`,這個 `T` 就是數組中元素的類型。如果數組中包含多種類型,`T` 則是跟這些類型最近的的公共父類型。空數組字面量由一組方括號定義,可用來創建特定類型的空數組。
```swift
var emptyArray: [Double] = []
```
字典字面量是一個包含無序鍵值對的集合,形式如下:
> [`鍵 1` : `值 1`, `鍵 2` : `值 2`, `...`]
>
字典中的最后一個表達式可以緊跟一個逗號。字典字面量的類型是 `[Key : Value]`,`Key` 表示鍵的類型,`Value` 表示值的類型。如果字典中包含多種類型,那么 `Key` 表示的類型則為所有鍵最接近的公共父類型,`Value` 與之相似。一個空的字典字面量由方括號中加一個冒號組成(`[:]`),從而與空數組字面量區分開,可以使用空字典字面量來創建特定類型的字典。
```swift
var emptyDictionary: [String : Double] = [:]
```
Xcode 使用 playground 字面量對程序編輯器中的顏色、文件或者圖片創建可交互的展示。在 Xcode 之外的空白文本中,playground 字面量使用一種特殊的字面量語法來展示。
更多關于在 Xcode 中使用 playground 字面量的信息,請參閱 [添加顏色、文件或圖片字面量](https://help.apple.com/xcode/mac/current/#/dev4c60242fc)。
> 字面量表達式語法
>
>
>
#### literal-expression {#literal-expression}
>
> *字面量表達式* → [字面量](./02_Lexical_Structure.md#literal)
>
> *字面量表達式* → [數組字面量](#array-literal) | [字典字面量](#dictionary-literal) | [練習場字面量](#playground-literal)
>
> *字面量表達式* → **#file** | **#filePath** | **#line** | **#column** | **#function**
>
>
#### array-literal {#array-literal}
>
> *數組字面量* → [[數組字面量項列表](#array-literal-items)<sub>可選</sub> **]**
>
>
#### array-literal-items {#array-literal-items}
>
> *數組字面量項列表* → [數組字面量項](#array-literal-item) **,**<sub>可選</sub> | [數組字面量項](#array-literal-item) **,** [數組字面量項列表](#array-literal-items)
>
>
#### array-literal-item {#array-literal-item}
>
> *數組字面量項* → [表達式](#expression)
>
>
>
#### dictionary-literal {#dictionary-literal}
>
> *字典字面量* → [[字典字面量項列表](#dictionary-literal-items) **]** | **[** **:** **]**
>
>
#### dictionary-literal-items {#dictionary-literal-items}
>
> *字典字面量項列表* → [字典字面量項](#dictionary-literal-item) **,**<sub>可選</sub> | [字典字面量項](#dictionary-literal-item) **,** [字典字面量項列表](#dictionary-literal-items)
>
>
#### dictionary-literal-item {#dictionary-literal-item}
>
> *字典字面量項* → [表達式](#expression) **:** [表達式](#expression)。
>
>
#### playground-literal {#playground-literal}
>
> *playground 字面量* → **#colorLiteral ( red : [表達式](#expression) , green :[表達式](#expression) [表達式](#e[*表達式*](#expression) xpression) , blue :[表達式](#expression) , alpha : [表達式](#expression) )**
>
> *playground 字面量* → **#fileLiteral ( resourceName : [表達式](#expression) )**
>
> #### playground 字面量* → **#imageLiteral ( resourceName : [表達式](#expression) )**self-expression {#self-expression}
### Self 表達式
`self` 表達式是對當前類型或者當前實例的顯式引用,它有如下形式:
> self
>
> self.`成員名稱`
>
> self[`下標索引`]
>
> self(`構造器參數`)
>
> self.init(`構造器參數`)
>
如果在構造器、下標、實例方法中,`self` 引用的是當前類型的實例。在一個類型方法中,`self` 引用的是當前的類型。
當訪問成員時,`self` 可用來區分重名變量,例如函數的參數:
```swift
class SomeClass {
var greeting: String
init(greeting: String) {
self.greeting = greeting
}
}
```
在 `mutating` 方法中,你可以對 `self` 重新賦值:
```swift
struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
```
> Self 表達式語法
>
#### self-expression {#self-expression}
> *self 表達式* → **self** | [self 方法表達式](#self-method-expression) | [self 下標表達式](#self-subscript-expression) | [self 構造器表達式](#self-initializer-expression)
>
>
#### 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}
*父類*表達式可以使我們在某個類中訪問它的父類,它有如下形式:
> super.`成員名稱`
>
> 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* 或*匿名*函數。跟函數一樣,閉包包含了待執行的代碼,不同的是閉包還會捕獲所在環境中的常量和變量。它的形式如下:
```swift
{ (parameters) -> return type in
statements
}
```
閉包的參數聲明形式跟函數一樣,請參閱 [函數聲明](./06_Declarations.md#function-declaration)。
閉包還有幾種特殊的形式,能讓閉包使用起來更加簡潔:
- 閉包可以省略它的參數和返回值的類型。如果省略了參數名和所有的類型,也要省略 `in` 關鍵字。如果被省略的類型無法被編譯器推斷,那么就會導致編譯錯誤。
- 閉包可以省略參數名,參數會被隱式命名為 `$` 加上其索引位置,例如 `$0`、`$1`、`$2` 分別表示第一個、第二個、第三個參數,以此類推。
- 如果閉包中只包含一個表達式,那么該表達式的結果就會被視為閉包的返回值。表達式結果的類型也會被推斷為閉包的返回類型。
下面幾個閉包表達式是等價的:
```swift
myFunction {
(x: Int, y: Int) -> Int in
return x + y
}
myFunction {
(x, y) in
return x + y
}
myFunction { return $0 + $1 }
myFunction { $0 + $1 }
```
關于如何將閉包作為參數來傳遞的內容,請參閱 [函數調用表達式](#function-call-expression)。
使用閉包表達式時,可以不必將其存儲在一個變量或常量中,例如作為函數調用的一部分來立即使用一個閉包。在上面的例子中,傳入 `myFunction` 的閉包表達式就是這種立即使用類型的閉包。因此,一個閉包是否逃逸與其使用時的上下文相關。一個會被立即調用或者作為函數的非逃逸參數傳遞的閉包表達式是非逃逸的,否則,這個閉包表達式是逃逸的。
關于逃逸閉包的內容,請參閱 [逃逸閉包](../02_language_guide/07_Closures.md#escaping-closures)。
## 捕獲列表 {#capture-lists}
默認情況下,閉包會捕獲附近作用域中的常量和變量,并使用強引用指向它們。你可以通過一個*捕獲列表*來顯式指定它的捕獲行為。
捕獲列表在參數列表之前,由中括號括起來,里面是由逗號分隔的一系列表達式。一旦使用了捕獲列表,就必須使用 `in` 關鍵字,即使省略了參數名、參數類型和返回類型。
捕獲列表中的項會在閉包創建時被初始化。每一項都會用閉包附近作用域中的同名常量或者變量的值初始化。例如下面的代碼示例中,捕獲列表包含 `a` 而不包含 `b`,這將導致這兩個變量具有不同的行為。
```swift
var a = 0
var b = 0
let closure = { [a] in
print(a, b)
}
a = 10
b = 10
closure()
// 打印“0 10”
```
在示例中,變量 `b` 只有一個,然而,變量 `a` 有兩個,一個在閉包外,一個在閉包內。閉包內的變量 `a` 會在閉包創建時用閉包外的變量 `a` 的值來初始化,除此之外它們并無其他聯系。這意味著在閉包創建后,改變某個 `a` 的值都不會對另一個 `a` 的值造成任何影響。與此相反,閉包內外都是同一個變量 `b`,因此在閉包外改變其值,閉包內的值也會受影響。
如果閉包捕獲的值具有引用語義則有所不同。例如,下面示例中,有兩個變量 `x`,一個在閉包外,一個在閉包內,由于它們的值是引用語義,雖然這是兩個不同的變量,它們卻都引用著同一實例。
```swift
class SimpleClass {
var value: Int = 0
}
var x = SimpleClass()
var y = SimpleClass()
let closure = { [x] in
print(x.value, y.value)
}
x.value = 10
y.value = 10
closure()
// 打印“10 10”
```
如果捕獲列表中的值是類類型,你可以使用 `weak` 或者 `unowned` 來修飾它,閉包會分別用弱引用和無主引用來捕獲該值。
```swift
myFunction { print(self.title) } // 隱式強引用捕獲
myFunction { [self] in print(self.title) } // 顯式強引用捕獲
myFunction { [weak self] in print(self!.title) } // 弱引用捕獲
myFunction { [unowned self] in print(self.title) } // 無主引用捕獲
```
在捕獲列表中,也可以將任意表達式的值綁定到一個常量上。該表達式會在閉包被創建時進行求值,閉包會按照指定的引用類型來捕獲表達式的值。例如:
```swift
// 以弱引用捕獲 self.parent 并賦值給 parent
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}
>
>
> 閉包簽名* → [參數子句](#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**
>
>
>
#### capture-list {#capture-list}
>
>
> 捕獲列表* → [ [捕獲列表項列表](#capture-list-items) **]**
>
>
#### capture-list-items {#capture-list-items}
>
> *捕獲列表項列表* → [捕獲列表項](#capture-list-item) | [捕獲列表項](#capture-list-item) **,** [捕獲列表項列表](#capture-list-items)
>
>
#### capture-list-item {#capture-list-item}
>
> *捕獲列表項* → [捕獲說明符](#capture-specifier)<sub>可選</sub> [表達式](#expression)
>
>
#### capture-specifier {#capture-specifier}
>
> *捕獲說明符* → **weak** | **unowned** | **unowned(safe)** | **unowned(unsafe)**
>
### 隱式成員表達式 {#implicit-member-expression}
若類型可被推斷出來,可以使用*隱式成員表達式*來訪問某個類型的成員(例如某個枚舉成員或某個類型方法),形式如下:
> .`成員名稱`
>
例如:
```swift
var x = MyEnumeration.SomeValue
x = .AnotherValue
```
> 隱式成員表達式語法
>
#### 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` 出現了兩次,但有一次在外層元組里,一次在內層元組里。
元組表達式可以一個表達式都沒有,也可以包含兩個或是更多的表達式。單個表達式用括號括起來就是括號表達式了。
> 注意
>
>
> 在 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` 則被忽略:
```swift
(x, _) = (10, 20)
// x 為 10,20 被忽略
```
> 通配符表達式語法
>
#### wildcard-expression {#wildcard-expression}
> *通配符表達式* → **_**
>
### Key-path 表達式 {#key-path-expression}
Key-path 表達式引用一個類型的屬性或下標。在動態語言中使場景可以使用 Key-path 表達式,例如觀察鍵值對。格式為:
> **\類型名.路徑**
>
*類型名*是一個具體類型的名稱,包含任何泛型參數,例如 `String`、`[Int]` 或 `Set<Int>`。
*路徑*可由屬性名稱、下標、可選鏈表達式或者強制解包表達式組成。以上任意 key-path 組件可以以任何順序重復多次。
在編譯期,key-path 表達式會被一個 [KeyPath](https://developer.apple.com/documentation/swift/keypath) 類的實例替換。
對于所有類型,都可以通過傳遞 key-path 參數到下標方法 `subscript(keyPath:)` 來訪問它的值。例如:
```swift
struct SomeStructure {
var someValue: Int
}
let s = SomeStructure(someValue: 12)
let pathToProperty = \SomeStructure.someValue
let value = s[keyPath: pathToProperty]
// 值為 12
```
在一些可以通過類型推斷來確定所訪問的具體類型的上下文中,可以省略 key-path 前的類型名字。下面的代碼使用 `\.someProperty` 代替了 `SomeClass.someProperty` :
```swift
class SomeClass: NSObject {
@objc dynamic var someProperty: Int
init(someProperty: Int) {
self.someProperty = someProperty
}
}
let c = SomeClass(someProperty: 10)
c.observe(\.someProperty) { object, change in
// ...
}
```
使用 `self` 作為路徑可以創建一個恒等 key path (`\.self`)。恒等 key path 可以作為整個實例的引用,因此你僅需一步操作便可以利用它來訪問以及修改其存儲的所有數據。例如:
```swift
var compoundValue = (a: 1, b: 2)
// 等價于 compoundValue = (a: 10, b: 20)
compoundValue[keyPath: \.self] = (a: 10, b: 20)
```
通過點語法,可以讓路徑包含多個屬性名稱,以此來訪問某實例的屬性的屬性。下面的代碼使用 key-path 表達式 `\OuterStructure.outer.someValue` 來訪問 `OuterStructure` 類型中 `outer` 屬性的 `someValue` 屬性。
```swift
struct OuterStructure {
var outer: SomeStructure
init(someValue: Int) {
self.outer = SomeStructure(someValue: someValue)
}
}
let nested = OuterStructure(someValue: 24)
let nestedKeyPath = \OuterStructure.outer.someValue
let nestedValue = nested[keyPath: nestedKeyPath]
// nestedValue 的值為 24
```
路徑中也可以包含使用中括號的下標訪問,只要下標訪問的參數類型滿足 `Hashable` 協議即可。下面的例子在 key path 中使用了下標來訪問數組的第二個元素。
```swift
let greetings = ["hello", "hola", "bonjour", "??"]
let myGreeting = greetings[keyPath: \[String].[1]]
// myGreeting 的值為 'hola'
```
下標訪問中使用的值可以是一個變量或者字面量,并且 key-path 表達式會使用值語義來捕獲此值。下面的代碼在 key-path 表達式和閉包中都使用了 `index` 變量來訪問 `greetings` 數組的第三個元素。當 `index` 被修改時,key-path 表達式仍舊引用數組第三個元素,而閉包則使用了新的索引值。
```swift
var index = 2
let path = \[String].[index]
let fn: ([String]) -> String = { strings in strings[index] }
print(greetings[keyPath: path])
// 打印 "bonjour"
print(fn(greetings))
// 打印 "bonjour"
// 將 'index' 設置為一個新的值不會影響到 'path'
index += 1
print(greetings[keyPath: path])
// 打印 "bonjour"
// 'fn' 閉包使用了新值。
print(fn(greetings))
// 打印 "??"
```
路徑可以使用可選鏈和強制解包。下面的代碼在 key path 中使用了可選鏈來訪問可選字符串的屬性。
```swift
let firstGreeting: String? = greetings.first
print(firstGreeting?.count as Any)
// 打印 "Optional(5)"
// 使用 key path 實現同樣的功能
let count = greetings[keyPath: \[String].first?.count]
print(count as Any)
// 打印 "Optional(5)"
```
可以混合使用各種 key path 組件來訪問一些深度嵌套類型的值。下面的代碼通過組合不同的組件,使用 key-path 表達式訪問了一個字典數組中不同的值和屬性。
```swift
let interestingNumbers = ["prime": [2, 3, 5, 7, 11, 13, 17],
"triangular": [1, 3, 6, 10, 15, 21, 28],
"hexagonal": [1, 6, 15, 28, 45, 66, 91]]
print(interestingNumbers[keyPath: \[String: [Int]].["prime"]] as Any)
// 打印 "Optional([2, 3, 5, 7, 11, 13, 17])"
print(interestingNumbers[keyPath: \[String: [Int]].["prime"]![0]])
// 打印 "2"
print(interestingNumbers[keyPath: \[String: [Int]].["hexagonal"]!.count])
// 打印 "7"
print(interestingNumbers[keyPath: \[String: [Int]].["hexagonal"]!.count.bitWidth])
// 打印 "64"
```
你可以在平時提供函數或者閉包的上下文里使用 key path 表達式。特別地,你可以用根類型是 `SomeType` 和路徑產生 `Value` 類型值的 key path 表達式來替換類型是 `(SomeType) -> Value` 的函數或者閉包。
```swift
struct Task {
var description: String
var completed: Bool
}
var toDoList = [
Task(description: "Practice ping-pong.", completed: false),
Task(description: "Buy a pirate costume.", completed: true),
Task(description: "Visit Boston in the Fall.", completed: false),
]
// 下面兩種寫法是等價的。
let descriptions = toDoList.filter(\.completed).map(\.description)
let descriptions2 = toDoList.filter { $0.completed }.map { $0.description }
```
任何 key path 表達式的副作用發生的關鍵在于表達式在哪里被執行。例如,如果你在 key path 表達式中的一個下標里使用函數調用,該函數只會在表達式計算的時候調用一次,而不是每次這個 key path 被使用的時候。
```swift
func makeIndex() -> Int {
print("Made an index")
return 0
}
// 下面這行調用 makeIndex()。
let taskKeyPath = \[Task][makeIndex()]
// 打印 "Made an index"
// 使用 taskKeyPath 不會再次調用 makeIndex()。
let someTask = toDoList[keyPath: taskKeyPath]
```
關于更多如何使用 key path 與 Objective-C APIs 交互的信息,請參閱 [在 Swift 中使用 Objective-C 運行時特性](https://developer.apple.com/documentation/swift/using_objective_c_runtime_features_in_swift)。關于更多 key-value 編程和 key-value 觀察的信息,請參閱 [Key-Value 編程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueCoding/index.html#//apple-ref/doc/uid/10000107i) 和 [Key-Value 觀察編程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html#//apple-ref/doc/uid/10000177i)。
> key-path 表達式語法
>
>
>
#### key-path-expression {#key-path-expression}
>
> *key-path 表達式* → **\\** [類型](./03_Types.md#type)<sub>可選</sub> **.** [多個 key-path 組件](#key-path-components)
>
>
#### key-path-components {#key-path-components}
>
> *多個 key-path 組件* → [key-path 組件](#key-path-component) | [key-path 組件](#key-path-component) **.** [多個 key-path 組件](#key-path-components)
>
>
#### key-path-component {#key-path-component}
>
> *key-path 組件* → [標識符](./02_Lexical_Structure.md#identifier) [多個 key-path 后綴](#key-path-postfixes)<sub>可選<sub> | [多個 key-path 后綴](#key-path-postfixes)
>
>
#### key-path-postfixes {#key-path-postfixes}
>
>
#### *多個 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) **\]**
>
### 選擇器表達式 {#selector-expression}
*選擇器表達式*可以讓你通過選擇器來引用在 Objective-C 中方法(method)和屬性(property)的 setter 和 getter 方法。
> \#selector(方法名)
>
\#selector(getter: 屬性名)
\#selector(setter: 屬性名)
方法名和屬性名必須是存在于 Objective-C 運行時中的方法和屬性的引用。選擇器表達式的返回值是一個 Selector 類型的實例。例如:
```swift
class SomeClass: NSObject {
let property: String
@objc(doSomethingWithInt:)
func doSomething(_ x: Int) { }
init(property: String) {
self.property = property
}
}
let selectorForMethod = #selector(SomeClass.doSomething(-:))
let selectorForPropertyGetter = #selector(getter: SomeClass.property)
```
當為屬性的 getter 創建選擇器時,屬性名可以是變量屬性或者常量屬性的引用。但是當為屬性的 setter 創建選擇器時,屬性名只可以是對變量屬性的引用。
方法名稱可以包含圓括號來進行分組,并使用 as 操作符來區分具有相同方法名但類型不同的方法,例如:
```swift
extension SomeClass {
@objc(doSomethingWithString:)
func doSomething(_ x: String) { }
}
let anotherSelector = #selector(SomeClass.doSomething(-:) as (SomeClass) -> (String) -> Void)
```
由于選擇器是在編譯時創建的,因此編譯器可以檢查方法或者屬性是否存在,以及是否在運行時暴露給了 Objective-C 。
> 注意
>
> 雖然方法名或者屬性名是個表達式,但是它不會被求值。
>
更多關于如何在 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) **)**
>
> *選擇器表達式* → __#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 字符串表達式會被一個字符串字面量替換。例如:
```swift
class SomeClass: NSObject {
@objc var someProperty: Int
init(someProperty: Int) {
self.someProperty = someProperty
}
}
let c = SomeClass(someProperty: 12)
let keyPath = #keyPath(SomeClass.someProperty)
if let value = c.value(forKey: keyPath) {
print(value)
}
// 打印 "12"
```
當在一個類中使用 key-path 字符串表達式時,可以省略類名,直接使用屬性名來訪問這個類的某個屬性。
```swift
extension SomeClass {
func getSomeKeyPath() -> String {
>
return #keyPath(someProperty)
}
}
print(keyPath == c.getSomeKeyPath())
// 打印 "true"
```
由于 key-path 字符串表達式在編譯期才創建,編譯期可以檢查屬性是否存在,以及屬性是否暴露給 Objective-C 運行時。
關于更多如何使用 key path 與 Objective-C APIs 交互的信息,請參閱 [在 Swift 中使用 Objective-C 運行時特性](https://developer.apple.com/documentation/swift/using_objective_c_runtime_features_in_swift)。關于更多 key-value 編程和 key-value 觀察的信息,請參閱 [Key-Value 編程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueCoding/index.md#//apple-ref/doc/uid/10000107i) 和 [Key-Value 觀察編程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.md#//apple-ref/doc/uid/10000177i)。
> 注意
>
>
> 盡管*屬性名*是一個表達式,但它永遠不會被求值
>
> key-path 字符串表達式語法
>
>
#### key-path-string-expression {#key-path-string-expression}
>
> *key-path 字符串表達式* → **#keyPath (** [表達式](#expression) **)**
>
## 后綴表達式 {#postfix-expressions}
*后綴表達式*就是在某個表達式的后面運用后綴運算符或其他后綴語法。從語法構成上來看,基本表達式也是后綴表達式。
關于這些運算符的更多信息,請參閱 [基本運算符](../02_language_guide/02_Basic_Operators.md) 和 [高級運算符](../02_language_guide/27_Advanced_Operators.md)。
關于 Swift 標準庫提供的運算符的更多信息,請參閱 [運算符定義](https://developer.apple.com/documentation/swift/operator_declarations)。
> 后綴表達式語法
>
#### postfix-expression {#postfix-expression}
> *后綴表達式* → [基本表達式](#primary-expression)
>
> *后綴表達式* → [后綴表達式](#postfix-expression) [后綴運算符](./02_Lexical_Structure.md#postfix-operator)
>
> *后綴表達式* → [函數調用表達式](#function-call-expression)
>
> *后綴表達式* → [構造器表達式](#initializer-expression)
>
> *后綴表達式* → [顯式成員表達式](#explicit-member-expression)
>
> *后綴表達式* → [后綴 self 表達式](#postfix-self-expression)
>
> *后綴表達式* → [dynamicType 表達式](#dynamic-type-expression)
>
> *后綴表達式* → [下標表達式](#subscript-expression)
>
> *后綴表達式* → [強制取值表達式](#forced-value-expression)
>
> *后綴表達式* → [可選鏈表達式](#optional-chaining-expression)
>
### 函數調用表達式 {#function-call-expression}
*函數調用表達式*由函數名和參數列表組成,形式如下:
> `函數名`(`參數 1`, `參數 2`)
>
函數名可以是值為函數類型的任意表達式。
如果函數聲明中指定了參數的名字,那么在調用的時候也必須得寫出來。這種函數調用表達式具有以下形式:
> `函數名`(`參數名 1`: `參數 1`, `參數名 2`: `參數 2`)
如果函數的最后一個參數是函數類型,可以在函數調用表達式的尾部(右圓括號之后)加上一個閉包,該閉包會作為函數的最后一個參數。如下兩種寫法是等價的:
```swift
// someFunction 接受整型和閉包的實參
someFunction(x, f: {$0 == 13})
someFunction(x) {$0 == 13}
// anotherFunction 接受一個整型和兩個閉包的實參
anotherFunction(x: x, f: { $0 == 13 }, g: { print(99) })
anotherFunction(x: x) { $0 == 13 } g: { print(99) }
```
如果閉包是該函數的唯一參數,那么圓括號可以省略。
```swift
// someFunction 只接受一個閉包參數
myData.someMethod() {$0 == 13}
myData.someMethod {$0 == 13}
```
如 [特殊名稱方法](./06_Declarations.md#methods-with-special-names) 所述,通過聲明幾種方法中的一種,類、結構體或枚舉類型可以為函數調用語法啟用語法糖。
> 函數調用表達式語法
>
>
#### 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-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)
>
> #### 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(`構造器參數`)
>
你可以在函數調用表達式中使用構造器表達式來初始化某個類型的新實例。也可以使用構造器表達式來代理給父類構造器。
```swift
class SomeSubClass: SomeSuperClass {
override init() {
// 此處為子類構造過程
super.init()
}
}
```
和函數類似,構造器表達式可以作為一個值。 例如:
```swift
// 類型注解是必須的,因為 String 類型有多種構造器
let initializer: Int -> String = String.init
let oneTwoThree = [1, 2, 3].map(initializer).reduce("", combine: +)
print(oneTwoThree)
// 打印“123”
```
如果通過名字來指定某個類型,可以不用構造器表達式而直接使用類型的構造器。在其他情況下,你必須使用構造器表達式。
```swift
let s1 = SomeType.init(data: 3) // 有效
let s2 = SomeType(data: 1) // 有效
let s4 = someValue.dynamicType(data: 5) // 錯誤
let s3 = someValue.dynamicType.init(data: 7) // 有效
```
> 構造器表達式語法
>
#### initializer-expression {#initializer-expression}
> *構造器表達式* → [后綴表達式](#postfix-expression) **.** **init**
>
> *構造器表達式* → [后綴表達式](#postfix-expression) **.** **init** **(** [參數名稱](#argument-names) **)**
>
### 顯式成員表達式 {#explicit-member-expression}
*顯式成員表達式*允許我們訪問命名類型、元組或者模塊的成員,其形式如下:
> `表達式`.`成員名`
>
命名類型的某個成員在原始實現或者擴展中定義,例如:
```swift
class SomeClass {
var someProperty = 42
}
let c = SomeClass()
let y = c.someProperty // 訪問成員
```
元組的成員會隱式地根據表示它們出現順序的整數來命名,以 0 開始,例如:
```swift
var t = (10, 20, 30)
t.0 = t.1
// 現在元組 t 為 (20, 20, 30)
```
對于模塊的成員來說,只能直接訪問頂級聲明中的成員。
使用 `dynamicMemberLookup` 屬性聲明的類型包含可以在運行時查找的成員,具體請參閱 [屬性](./07_Attributes.md)。
為了區分只有參數名有所不同的方法或構造器,在圓括號中寫出參數名,參數名后緊跟一個冒號,對于沒有參數名的參數,使用下劃線代替參數名。而對于重載方法,則需使用類型注解進行區分。例如:
```swift
class SomeClass {
func someMethod(x: Int, y: Int) {}
func someMethod(x: Int, z: Int) {}
func overloadedMethod(x: Int, y: Int) {}
func overloadedMethod(x: Int, y: Bool) {}
}
let instance = SomeClass()
let a = instance.someMethod // 有歧義
let b = instance.someMethod(_:y:) // 無歧義
let d = instance.overloadedMethod // 有歧義
let d = instance.overloadedMethod(_:y:) // 有歧義
let d: (Int, Bool) -> Void = instance.overloadedMethod(_:y:) // 無歧義
```
如果點號(`.`)出現在行首,它會被視為顯式成員表達式的一部分,而不是隱式成員表達式的一部分。例如如下代碼所展示的被分為多行的鏈式方法調用:
```swift
let x = [10, 3, 20, 15, 4]
.sort()
.filter { $0 > 5 }
.map { $0 * 100 }
```
> 顯式成員表達式語法
>
#### 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) **)**
#### 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` 組成,其形式如下:
> `表達式`.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`,則返回解包后的值。否則,拋出運行時錯誤。
返回的值可以被修改,無論是修改值本身,還是修改值的成員。例如:
```swift
var x: Int? = 0
x!++
// x 現在是 1
var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]
someDictionary["a"]![0] = 100
// someDictionary 現在是 [b: [10, 20], a: [100, 2, 3]]
```
> 強制取值語法
>
#### forced-value-expression {#forced-value-expression}
> *強制取值表達式* → [后綴表達式](#postfix-expression) **!**
>
### 可選鏈表達式 {#optional-chaining-expression}
*可選鏈表達式*提供了一種使用可選值的便捷方法,形式如下:
> `表達式`?
>
后綴 `?` 運算符會根據表達式生成可選鏈表達式而不會改變表達式的值。
如果某個后綴表達式包含可選鏈表達式,那么它的執行過程會比較特殊。如果該可選鏈表達式的值是 `nil`,整個后綴表達式會直接返回 `nil`。如果該可選鏈表達式的值不是 `nil`,則返回可選鏈表達式解包后的值,并將該值用于后綴表達式中剩余的表達式。在這兩種情況下,整個后綴表達式的值都會是可選類型。
如果某個后綴表達式中包含了可選鏈表達式,那么只有最外層的表達式會返回一個可選類型。例如,在下面的例子中,如果 `c` 不是 `nil`,那么它的值會被解包,然后通過 `.property` 訪問它的屬性,接著進一步通過 `.performAction()` 調用相應方法。整個 `c?.property.performAction()` 表達式返回一個可選類型的值,而不是多重可選類型。
```swift
var c: SomeClass?
var result: Bool? = c?.property.performAction()
```
上面的例子跟下面的不使用可選鏈表達式的例子等價:
```swift
var result: Bool? = nil
if let unwrappedC = c {
result = unwrappedC.property.performAction()
}
```
可選鏈表達式解包后的值可以被修改,無論是修改值本身,還是修改值的成員。如果可選鏈表達式的值為 `nil`,則表達式右側的賦值操作不會被執行。例如:
```swift
func someFunctionWithSideEffects() -> Int {
// 譯者注:為了能看出此函數是否被執行,加上了一句打印
print("someFunctionWithSideEffects")
return 42
}
var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]
someDictionary["not here"]?[0] = someFunctionWithSideEffects()
// someFunctionWithSideEffects 不會被執行
// someDictionary 依然是 ["b": [10, 20], "a": [1, 2, 3]]
someDictionary["a"]?[0] = someFunctionWithSideEffects()
// someFunctionWithSideEffects 被執行并返回 42
// someDictionary 現在是 ["b": [10, 20], "a": [42, 2, 3]]
```
> 可選鏈表達式語法
>
#### optional-chaining-expression {#optional-chaining-expression}
> *可選鏈表達式* → [后綴表達式](#postfix-expression) **?**
- 1.關于 Swift
- 2.Swift 初見
- 2-1基礎部分
- 2-2基本運算符
- 2-3字符串和字符
- 2-4集合類型
- 2-5控制流
- 2-6函數
- 2-7閉包
- 2-8枚舉
- 2-9類和結構體
- 2-10屬性
- 2-11方法
- 2-12下標
- 2-13繼承
- 2-14構造過程
- 2-15析構過程
- 2-16可選鏈
- 2-17錯誤處理
- 2-18類型轉換
- 2-19嵌套類型
- 2-20擴展
- 2-21協議
- 2-22泛型
- 2-23不透明類型
- 2-24自動引用計數
- 2-25內存安全
- 2-26訪問控制
- 2-27高級運算符
- 3-1關于語言參考
- 3-2詞法結構
- 3-3類型
- 3-4表達式
- 3-5語句
- 3-6聲明
- 3-7特性
- 3-8模式
- 3-9泛型參數
- 4語法總結