空合運算符
空合運算符( a ?? b )将對可選類型 a 進⾏空判斷,如果 a 包含一個值就進⾏解包,否則就傳回一個預設值b 。表達式 a 必須是
Optional 類型。預設值 b 的類型必須要和 a 存儲值的類型保持一緻。
空合運算符是對以下代碼的簡短表達方法:
a != nil ? a! : b
上述代碼使用了三元運算符。當可選類型 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 ⾮空,是以 colorNameToUse 的值為 "green"
區間運算符
Swift 提供了幾種方便表達一個區間的值的區間運算符。
閉區間運算符
閉區間運算符( a...b )定義一個包含從 a 到 b (包括 a 和 b )的所有值的區間。 a 的值不能超過 b 。 閉區間運算符在疊代一個區間
的所有值時是非常有用的,如在 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
半開區間運算符
半開區間運算符( 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(最後一個元素的下标),因為它是半開區間。
單側區間
閉區間操作符有另一個表達形式,可以表達往一側無限延伸的區間 —— 例如,一個包含了數組從索引 2 到結尾的所有值的區
間。在這些情況下,你可以省略掉區間操作符一側的值。這種區間叫做單側區間,因為操作符隻有一側有值。例如:
for name in names[2...] {
print(name)
}
// Brian
// Jack
for name in names[...2] {
print(name)
}
// Anna
// Alex
// Brian
半開區間操作符也有單側表達形式,附帶上它的最終值。就像你使用區間去包含一個值,最終值并不會落在區間内。例如:
for name in names[..<2] {
print(name)
}
// Anna
// Alex
單側區間不止可以在下标⾥使用,也可以在别的情景下使用。你不能周遊省略了初始值的單側區間,因為周遊的開端并不明顯。
你可以周遊一個省略最終值的單側區間;然而,由于這種區間無限延伸的特性,請保證你在循環⾥有一個結束循環的分支。你也可
以檢視一個單側區間是否包含某個特定的值,就像下面展示的那樣。
let range = ...5
range.contains(7) // false
range.contains(4) // true
range.contains(-1) // true
邏輯運算符
邏輯運算符的操作對象是邏輯布爾值。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 語句可以讀作「如果非 allowedEntry」,下一行代碼隻有在「非 allowedEntry」為 true ,即allowEntry 為 false
時被執⾏。
在示例代碼中,⼩心地選擇布爾常量或變量有助于代碼的可讀性,并且避免使用雙重邏輯非運算,或混亂的邏輯語句。
邏輯與運算符
邏輯與運算符( a && b )表達了隻有 a 和 b 的值都為 true 時,整個表達式的值才會是 true 。 隻要任意一個值為 false ,整個表達
式的值就為 false 。事實上,如果第一個值為 false ,那麼是不去計算第二個值的,因為它已經不可能影響整個表達式的結果
了。這被稱做短路計算(short-circuit evaluation)。 以下例子,隻有兩個 Bool 值都為 true 的時候才允許進入 if:
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!”
這括号使得前兩個值被看成整個邏輯表達中獨⽴的一個部分。雖然有括号和沒括号的輸出結果是一樣的,但對于讀代碼的人來說
有括号的代碼更清晰。可讀性比簡潔性更重要,請在可以讓你代碼變清晰的地方加個括号吧!