運(yùn)算符是檢查、改變、合并值的特殊符號(hào)或短語(yǔ)。例如,加號(hào)+
將兩個(gè)數(shù)相加(如let i = 1 + 2
)。復(fù)雜些的運(yùn)算例如邏輯與運(yùn)算符&&
(如if enteredDoorCode && passedRetinaScan
),或讓 i 值加1的便捷自增運(yùn)算符++i
等。
Swift 支持大部分標(biāo)準(zhǔn) C 語(yǔ)言的運(yùn)算符,且改進(jìn)許多特性來(lái)減少常規(guī)編碼錯(cuò)誤。如:賦值符(=
)不返回值,以防止把想要判斷相等運(yùn)算符(==
)的地方寫(xiě)成賦值符導(dǎo)致的錯(cuò)誤。算術(shù)運(yùn)算符(+
,-
,*
,/
,%
等)會(huì)檢測(cè)并不允許值溢出,以此來(lái)避免保存變量時(shí)由于變量大于或小于其類(lèi)型所能承載的范圍時(shí)導(dǎo)致的異常結(jié)果。當(dāng)然允許你使用 Swift 的溢出運(yùn)算符來(lái)實(shí)現(xiàn)溢出。詳情參見(jiàn)溢出運(yùn)算符。
區(qū)別于 C 語(yǔ)言,在 Swift 中你可以對(duì)浮點(diǎn)數(shù)進(jìn)行取余運(yùn)算(%
),Swift 還提供了 C 語(yǔ)言沒(méi)有的表達(dá)兩數(shù)之間的值的區(qū)間運(yùn)算符(a..<b
和a...b
),這方便我們表達(dá)一個(gè)區(qū)間內(nèi)的數(shù)值。
本章節(jié)只描述了 Swift 中的基本運(yùn)算符,高級(jí)運(yùn)算符包含了高級(jí)運(yùn)算符,及如何自定義運(yùn)算符,及如何進(jìn)行自定義類(lèi)型的運(yùn)算符重載。
運(yùn)算符有一元、二元和三元運(yùn)算符。
-a
)。一元運(yùn)算符分前置運(yùn)算符和后置運(yùn)算符,前置運(yùn)算符需緊排操作對(duì)象之前(如!b
),后置運(yùn)算符需緊跟操作對(duì)象之后(如i++
)。2 + 3
),是中置的,因?yàn)樗鼈兂霈F(xiàn)在兩個(gè)操作對(duì)象之間。a ? b : c
)。受運(yùn)算符影響的值叫操作數(shù),在表達(dá)式1 + 2
中,加號(hào)+
是二元運(yùn)算符,它的兩個(gè)操作數(shù)是值1
和2
。
賦值運(yùn)算(a = b
),表示用b
的值來(lái)初始化或更新a
的值:
let b = 10
var a = 5
a = b
// a 現(xiàn)在等于 10
如果賦值的右邊是一個(gè)多元組,它的元素可以馬上被分解多個(gè)常量或變量:
let (x, y) = (1, 2)
// 現(xiàn)在 x 等于 1, y 等于 2
與 C 語(yǔ)言和 Objective-C 不同,Swift 的賦值操作并不返回任何值。所以以下代碼是錯(cuò)誤的:
if x = y {
// 此句錯(cuò)誤, 因?yàn)?x = y 并不返回任何值
}
這個(gè)特性使你無(wú)法把(==
)錯(cuò)寫(xiě)成(=
),由于if x = y
是錯(cuò)誤代碼,Swift幫你避免此類(lèi)錯(cuò)誤的的發(fā)生。
Swift 中所有數(shù)值類(lèi)型都支持了基本的四則算術(shù)運(yùn)算:
+
)-
)*
)/
) 1 + 2 // 等于 3
5 - 3 // 等于 2
2 * 3 // 等于 6
10.0 / 2.5 // 等于 4.0
與 C 語(yǔ)言和 Objective-C 不同的是,Swift 默認(rèn)情況下不允許在數(shù)值運(yùn)算中出現(xiàn)溢出情況。但是你可以使用 Swift 的溢出運(yùn)算符來(lái)實(shí)現(xiàn)溢出運(yùn)算(如a &+ b
)。詳情參見(jiàn)溢出運(yùn)算符。
加法運(yùn)算符也可用于String
的拼接:
"hello, " + "world" // 等于 "hello, world"
兩個(gè)Character
值或一個(gè)String
和一個(gè)Character
值,相加會(huì)生成一個(gè)新的String
值:
let dog: Character = "d"
let cow: Character = "c"
let dogCow = dog + cow
// 譯者注: 原來(lái)的引號(hào)內(nèi)是很可愛(ài)的小狗和小牛, 但win os下不支持表情字符, 所以改成了普通字符
// dogCow 現(xiàn)在是 "dc"
詳情參見(jiàn)字符,字符串的拼接。
求余運(yùn)算(a % b
)是計(jì)算b
的多少倍剛剛好可以容入a
,返回多出來(lái)的那部分(余數(shù))。
注意:
求余運(yùn)算(%
)在其他語(yǔ)言也叫取模運(yùn)算。然而嚴(yán)格說(shuō)來(lái),我們看該運(yùn)算符對(duì)負(fù)數(shù)的操作結(jié)果,"求余"比"取模"更合適些。
我們來(lái)談?wù)勅∮嗍窃趺椿厥?,?jì)算9 % 4
,你先計(jì)算出4
的多少倍會(huì)剛好可以容入9
中:
2倍,非常好,那余數(shù)是1(用橙色標(biāo)出)
在 Swift 中可以表達(dá)為:
9 % 4 // 等于 1
為了得到a % b
的結(jié)果,%
計(jì)算了以下等式,并輸出余數(shù)
作為結(jié)果:
a = (b × 倍數(shù)) + 余數(shù)
當(dāng)倍數(shù)
取最大值的時(shí)候,就會(huì)剛好可以容入a
中。
把9
和4
代入等式中,我們得1
:
9 = (4 × 2) + 1
同樣的方法,我來(lái)們計(jì)算 -9 % 4
:
-9 % 4 // 等于 -1
把-9
和4
代入等式,-2
是取到的最大整數(shù):
-9 = (4 × -2) + -1
余數(shù)是-1
。
在對(duì)負(fù)數(shù)b
求余時(shí),b
的符號(hào)會(huì)被忽略。這意味著 a % b
和 a % -b
的結(jié)果是相同的。
不同于 C 語(yǔ)言和 Objective-C,Swift 中是可以對(duì)浮點(diǎn)數(shù)進(jìn)行求余的。
8 % 2.5 // 等于 0.5
這個(gè)例子中,8
除于2.5
等于3
余0.5
,所以結(jié)果是一個(gè)Double
值0.5
。
和 C 語(yǔ)言一樣,Swift 也提供了對(duì)變量本身加1或減1的自增(++
)和自減(--
)的縮略算符。其操作對(duì)象可以是整形和浮點(diǎn)型。?
var i = 0
++i // 現(xiàn)在 i = 1
每調(diào)用一次++i
,i
的值就會(huì)加1。實(shí)際上,++i
是i = i + 1
的簡(jiǎn)寫(xiě),而--i
是i = i - 1
的簡(jiǎn)寫(xiě)。
++
和--
既可以用作前置運(yùn)算又可以用作后置運(yùn)算。++i
,i++
,--i
和i--
都是有效的寫(xiě)法。
我們需要注意的是這些運(yùn)算符即可修改了i
的值也可以返回i
的值。如果你只想修改i
的值,那你就可以忽略這個(gè)返回值。但如果你想使用返回值,你就需要留意前置和后置操作的返回值是不同的,她們遵循以下原則:
++
前置的時(shí)候,先自増?jiān)俜祷亍?/li>++
后置的時(shí)候,先返回再自增。例如:
var a = 0
let b = ++a // a 和 b 現(xiàn)在都是 1
let c = a++ // a 現(xiàn)在 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
,因?yàn)橄刃薷暮蠓祷剡@樣的行為更符合我們的邏輯。
數(shù)值的正負(fù)號(hào)可以使用前綴-
(即一元負(fù)號(hào))來(lái)切換:
let three = 3
let minusThree = -three // minusThree 等于 -3
let plusThree = -minusThree // plusThree 等于 3, 或 "負(fù)負(fù)3"
一元負(fù)號(hào)(-
)寫(xiě)在操作數(shù)之前,中間沒(méi)有空格。
一元正號(hào)(+
)不做任何改變地返回操作數(shù)的值。
let minusSix = -6
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
雖然一元+
什么都不會(huì)改變,但當(dāng)你在使用一元負(fù)號(hào)來(lái)表達(dá)負(fù)數(shù)時(shí),你可以使用一元正號(hào)來(lái)表達(dá)正數(shù),如此你的代碼會(huì)具有對(duì)稱美。
如同強(qiáng)大的 C 語(yǔ)言,Swift 也提供把其他運(yùn)算符和賦值運(yùn)算(=
)組合的復(fù)合賦值運(yùn)算符,組合加運(yùn)算(+=
)是其中一個(gè)例子:
var a = 1
a += 2
// a 現(xiàn)在是 3
表達(dá)式a += 2
是a = a + 2
的簡(jiǎn)寫(xiě),一個(gè)組合加運(yùn)算就是把加法運(yùn)算和賦值運(yùn)算組合成進(jìn)一個(gè)運(yùn)算符里,同時(shí)完成兩個(gè)運(yùn)算任務(wù)。
注意:
復(fù)合賦值運(yùn)算沒(méi)有返回值,let b = a += 2
這類(lèi)代碼是錯(cuò)誤。這不同于上面提到的自增和自減運(yùn)算符。
在表達(dá)式章節(jié)里有復(fù)合運(yùn)算符的完整列表。?
所有標(biāo)準(zhǔn) C 語(yǔ)言中的比較運(yùn)算都可以在 Swift 中使用。
a == b
)a != b
)a > b
)a < b
)a >= b
)a <= b
)注意:Swift 也提供恒等
===
和不恒等!==
這兩個(gè)比較符來(lái)判斷兩個(gè)對(duì)象是否引用同一個(gè)對(duì)象實(shí)例。更多細(xì)節(jié)在類(lèi)與結(jié)構(gòu)。
每個(gè)比較運(yùn)算都返回了一個(gè)標(biāo)識(shí)表達(dá)式是否成立的布爾值:
1 == 1 // true, 因?yàn)?1 等于 1
2 != 1 // true, 因?yàn)?2 不等于 1
2 > 1 // true, 因?yàn)?2 大于 1
1 < 2 // true, 因?yàn)?1 小于2
1 >= 1 // true, 因?yàn)?1 大于等于 1
2 <= 1 // false, 因?yàn)?2 并不小于等于 1
比較運(yùn)算多用于條件語(yǔ)句,如if
條件:
let name = "world"
if name == "world" {
println("hello, world")
} else {
println("I'm sorry \(name), but I don't recognize you")
}
// 輸出 "hello, world", 因?yàn)?`name` 就是等于 "world"
關(guān)于if
語(yǔ)句,請(qǐng)看控制流。
三目運(yùn)算符的特殊在于它是有三個(gè)操作數(shù)的運(yùn)算符,它的原型是 問(wèn)題 ? 答案1 : 答案2
。它簡(jiǎn)潔地表達(dá)根據(jù)問(wèn)題
成立與否作出二選一的操作。如果問(wèn)題
成立,返回答案1
的結(jié)果; 如果不成立,返回答案2
的結(jié)果。
三目運(yùn)算符是以下代碼的縮寫(xiě)形式:
if question: {
answer1
} else {
answer2
}
這里有個(gè)計(jì)算表格行高的例子。如果有表頭,那行高應(yīng)比內(nèi)容高度要高出50像素; 如果沒(méi)有表頭,只需高出20像素。
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight 現(xiàn)在是 90
這樣寫(xiě)會(huì)比下面的代碼簡(jiǎn)潔:
let contentHeight = 40
let hasHeader = true
var rowHeight = contentHeight
if hasHeader {
rowHeight = rowHeight + 50
} else {
rowHeight = rowHeight + 20
}
// rowHeight 現(xiàn)在是 90
第一段代碼例子使用了三目運(yùn)算,所以一行代碼就能讓我們得到正確答案。這比第二段代碼簡(jiǎn)潔得多,無(wú)需將rowHeight
定義成變量,因?yàn)樗闹禑o(wú)需在if
語(yǔ)句中改變。
三目運(yùn)算提供有效率且便捷的方式來(lái)表達(dá)二選一的選擇。需要注意的事,過(guò)度使用三目運(yùn)算符會(huì)使簡(jiǎn)潔的代碼變的難懂。我們應(yīng)避免在一個(gè)組合語(yǔ)句中使用多個(gè)三目運(yùn)算符。
空合運(yùn)算符(a ?? b
)將對(duì)可選類(lèi)型a
進(jìn)行空判斷,如果a
包含一個(gè)值就進(jìn)行解封,否則就返回一個(gè)默認(rèn)值b
.這個(gè)運(yùn)算符有兩個(gè)條件:
a
必須是Optional類(lèi)型b
的類(lèi)型必須要和a
存儲(chǔ)值的類(lèi)型保持一致空合并運(yùn)算符是對(duì)以下代碼的簡(jiǎn)短表達(dá)方法
a != nil ? a! : b
上述代碼使用了三目運(yùn)算符。當(dāng)可選類(lèi)型a
的值不為空時(shí),進(jìn)行強(qiáng)制解封(a!
)訪問(wèn)a
中值,反之當(dāng)a
中值為空時(shí),返回默認(rèn)值b。無(wú)疑空合運(yùn)算符(??
)提供了一種更為優(yōu)雅的方式去封裝條件判斷和解封兩種行為,顯得簡(jiǎn)潔以及更具可讀性。
注意:如果
a
為非空值(non-nil
),那么值b
將不會(huì)被估值。這也就是所謂的短路求值。
下文例子采用空合并運(yùn)算符,實(shí)現(xiàn)了在默認(rèn)顏色名和可選自定義顏色名之間抉擇:
let defaultColorName = "red"
var userDefinedColorName:String? //默認(rèn)值為nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
//userDefinedColorName的值為空 ,所以colorNameToUse的值為`red`
userDefinedColorName
變量被定義為一個(gè)可選字符串類(lèi)型,默認(rèn)值為nil。由于userDefinedColorName
是一個(gè)可選類(lèi)型,我們可以使用空合運(yùn)算符去判斷其值。在上一個(gè)例子中,通過(guò)空合運(yùn)算符為一個(gè)名為colorNameToUse
的變量賦予一個(gè)字符串類(lèi)型初始值。由于userDefinedColorName
值為空,因此表達(dá)式userDefinedColorName ?? defaultColorName
返回默認(rèn)值,即red
。
另一種情況,分配一個(gè)非空值(non-nil
)給 userDefinedColorName
,再次執(zhí)行空合運(yùn)算,運(yùn)算結(jié)果為封包在userDefaultColorName
中的值,而非默認(rèn)值。
userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
//userDefinedColorName非空,因此colorNameToUsede的值為綠色
Swift 提供了兩個(gè)方便表達(dá)一個(gè)區(qū)間的值的運(yùn)算符。
閉區(qū)間運(yùn)算符(a...b
)定義一個(gè)包含從a
到b
(包括a
和b
)的所有值的區(qū)間,b
必須大于a
。?閉區(qū)間運(yùn)算符在迭代一個(gè)區(qū)間的所有值時(shí)是非常有用的,如在for-in
循環(huán)中:
for index in 1...5 {
println("\(index) * 5 = \(index * 5)")
}
// 1 * 5 = 5
// 2 * 5 = 10
// 3 * 5 = 15
// 4 * 5 = 20
// 5 * 5 = 25
關(guān)于for-in
,請(qǐng)看控制流。
半開(kāi)區(qū)間(a..<b
)定義一個(gè)從a
到b
但不包括b
的區(qū)間。之所以稱為半開(kāi)區(qū)間,是因?yàn)樵搮^(qū)間包含第一個(gè)值而不包括最后的值。
半開(kāi)區(qū)間的實(shí)用性在于當(dāng)你使用一個(gè)0始的列表(如數(shù)組)時(shí),非常方便地從0數(shù)到列表的長(zhǎng)度。
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
println("第 \(i + 1) 個(gè)人叫 \(names[i])")
}
// 第 1 個(gè)人叫 Anna
// 第 2 個(gè)人叫 Alex
// 第 3 個(gè)人叫 Brian
// 第 4 個(gè)人叫 Jack
數(shù)組有4個(gè)元素,但0..<count
只數(shù)到3(最后一個(gè)元素的下標(biāo)),因?yàn)樗前腴_(kāi)區(qū)間。關(guān)于數(shù)組,請(qǐng)查閱數(shù)組。
邏輯運(yùn)算的操作對(duì)象是邏輯布爾值。Swift 支持基于 C 語(yǔ)言的三個(gè)標(biāo)準(zhǔn)邏輯運(yùn)算。
!a
)a && b
)a || b
)邏輯非運(yùn)算(!a
)對(duì)一個(gè)布爾值取反,使得true
變false
,false
變true
。
它是一個(gè)前置運(yùn)算符,需出現(xiàn)在操作數(shù)之前,且不加空格。讀作非 a
,例子如下:
let allowedEntry = false
if !allowedEntry {
println("ACCESS DENIED")
}
// 輸出 "ACCESS DENIED"
if !allowedEntry
語(yǔ)句可以讀作 "如果 非 alowed entry。",接下一行代碼只有在如果 "非 allow entry" 為true
,即allowEntry
為false
時(shí)被執(zhí)行。
在示例代碼中,小心地選擇布爾常量或變量有助于代碼的可讀性,并且避免使用雙重邏輯非運(yùn)算,或混亂的邏輯語(yǔ)句。
邏輯與(a && b
)表達(dá)了只有a
和b
的值都為true
時(shí),整個(gè)表達(dá)式的值才會(huì)是true
。
只要任意一個(gè)值為false
,整個(gè)表達(dá)式的值就為false
。事實(shí)上,如果第一個(gè)值為false
,那么是不去計(jì)算第二個(gè)值的,因?yàn)樗呀?jīng)不可能影響整個(gè)表達(dá)式的結(jié)果了。這被稱做 "短路計(jì)算(short-circuit evaluation)"。
以下例子,只有兩個(gè)Bool
值都為true
的時(shí)候才允許進(jìn)入:
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
println("Welcome!")
} else {
println("ACCESS DENIED")
}
// 輸出 "ACCESS DENIED"
邏輯或(a || b
)是一個(gè)由兩個(gè)連續(xù)的|
組成的中置運(yùn)算符。它表示了兩個(gè)邏輯表達(dá)式的其中一個(gè)為true
,整個(gè)表達(dá)式就為true
。
同邏輯與運(yùn)算類(lèi)似,邏輯或也是"短路計(jì)算"的,當(dāng)左端的表達(dá)式為true
時(shí),將不計(jì)算右邊的表達(dá)式了,因?yàn)樗豢赡芨淖冋麄€(gè)表達(dá)式的值了。
以下示例代碼中,第一個(gè)布爾值(hasDoorKey
)為false
,但第二個(gè)值(knowsOverridePassword
)為true
,所以整個(gè)表達(dá)是true
,于是允許進(jìn)入:
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
println("Welcome!")
} else {
println("ACCESS DENIED")
}
// 輸出 "Welcome!"
我們可以組合多個(gè)邏輯運(yùn)算來(lái)表達(dá)一個(gè)復(fù)合邏輯:
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
println("Welcome!")
} else {
println("ACCESS DENIED")
}
// 輸出 "Welcome!"
這個(gè)例子使用了含多個(gè)&&
和||
的復(fù)合邏輯。但無(wú)論怎樣,&&
和||
始終只能操作兩個(gè)值。所以這實(shí)際是三個(gè)簡(jiǎn)單邏輯連續(xù)操作的結(jié)果。我們來(lái)解讀一下:
如果我們輸入了正確的密碼并通過(guò)了視網(wǎng)膜掃描; 或者我們有一把有效的鑰匙; 又或者我們知道緊急情況下重置的密碼,我們就能把門(mén)打開(kāi)進(jìn)入。
前兩種情況,我們都不滿足,所以前兩個(gè)簡(jiǎn)單邏輯的結(jié)果是false
,但是我們是知道緊急情況下重置的密碼的,所以整個(gè)復(fù)雜表達(dá)式的值還是true
。
為了一個(gè)復(fù)雜表達(dá)式更容易讀懂,在合適的地方使用括號(hào)來(lái)明確優(yōu)先級(jí)是很有效的,雖然它并非必要的。在上個(gè)關(guān)于門(mén)的權(quán)限的例子中,我們給第一個(gè)部分加個(gè)括號(hào),使用它看起來(lái)邏輯更明確:
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
println("Welcome!")
} else {
println("ACCESS DENIED")
}
// 輸出 "Welcome!"
這括號(hào)使得前兩個(gè)值被看成整個(gè)邏輯表達(dá)中獨(dú)立的一個(gè)部分。雖然有括號(hào)和沒(méi)括號(hào)的輸出結(jié)果是一樣的,但對(duì)于讀代碼的人來(lái)說(shuō)有括號(hào)的代碼更清晰??勺x性比簡(jiǎn)潔性更重要,請(qǐng)?jiān)诳梢宰屇愦a變清晰地地方加個(gè)括號(hào)吧!
更多建議: