> 1.0 翻譯:[XieLingWang](https://github.com/xielingwang)?校對:[EvilCome](https://github.com/Evilcome)
>
> 2.0 翻譯+校對:[JackAlan](https://github.com/AlanMelody)
* * *
本頁包含內容:
[TOC=2]
運算符是檢查、改變、合并值的特殊符號或短語。例如,加號`+`將兩個數相加(如`let i = 1 + 2`)。更復雜的運算例子包括邏輯與運算符`&&`(如`if enteredDoorCode && passedRetinaScan`),或讓 i 值加1的便捷自增運算符`++i`等。
Swift 支持大部分標準 C 語言的運算符,且改進許多特性來減少常規編碼錯誤。如:賦值符(`=`)不返回值,以防止把想要判斷相等運算符(`==`)的地方寫成賦值符導致的錯誤。算術運算符(`+`,`-`,`*`,`/`,`%`等)會檢測并不允許值溢出,以此來避免保存變量時由于變量大于或小于其類型所能承載的范圍時導致的異常結果。當然允許你使用 Swift 的溢出運算符來實現溢出。詳情參見[溢出運算符](http://wiki.jikexueyuan.com/project/swift/chapter2/24_Advanced_Operators.html#overflow_operators)。
區別于 C 語言,在 Swift 中你可以對浮點數進行取余運算(`%`),Swift 還提供了 C 語言沒有的表達兩數之間的值的區間運算符(`a..<b`和`a...b`),這方便我們表達一個區間內的數值。
本章節只描述了 Swift 中的基本運算符,[高級運算符](http://wiki.jikexueyuan.com/project/swift/chapter2/24_Advanced_Operators.html)包含了高級運算符,及如何自定義運算符,及如何進行自定義類型的運算符重載。
## 術語
運算符有一元、二元和三元運算符。
* 一元運算符對單一操作對象操作(如`-a`)。一元運算符分前置運算符和后置運算符,前置運算符需緊跟在操作對象之前(如`!b`),后置運算符需緊跟在操作對象之后(如`i++`)。
* 二元運算符操作兩個操作對象(如`2 + 3`),是中置的,因為它們出現在兩個操作對象之間。
* 三元運算符操作三個操作對象,和 C 語言一樣,Swift 只有一個三元運算符,就是三目運算符(`a ? b : c`)。
受運算符影響的值叫操作數,在表達式`1 + 2`中,加號`+`是二元運算符,它的兩個操作數是值`1`和`2`。
## 賦值運算符
賦值運算(`a = b`),表示用`b`的值來初始化或更新`a`的值:
~~~
let b = 10
var a = 5
a = b
// a 現在等于 10
~~~
如果賦值的右邊是一個多元組,它的元素可以馬上被分解成多個常量或變量:
~~~
let (x, y) = (1, 2)
// 現在 x 等于 1, y 等于 2
~~~
與 C 語言和 Objective-C 不同,Swift 的賦值操作并不返回任何值。所以以下代碼是錯誤的:
~~~
if x = y {
// 此句錯誤, 因為 x = y 并不返回任何值
}
~~~
這個特性使你無法把(`==`)錯寫成(`=`),由于`if x = y`是錯誤代碼,Swift幫你避免此類錯誤的的發生。
## 算術運算符
Swift 中所有數值類型都支持了基本的四則算術運算:
* 加法(`+`)
* 減法(`-`)
* 乘法(`*`)
* 除法(`/`)
~~~
1 + 2 // 等于 3
5 - 3 // 等于 2
2 * 3 // 等于 6
10.0 / 2.5 // 等于 4.0
~~~
與 C 語言和 Objective-C 不同的是,Swift 默認情況下不允許在數值運算中出現溢出情況。但是你可以使用 Swift 的溢出運算符來實現溢出運算(如`a &+ b`)。詳情參見[溢出運算符](http://wiki.jikexueyuan.com/project/swift/chapter2/24_Advanced_Operators.html#overflow_operators)。
加法運算符也可用于`String`的拼接:
~~~
"hello, " + "world" // 等于 "hello, world"
~~~
### 求余運算符
求余運算(`a % b`)是計算`b`的多少倍剛剛好可以容入`a`,返回多出來的那部分(余數)。
> 注意:
> 求余運算(`%`)在其他語言也叫取模運算。然而嚴格說來,我們看該運算符對負數的操作結果,"求余"比"取模"更合適些。
我們來談談取余是怎么回事,計算`9 % 4`,你先計算出`4`的多少倍會剛好可以容入`9`中:

2倍,非常好,那余數是1(用橙色標出)
在 Swift 中可以表達為:
~~~
9 % 4 // 等于 1
~~~
為了得到`a % b`的結果,`%`計算了以下等式,并輸出`余數`作為結果:
~~~
a = (b × 倍數) + 余數
~~~
當`倍數`取最大值的時候,就會剛好可以容入`a`中。
把`9`和`4`代入等式中,我們得`1`:
~~~
9 = (4 × 2) + 1
~~~
同樣的方法,我來們計算?`-9 % 4`:
~~~
-9 % 4 // 等于 -1
~~~
把`-9`和`4`代入等式,`-2`是取到的最大整數:
~~~
-9 = (4 × -2) + -1
~~~
余數是`-1`。
在對負數`b`求余時,`b`的符號會被忽略。這意味著?`a % b`?和?`a % -b`的結果是相同的。
### 浮點數求余計算
不同于 C 語言和 Objective-C,Swift 中是可以對浮點數進行求余的。
~~~
8 % 2.5 // 等于 0.5
~~~
這個例子中,`8`除于`2.5`等于`3`余`0.5`,所以結果是一個`Double`值`0.5`。

### 自增和自減運算
和 C 語言一樣,Swift 也提供了對變量本身加1或減1的自增(`++`)和自減(`--`)的縮略算符。其操作對象可以是整形和浮點型。 ?
~~~
var i = 0
++i // 現在 i = 1
~~~
每調用一次`++i`,`i`的值就會加1。實際上,`++i`是`i = i + 1`的簡寫,而`--i`是`i = i - 1`的簡寫。
`++`和`--`既可以用作前置運算又可以用作后置運算。`++i`,`i++`,`--i`和`i--`都是有效的寫法。
我們需要注意的是這些運算符即可修改了`i`的值也可以返回`i`的值。如果你只想修改`i`的值,那你就可以忽略這個返回值。但如果你想使用返回值,你就需要留意前置和后置操作的返回值是不同的,她們遵循以下原則:
* 當`++`前置的時候,先自増再返回。
* 當`++`后置的時候,先返回再自增。
例如:
~~~
var a = 0
let b = ++a // a 和 b 現在都是 1
let c = a++ // a 現在 2, 但 c 是 a 自增前的值 1
~~~
上述例子,`let b = ++a`先把`a`加1了再返回`a`的值。所以`a`和`b`都是新值`1`。
而`let c = a++`,是先返回了`a`的值,然后`a`才加1。所以`c`得到了`a`的舊值1,而`a`加1后變成2。
除非你需要使用`i++`的特性,不然推薦你使用`++i`和`--i`,因為先修改后返回這樣的行為更符合我們的邏輯。
### 一元負號運算符
數值的正負號可以使用前綴`-`(即一元負號)來切換:
~~~
let three = 3
let minusThree = -three // minusThree 等于 -3
let plusThree = -minusThree // plusThree 等于 3, 或 "負負3"
~~~
一元負號(`-`)寫在操作數之前,中間沒有空格。
### 一元正號運算符
一元正號(`+`)不做任何改變地返回操作數的值。
~~~
let minusSix = -6
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
~~~
雖然一元`+`什么都不會改變,但當你在使用一元負號來表達負數時,你可以使用一元正號來表達正數,如此你的代碼會具有對稱美。
## 復合賦值(Compound Assignment Operators)
如同 C 語言,Swift 也提供把其他運算符和賦值運算(`=`)組合的復合賦值運算符,組合加運算(`+=`)是其中一個例子:
~~~
var a = 1
a += 2 // a 現在是 3
~~~
表達式`a += 2`是`a = a + 2`的簡寫,一個組合加運算就是把加法運算和賦值運算組合成進一個運算符里,同時完成兩個運算任務。
> 注意:
> 復合賦值運算沒有返回值,`let b = a += 2`這類代碼是錯誤。這不同于上面提到的自增和自減運算符。
在[表達式](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html)章節里有復合運算符的完整列表。 ?
## 比較運算符
所有標準 C 語言中的比較運算都可以在 Swift 中使用。
* 等于(`a == b`)
* 不等于(`a != b`)
* 大于(`a > b`)
* 小于(`a < b`)
* 大于等于(`a >= b`)
* 小于等于(`a <= b`)
> 注意: Swift 也提供恒等`===`和不恒等`!==`這兩個比較符來判斷兩個對象是否引用同一個對象實例。更多細節在[類與結構](http://wiki.jikexueyuan.com/project/swift/chapter2/09_Classes_and_Structures.html)。
每個比較運算都返回了一個標識表達式是否成立的布爾值:
~~~
1 == 1 // true, 因為 1 等于 1
2 != 1 // true, 因為 2 不等于 1
2 > 1 // true, 因為 2 大于 1
1 < 2 // true, 因為 1 小于2
1 >= 1 // true, 因為 1 大于等于 1
2 <= 1 // false, 因為 2 并不小于等于 1
~~~
比較運算多用于條件語句,如`if`條件:
~~~
let name = "world"
if name == "world" {
print("hello, world")
} else {
print("I'm sorry \(name), but I don't recognize you")
}
// 輸出 "hello, world", 因為 `name` 就是等于 "world"
~~~
關于`if`語句,請看[控制流](http://wiki.jikexueyuan.com/project/swift/chapter2/05_Control_Flow.html)。
## 三目運算符(Ternary Conditional Operator)
三目運算符的特殊在于它是有三個操作數的運算符,它的原型是?`問題 ? 答案1 : 答案2`。它簡潔地表達根據`問題`成立與否作出二選一的操作。如果`問題`成立,返回`答案1`的結果; 如果不成立,返回`答案2`的結果。
三目運算符是以下代碼的縮寫形式:
~~~
if question {
answer1
} else {
answer2
}
~~~
這里有個計算表格行高的例子。如果有表頭,那行高應比內容高度要高出50像素; 如果沒有表頭,只需高出20像素。
~~~
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight 現在是 90
~~~
這樣寫會比下面的代碼簡潔:
~~~
let contentHeight = 40
let hasHeader = true
var rowHeight = contentHeight
if hasHeader {
rowHeight = rowHeight + 50
} else {
rowHeight = rowHeight + 20
}
// rowHeight 現在是 90
~~~
第一段代碼例子使用了三目運算,所以一行代碼就能讓我們得到正確答案。這比第二段代碼簡潔得多,無需將`rowHeight`定義成變量,因為它的值無需在`if`語句中改變。
三目運算提供有效率且便捷的方式來表達二選一的選擇。需要注意的事,過度使用三目運算符會使簡潔的代碼變的難懂。我們應避免在一個組合語句中使用多個三目運算符。
## 空合運算符(Nil Coalescing Operator)
空合運算符(`a ?? b`)將對可選類型`a`進行空判斷,如果`a`包含一個值就進行解封,否則就返回一個默認值`b`.這個運算符有兩個條件:
* 表達式`a`必須是Optional類型
* 默認值`b`的類型必須要和`a`存儲值的類型保持一致
空合并運算符是對以下代碼的簡短表達方法
~~~
a != nil ? a! : b
~~~
上述代碼使用了三目運算符。當可選類型`a`的值不為空時,進行強制解封(`a!`)訪問`a`中值,反之當`a`中值為空時,返回默認值b。無疑空合運算符(`??`)提供了一種更為優雅的方式去封裝條件判斷和解封兩種行為,顯得簡潔以及更具可讀性。
> 注意: 如果`a`為非空值(`non-nil`),那么值`b`將不會被估值。這也就是所謂的短路求值。
下文例子采用空合并運算符,實現了在默認顏色名和可選自定義顏色名之間抉擇:
~~~
let defaultColorName = "red"
var userDefinedColorName:String? //默認值為nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
//userDefinedColorName的值為空 ,所以colorNameToUse的值為`red`
~~~
`userDefinedColorName`變量被定義為一個可選`String`類型,默認值為`nil`。由于`userDefinedColorName`是一個可選類型,我們可以使用空合運算符去判斷其值。在上一個例子中,通過空合運算符為一個名為`colorNameToUse`的變量賦予一個字符串類型初始值。 由于`userDefinedColorName`值為空,因此表達式`userDefinedColorName ?? defaultColorName`返回`defaultColorName`的值,即`red`。
另一種情況,分配一個非空值(`non-nil`)給?`userDefinedColorName`,再次執行空合運算,運算結果為封包在`userDefaultColorName`中的值,而非默認值。
~~~
userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
//userDefinedColorName非空,因此colorNameToUsede的值為綠色
~~~
## 區間運算符
Swift 提供了兩個方便表達一個區間的值的運算符。
### 閉區間運算符
閉區間運算符(`a...b`)定義一個包含從`a`到`b`(包括`a`和`b`)的所有值的區間,`b`必須大于等于`a`。 ? 閉區間運算符在迭代一個區間的所有值時是非常有用的,如在`for-in`循環中:
~~~
for index in 1...5 {
print("\(index) * 5 = \(index * 5)")
}
// 1 * 5 = 5
// 2 * 5 = 10
// 3 * 5 = 15
// 4 * 5 = 20
// 5 * 5 = 25
~~~
關于`for-in`,請看[控制流](http://wiki.jikexueyuan.com/project/swift/chapter2/05_Control_Flow.html)。
### 半開區間運算符
半開區間(`a..<b`)定義一個從`a`到`b`但不包括`b`的區間。 之所以稱為半開區間,是因為該區間包含第一個值而不包括最后的值。
半開區間的實用性在于當你使用一個0始的列表(如數組)時,非常方便地從0數到列表的長度。
~~~
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
print("第 \(i + 1) 個人叫 \(names[i])")
}
// 第 1 個人叫 Anna
// 第 2 個人叫 Alex
// 第 3 個人叫 Brian
// 第 4 個人叫 Jack
~~~
數組有4個元素,但`0..<count`只數到3(最后一個元素的下標),因為它是半開區間。關于數組,請查閱[數組](http://wiki.jikexueyuan.com/project/swift/chapter2/04_Collection_Types.html#arrays)。
## 邏輯運算
邏輯運算的操作對象是邏輯布爾值。Swift 支持基于 C 語言的三個標準邏輯運算。
* 邏輯非(`!a`)
* 邏輯與(`a && b`)
* 邏輯或(`a || b`)
### 邏輯非
邏輯非運算(`!a`)對一個布爾值取反,使得`true`變`false`,`false`變`true`。
它是一個前置運算符,需緊跟在操作數之前,且不加空格。讀作`非 a`,例子如下:
~~~
let allowedEntry = false
if !allowedEntry {
print("ACCESS DENIED")
}
// 輸出 "ACCESS DENIED"
~~~
`if !allowedEntry`語句可以讀作 "如果 非 alowed entry。",接下一行代碼只有在如果 "非 allow entry" 為`true`,即`allowEntry`為`false`時被執行。
在示例代碼中,小心地選擇布爾常量或變量有助于代碼的可讀性,并且避免使用雙重邏輯非運算,或混亂的邏輯語句。
### 邏輯與
邏輯與(`a && b`)表達了只有`a`和`b`的值都為`true`時,整個表達式的值才會是`true`。
只要任意一個值為`false`,整個表達式的值就為`false`。事實上,如果第一個值為`false`,那么是不去計算第二個值的,因為它已經不可能影響整個表達式的結果了。這被稱做 "短路計算(short-circuit evaluation)"。
以下例子,只有兩個`Bool`值都為`true`的時候才允許進入:
~~~
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "ACCESS DENIED"
~~~
### 邏輯或
邏輯或(`a || b`)是一個由兩個連續的`|`組成的中置運算符。它表示了兩個邏輯表達式的其中一個為`true`,整個表達式就為`true`。
同邏輯與運算類似,邏輯或也是"短路計算"的,當左端的表達式為`true`時,將不計算右邊的表達式了,因為它不可能改變整個表達式的值了。
以下示例代碼中,第一個布爾值(`hasDoorKey`)為`false`,但第二個值(`knowsOverridePassword`)為`true`,所以整個表達是`true`,于是允許進入:
~~~
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "Welcome!"
~~~
### 邏輯運算符組合計算
我們可以組合多個邏輯運算來表達一個復合邏輯:
~~~
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "Welcome!"
~~~
這個例子使用了含多個`&&`和`||`的復合邏輯。但無論怎樣,`&&`和`||`始終只能操作兩個值。所以這實際是三個簡單邏輯連續操作的結果。我們來解讀一下:
如果我們輸入了正確的密碼并通過了視網膜掃描; 或者我們有一把有效的鑰匙; 又或者我們知道緊急情況下重置的密碼,我們就能把門打開進入。
前兩種情況,我們都不滿足,所以前兩個簡單邏輯的結果是`false`,但是我們是知道緊急情況下重置的密碼的,所以整個復雜表達式的值還是`true`。
> 注意: Swift 邏輯操作符`&&`和`||`是左結合的,這意味著擁有多元邏輯操作符的復合表達式優先計算最左邊的子表達式。
### 使用括號來明確優先級
為了一個復雜表達式更容易讀懂,在合適的地方使用括號來明確優先級是很有效的,雖然它并非必要的。在上個關于門的權限的例子中,我們給第一個部分加個括號,使用它看起來邏輯更明確:
~~~
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "Welcome!"
~~~
這括號使得前兩個值被看成整個邏輯表達中獨立的一個部分。雖然有括號和沒括號的輸出結果是一樣的,但對于讀代碼的人來說有括號的代碼更清晰。可讀性比簡潔性更重要,請在可以讓你代碼變清晰地地方加個括號吧!
- 介紹
- 歡迎使用 Swift
- 關于 Swift
- Swift 初見
- Swift 版本歷史記錄
- Swift1.0 發布內容
- Swift 教程
- 基礎部分
- 基本運算符
- 字符串和字符
- 集合類型
- 控制流
- 函數
- 閉包
- 枚舉
- 類和結構體
- 屬性
- 方法
- 下標腳本
- 繼承
- 構造過程
- 析構過程
- 自動引用計數
- 可選鏈
- 錯誤處理
- 類型轉換
- 嵌套類型
- 擴展
- 協議
- 泛型
- 權限控制
- 高級操作符
- 語言參考
- 關于語言參考
- 詞法結構
- 類型
- 表達式
- 語句
- 聲明
- 特性
- 模式
- 泛型參數
- 語法總結
- 蘋果官方Blog官方翻譯
- Access Control 權限控制的黑與白
- 造個類型不是夢-白話Swift類型創建
- WWDC里面的那個“大炮打氣球”
- Swift與C語言指針友好合作
- 引用類型和值類型的恩怨
- 訪問控制和Protected
- 可選類型完美解決占位問題