1.0 翻譯:xielingwang 校對(duì):numbbbbb
2.0 翻譯+校對(duì):buginux
2.1 校對(duì):shanks,2015-11-01
2.2 翻譯+校對(duì):SketchK 2016-05-17
3.0 翻譯+校對(duì):mmoaay 2016-09-20
3.0.1:shanks,2016-11-13
本頁(yè)內(nèi)容包括:
除了在之前介紹過的基本運(yùn)算符,Swift 中還有許多可以對(duì)數(shù)值進(jìn)行復(fù)雜運(yùn)算的高級(jí)運(yùn)算符。這些高級(jí)運(yùn)算符包含了在 C 和 Objective-C 中已經(jīng)被大家所熟知的位運(yùn)算符和移位運(yùn)算符。
與 C 語(yǔ)言中的算術(shù)運(yùn)算符不同,Swift 中的算術(shù)運(yùn)算符默認(rèn)是不會(huì)溢出的。所有溢出行為都會(huì)被捕獲并報(bào)告為錯(cuò)誤。如果想讓系統(tǒng)允許溢出行為,可以選擇使用 Swift 中另一套默認(rèn)支持溢出的運(yùn)算符,比如溢出加法運(yùn)算符(&+
)。所有的這些溢出運(yùn)算符都是以 &
開頭的。
自定義結(jié)構(gòu)體、類和枚舉時(shí),如果也為它們提供標(biāo)準(zhǔn) Swift 運(yùn)算符的實(shí)現(xiàn),將會(huì)非常有用。在 Swift 中自定義運(yùn)算符非常簡(jiǎn)單,運(yùn)算符也會(huì)針對(duì)不同類型使用對(duì)應(yīng)實(shí)現(xiàn)。
我們不用被預(yù)定義的運(yùn)算符所限制。在 Swift 中可以自由地定義中綴、前綴、后綴和賦值運(yùn)算符,以及相應(yīng)的優(yōu)先級(jí)與結(jié)合性。這些運(yùn)算符在代碼中可以像預(yù)定義的運(yùn)算符一樣使用,我們甚至可以擴(kuò)展已有的類型以支持自定義的運(yùn)算符。
位運(yùn)算符可以操作數(shù)據(jù)結(jié)構(gòu)中每個(gè)獨(dú)立的比特位。它們通常被用在底層開發(fā)中,比如圖形編程和創(chuàng)建設(shè)備驅(qū)動(dòng)。位運(yùn)算符在處理外部資源的原始數(shù)據(jù)時(shí)也十分有用,比如對(duì)自定義通信協(xié)議傳輸?shù)臄?shù)據(jù)進(jìn)行編碼和解碼。
Swift 支持 C 語(yǔ)言中的全部位運(yùn)算符,接下來會(huì)一一介紹。
按位取反運(yùn)算符(~
)可以對(duì)一個(gè)數(shù)值的全部比特位進(jìn)行取反:
按位取反運(yùn)算符是一個(gè)前綴運(yùn)算符,需要直接放在運(yùn)算的數(shù)之前,并且它們之間不能添加任何空格:
let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // 等于 0b11110000
UInt8
類型的整數(shù)有 8 個(gè)比特位,可以存儲(chǔ) 0 ~ 255
之間的任意整數(shù)。這個(gè)例子初始化了一個(gè) UInt8
類型的整數(shù),并賦值為二進(jìn)制的 00001111
,它的前 4 位都為 0
,后 4 位都為 1
。這個(gè)值等價(jià)于十進(jìn)制的 15
。
接著使用按位取反運(yùn)算符創(chuàng)建了一個(gè)名為 invertedBits
的常量,這個(gè)常量的值與全部位取反后的 initialBits
相等。即所有的 0
都變成了 1
,同時(shí)所有的 1
都變成 0
。invertedBits
的二進(jìn)制值為 11110000
,等價(jià)于無符號(hào)十進(jìn)制數(shù)的 240
。
按位與運(yùn)算符(&
)可以對(duì)兩個(gè)數(shù)的比特位進(jìn)行合并。它返回一個(gè)新的數(shù),只有當(dāng)兩個(gè)數(shù)的對(duì)應(yīng)位都為 1
的時(shí)候,新數(shù)的對(duì)應(yīng)位才為 1
:
在下面的示例當(dāng)中,firstSixBits
和 lastSixBits
中間 4 個(gè)位的值都為 1
。按位與運(yùn)算符對(duì)它們進(jìn)行了運(yùn)算,得到二進(jìn)制數(shù)值 00111100
,等價(jià)于無符號(hào)十進(jìn)制數(shù)的 60
:
let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8 = 0b00111111
let middleFourBits = firstSixBits & lastSixBits // 等于 00111100
按位或運(yùn)算符(|
)可以對(duì)兩個(gè)數(shù)的比特位進(jìn)行比較。它返回一個(gè)新的數(shù),只要兩個(gè)數(shù)的對(duì)應(yīng)位中有任意一個(gè)為 1
時(shí),新數(shù)的對(duì)應(yīng)位就為 1
:
在下面的示例中,someBits
和 moreBits
不同的位會(huì)被設(shè)置為 1
。接位或運(yùn)算符對(duì)它們進(jìn)行了運(yùn)算,得到二進(jìn)制數(shù)值 11111110
,等價(jià)于無符號(hào)十進(jìn)制數(shù)的 254
:
let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits // 等于 11111110
按位異或運(yùn)算符(^
)可以對(duì)兩個(gè)數(shù)的比特位進(jìn)行比較。它返回一個(gè)新的數(shù),當(dāng)兩個(gè)數(shù)的對(duì)應(yīng)位不相同時(shí),新數(shù)的對(duì)應(yīng)位就為 1
:
在下面的示例當(dāng)中,firstBits
和 otherBits
都有一個(gè)自己的位為 1
而對(duì)方的對(duì)應(yīng)位為 0
的位。 按位異或運(yùn)算符將新數(shù)的這兩個(gè)位都設(shè)置為 1
,同時(shí)將其它位都設(shè)置為 0
:
let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits // 等于 00010001
按位左移運(yùn)算符(<<
)和按位右移運(yùn)算符(>>
)可以對(duì)一個(gè)數(shù)的所有位進(jìn)行指定位數(shù)的左移和右移,但是需要遵守下面定義的規(guī)則。
對(duì)一個(gè)數(shù)進(jìn)行按位左移或按位右移,相當(dāng)于對(duì)這個(gè)數(shù)進(jìn)行乘以 2 或除以 2 的運(yùn)算。將一個(gè)整數(shù)左移一位,等價(jià)于將這個(gè)數(shù)乘以 2,同樣地,將一個(gè)整數(shù)右移一位,等價(jià)于將這個(gè)數(shù)除以 2。
對(duì)無符號(hào)整數(shù)進(jìn)行移位的規(guī)則如下:
0
來填充移位后產(chǎn)生的空白位。這種方法稱為邏輯移位。
以下這張圖展示了 11111111 << 1
(即把 11111111
向左移動(dòng) 1
位),和 11111111 >> 1
(即把 11111111
向右移動(dòng) 1
位)的結(jié)果。藍(lán)色的部分是被移位的,灰色的部分是被拋棄的,橙色的部分則是被填充進(jìn)來的:
下面的代碼演示了 Swift 中的移位運(yùn)算:
let shiftBits: UInt8 = 4 // 即二進(jìn)制的 00000100
shiftBits << 1 // 00001000
shiftBits << 2 // 00010000
shiftBits << 5 // 10000000
shiftBits << 6 // 00000000
shiftBits >> 2 // 00000001
可以使用移位運(yùn)算對(duì)其他的數(shù)據(jù)類型進(jìn)行編碼和解碼:
let pink: UInt32 = 0xCC6699
let redComponent = (pink & 0xFF0000) >> 16 // redComponent 是 0xCC,即 204
let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent 是 0x66, 即 102
let blueComponent = pink & 0x0000FF // blueComponent 是 0x99,即 153
這個(gè)示例使用了一個(gè)命名為 pink
的 UInt32
型常量來存儲(chǔ) CSS 中粉色的顏色值。該 CSS 的十六進(jìn)制顏色值 #CC6699
,在 Swift 中表示為 0xCC6699
。然后利用按位與運(yùn)算符(&
)和按位右移運(yùn)算符(>>
)從這個(gè)顏色值中分解出紅(CC
)、綠(66
)以及藍(lán)(99
)三個(gè)部分。
紅色部分是通過對(duì) 0xCC6699
和 0xFF0000
進(jìn)行按位與運(yùn)算后得到的。0xFF0000
中的 0
部分“掩蓋”了 OxCC6699
中的第二、第三個(gè)字節(jié),使得數(shù)值中的 6699
被忽略,只留下 0xCC0000
。
然后,再將這個(gè)數(shù)按向右移動(dòng) 16 位(>> 16
)。十六進(jìn)制中每?jī)蓚€(gè)字符表示 8 個(gè)比特位,所以移動(dòng) 16 位后 0xCC0000
就變?yōu)?0x0000CC
。這個(gè)數(shù)和0xCC
是等同的,也就是十進(jìn)制數(shù)值的 204
。
同樣的,綠色部分通過對(duì) 0xCC6699
和 0x00FF00
進(jìn)行按位與運(yùn)算得到 0x006600
。然后將這個(gè)數(shù)向右移動(dòng) 8 位,得到 0x66
,也就是十進(jìn)制數(shù)值的 102
。
最后,藍(lán)色部分通過對(duì) 0xCC6699
和 0x0000FF
進(jìn)行按位與運(yùn)算得到 0x000099
。這里不需要再向右移位,所以結(jié)果為 0x99
,也就是十進(jìn)制數(shù)值的 153
。
對(duì)比無符號(hào)整數(shù),有符號(hào)整數(shù)的移位運(yùn)算相對(duì)復(fù)雜得多,這種復(fù)雜性源于有符號(hào)整數(shù)的二進(jìn)制表現(xiàn)形式。(為了簡(jiǎn)單起見,以下的示例都是基于 8 比特位的有符號(hào)整數(shù)的,但是其中的原理對(duì)任何位數(shù)的有符號(hào)整數(shù)都是通用的。)
有符號(hào)整數(shù)使用第 1 個(gè)比特位(通常被稱為符號(hào)位)來表示這個(gè)數(shù)的正負(fù)。符號(hào)位為 0
代表正數(shù),為 1
代表負(fù)數(shù)。
其余的比特位(通常被稱為數(shù)值位)存儲(chǔ)了實(shí)際的值。有符號(hào)正整數(shù)和無符號(hào)數(shù)的存儲(chǔ)方式是一樣的,都是從 0
開始算起。這是值為 4
的 Int8
型整數(shù)的二進(jìn)制位表現(xiàn)形式:
符號(hào)位為 0
,說明這是一個(gè)正數(shù),另外 7 位則代表了十進(jìn)制數(shù)值 4
的二進(jìn)制表示。
負(fù)數(shù)的存儲(chǔ)方式略有不同。它存儲(chǔ)的值的絕對(duì)值等于 2
的 n
次方減去它的實(shí)際值(也就是數(shù)值位表示的值),這里的 n
為數(shù)值位的比特位數(shù)。一個(gè) 8 比特位的數(shù)有 7 個(gè)比特位是數(shù)值位,所以是 2
的 7
次方,即 128
。
這是值為 -4
的 Int8
型整數(shù)的二進(jìn)制位表現(xiàn)形式:
這次的符號(hào)位為 1
,說明這是一個(gè)負(fù)數(shù),另外 7 個(gè)位則代表了數(shù)值 124
(即 128 - 4
)的二進(jìn)制表示:
負(fù)數(shù)的表示通常被稱為二進(jìn)制補(bǔ)碼表示。用這種方法來表示負(fù)數(shù)乍看起來有點(diǎn)奇怪,但它有幾個(gè)優(yōu)點(diǎn)。
首先,如果想對(duì) -1
和 -4
進(jìn)行加法運(yùn)算,我們只需要將這兩個(gè)數(shù)的全部 8 個(gè)比特位進(jìn)行相加,并且將計(jì)算結(jié)果中超出 8 位的數(shù)值丟棄:
其次,使用二進(jìn)制補(bǔ)碼可以使負(fù)數(shù)的按位左移和右移運(yùn)算得到跟正數(shù)同樣的效果,即每向左移一位就將自身的數(shù)值乘以 2,每向右一位就將自身的數(shù)值除以 2。要達(dá)到此目的,對(duì)有符號(hào)整數(shù)的右移有一個(gè)額外的規(guī)則:
0
。這個(gè)行為可以確保有符號(hào)整數(shù)的符號(hào)位不會(huì)因?yàn)橛乙七\(yùn)算而改變,這通常被稱為算術(shù)移位。
由于正數(shù)和負(fù)數(shù)的特殊存儲(chǔ)方式,在對(duì)它們進(jìn)行右移的時(shí)候,會(huì)使它們?cè)絹碓浇咏?0
。在移位的過程中保持符號(hào)位不變,意味著負(fù)整數(shù)在接近 0
的過程中會(huì)一直保持為負(fù)。
在默認(rèn)情況下,當(dāng)向一個(gè)整數(shù)賦予超過它容量的值時(shí),Swift 默認(rèn)會(huì)報(bào)錯(cuò),而不是生成一個(gè)無效的數(shù)。這個(gè)行為為我們?cè)谶\(yùn)算過大或著過小的數(shù)的時(shí)候提供了額外的安全性。
例如,Int16
型整數(shù)能容納的有符號(hào)整數(shù)范圍是 -32768
到 32767
,當(dāng)為一個(gè) Int16
型變量賦的值超過這個(gè)范圍時(shí),系統(tǒng)就會(huì)報(bào)錯(cuò):
var potentialOverflow = Int16.max
// potentialOverflow 的值是 32767,這是 Int16 能容納的最大整數(shù)
potentialOverflow += 1
// 這里會(huì)報(bào)錯(cuò)
為過大或者過小的數(shù)值提供錯(cuò)誤處理,能讓我們?cè)谔幚磉吔缰禃r(shí)更加靈活。
然而,也可以選擇讓系統(tǒng)在數(shù)值溢出的時(shí)候采取截?cái)嗵幚恚菆?bào)錯(cuò)??梢允褂?Swift 提供的三個(gè)溢出運(yùn)算符來讓系統(tǒng)支持整數(shù)溢出運(yùn)算。這些運(yùn)算符都是以 &
開頭的:
&+
&-
&*
數(shù)值有可能出現(xiàn)上溢或者下溢。
這個(gè)示例演示了當(dāng)我們對(duì)一個(gè)無符號(hào)整數(shù)使用溢出加法(&+
)進(jìn)行上溢運(yùn)算時(shí)會(huì)發(fā)生什么:
var unsignedOverflow = UInt8.max
// unsignedOverflow 等于 UInt8 所能容納的最大整數(shù) 255
unsignedOverflow = unsignedOverflow &+ 1
// 此時(shí) unsignedOverflow 等于 0
unsignedOverflow
被初始化為 UInt8
所能容納的最大整數(shù)(255
,以二進(jìn)制表示即 11111111
)。然后使用了溢出加法運(yùn)算符(&+
)對(duì)其進(jìn)行加 1
運(yùn)算。這使得它的二進(jìn)制表示正好超出 UInt8
所能容納的位數(shù),也就導(dǎo)致了數(shù)值的溢出,如下圖所示。數(shù)值溢出后,留在 UInt8
邊界內(nèi)的值是 00000000
,也就是十進(jìn)制數(shù)值的 0
。
同樣地,當(dāng)我們對(duì)一個(gè)無符號(hào)整數(shù)使用溢出減法(&-
)進(jìn)行下溢運(yùn)算時(shí)也會(huì)產(chǎn)生類似的現(xiàn)象:
var unsignedOverflow = UInt8.min
// unsignedOverflow 等于 UInt8 所能容納的最小整數(shù) 0
unsignedOverflow = unsignedOverflow &- 1
// 此時(shí) unsignedOverflow 等于 255
UInt8
型整數(shù)能容納的最小值是 0
,以二進(jìn)制表示即 00000000
。當(dāng)使用溢出減法運(yùn)算符對(duì)其進(jìn)行減 1
運(yùn)算時(shí),數(shù)值會(huì)產(chǎn)生下溢并被截?cái)酁?11111111
, 也就是十進(jìn)制數(shù)值的 255
。
溢出也會(huì)發(fā)生在有符號(hào)整型數(shù)值上。在對(duì)有符號(hào)整型數(shù)值進(jìn)行溢出加法或溢出減法運(yùn)算時(shí),符號(hào)位也需要參與計(jì)算,正如按位左移、右移運(yùn)算符所描述的。
var signedOverflow = Int8.min
// signedOverflow 等于 Int8 所能容納的最小整數(shù) -128
signedOverflow = signedOverflow &- 1
// 此時(shí) signedOverflow 等于 127
Int8
型整數(shù)能容納的最小值是 -128
,以二進(jìn)制表示即 10000000
。當(dāng)使用溢出減法運(yùn)算符對(duì)其進(jìn)行減 1
運(yùn)算時(shí),符號(hào)位被翻轉(zhuǎn),得到二進(jìn)制數(shù)值 01111111
,也就是十進(jìn)制數(shù)值的 127
,這個(gè)值也是 Int8
型整數(shù)所能容納的最大值。
對(duì)于無符號(hào)與有符號(hào)整型數(shù)值來說,當(dāng)出現(xiàn)上溢時(shí),它們會(huì)從數(shù)值所能容納的最大數(shù)變成最小的數(shù)。同樣地,當(dāng)發(fā)生下溢時(shí),它們會(huì)從所能容納的最小數(shù)變成最大的數(shù)。
運(yùn)算符的優(yōu)先級(jí)使得一些運(yùn)算符優(yōu)先于其他運(yùn)算符,高優(yōu)先級(jí)的運(yùn)算符會(huì)先被計(jì)算。
結(jié)合性定義了相同優(yōu)先級(jí)的運(yùn)算符是如何結(jié)合的,也就是說,是與左邊結(jié)合為一組,還是與右邊結(jié)合為一組??梢詫⑦@意思理解為“它們是與左邊的表達(dá)式結(jié)合的”或者“它們是與右邊的表達(dá)式結(jié)合的”。
在復(fù)合表達(dá)式的運(yùn)算順序中,運(yùn)算符的優(yōu)先級(jí)和結(jié)合性是非常重要的。舉例來說,運(yùn)算符優(yōu)先級(jí)解釋了為什么下面這個(gè)表達(dá)式的運(yùn)算結(jié)果會(huì)是 17
。
2 + 3 % 4 * 5
// 結(jié)果是 17
如果完全從左到右進(jìn)行運(yùn)算,則運(yùn)算的過程是這樣的:
但是正確答案是 17
而不是 5
。優(yōu)先級(jí)高的運(yùn)算符要先于優(yōu)先級(jí)低的運(yùn)算符進(jìn)行計(jì)算。與 C 語(yǔ)言類似,在 Swift 中,乘法運(yùn)算符(*
)與取余運(yùn)算符(%
)的優(yōu)先級(jí)高于加法運(yùn)算符(+
)。因此,它們的計(jì)算順序要先于加法運(yùn)算。
而乘法與取余的優(yōu)先級(jí)相同。這時(shí)為了得到正確的運(yùn)算順序,還需要考慮結(jié)合性。乘法與取余運(yùn)算都是左結(jié)合的??梢詫⑦@考慮成為這兩部分表達(dá)式都隱式地加上了括號(hào):
2 + ((3 % 4) * 5)
(3 % 4)
等于 3
,所以表達(dá)式相當(dāng)于:
2 + (3 * 5)
3 * 5
等于 15
,所以表達(dá)式相當(dāng)于:
2 + 15
因此計(jì)算結(jié)果為 17
。
如果想查看完整的 Swift 運(yùn)算符優(yōu)先級(jí)和結(jié)合性規(guī)則,請(qǐng)參考表達(dá)式。如果想查看 Swift 標(biāo)準(zhǔn)庫(kù)提供所有的運(yùn)算符,請(qǐng)查看 Swift Standard Library Operators Reference。
注意
相對(duì) C 語(yǔ)言和 Objective-C 來說,Swift 的運(yùn)算符優(yōu)先級(jí)和結(jié)合性規(guī)則更加簡(jiǎn)潔和可預(yù)測(cè)。但是,這也意味著它們相較于 C 語(yǔ)言及其衍生語(yǔ)言并不是完全一致的。在對(duì)現(xiàn)有的代碼進(jìn)行移植的時(shí)候,要注意確保運(yùn)算符的行為仍然符合你的預(yù)期。
類和結(jié)構(gòu)體可以為現(xiàn)有的運(yùn)算符提供自定義的實(shí)現(xiàn),這通常被稱為運(yùn)算符重載。
下面的例子展示了如何為自定義的結(jié)構(gòu)體實(shí)現(xiàn)加法運(yùn)算符(+
)。算術(shù)加法運(yùn)算符是一個(gè)雙目運(yùn)算符,因?yàn)樗梢詫?duì)兩個(gè)值進(jìn)行運(yùn)算,同時(shí)它還是中綴運(yùn)算符,因?yàn)樗霈F(xiàn)在兩個(gè)值中間。
例子中定義了一個(gè)名為 Vector2D
的結(jié)構(gòu)體用來表示二維坐標(biāo)向量 (x, y)
,緊接著定義了一個(gè)可以對(duì)兩個(gè) Vector2D
結(jié)構(gòu)體進(jìn)行相加的運(yùn)算符函數(shù):
struct Vector2D {
var x = 0.0, y = 0.0
}
extension Vector2D {
static func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
}
該運(yùn)算符函數(shù)被定義為 Vector2D
上的一個(gè)類方法,并且函數(shù)的名字與它要進(jìn)行重載的 +
名字一致。因?yàn)榧臃ㄟ\(yùn)算并不是一個(gè)向量必需的功能,所以這個(gè)類方法被定義在 Vector2D
的一個(gè)擴(kuò)展中,而不是 Vector2D
結(jié)構(gòu)體聲明內(nèi)。而算術(shù)加法運(yùn)算符是雙目運(yùn)算符,所以這個(gè)運(yùn)算符函數(shù)接收兩個(gè)類型為 Vector2D
的參數(shù),同時(shí)有一個(gè) Vector2D
類型的返回值。
在這個(gè)實(shí)現(xiàn)中,輸入?yún)?shù)分別被命名為 left
和 right
,代表在 +
運(yùn)算符左邊和右邊的兩個(gè) Vector2D
實(shí)例。函數(shù)返回了一個(gè)新的 Vector2D
實(shí)例,這個(gè)實(shí)例的 x
和 y
分別等于作為參數(shù)的兩個(gè)實(shí)例的 x
和 y
的值之和。
這個(gè)類方法可以在任意兩個(gè) Vector2D
實(shí)例中間作為中綴運(yùn)算符來使用:
let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector 是一個(gè)新的 Vector2D 實(shí)例,值為 (5.0, 5.0)
這個(gè)例子實(shí)現(xiàn)兩個(gè)向量 (3.0,1.0)
和 (2.0,4.0)
的相加,并得到新的向量 (5.0,5.0)
。這個(gè)過程如下圖示:
上個(gè)例子演示了一個(gè)雙目中綴運(yùn)算符的自定義實(shí)現(xiàn)。類與結(jié)構(gòu)體也能提供標(biāo)準(zhǔn)單目運(yùn)算符的實(shí)現(xiàn)。單目運(yùn)算符只運(yùn)算一個(gè)值。當(dāng)運(yùn)算符出現(xiàn)在值之前時(shí),它就是前綴的(例如 -a
),而當(dāng)它出現(xiàn)在值之后時(shí),它就是后綴的(例如 b!
)。
要實(shí)現(xiàn)前綴或者后綴運(yùn)算符,需要在聲明運(yùn)算符函數(shù)的時(shí)候在 func
關(guān)鍵字之前指定 prefix
或者 postfix
修飾符:
extension Vector2D {
static prefix func - (vector: Vector2D) -> Vector2D {
return Vector2D(x: -vector.x, y: -vector.y)
}
}
這段代碼為 Vector2D
類型實(shí)現(xiàn)了單目負(fù)號(hào)運(yùn)算符。由于該運(yùn)算符是前綴運(yùn)算符,所以這個(gè)函數(shù)需要加上 prefix
修飾符。
對(duì)于簡(jiǎn)單數(shù)值,單目負(fù)號(hào)運(yùn)算符可以對(duì)它們的正負(fù)性進(jìn)行改變。對(duì)于 Vector2D
來說,該運(yùn)算將其 x
和 y
屬性的正負(fù)性都進(jìn)行了改變:
let positive = Vector2D(x: 3.0, y: 4.0)
let negative = -positive
// negative 是一個(gè)值為 (-3.0, -4.0) 的 Vector2D 實(shí)例
let alsoPositive = -negative
// alsoPositive 是一個(gè)值為 (3.0, 4.0) 的 Vector2D 實(shí)例
復(fù)合賦值運(yùn)算符將賦值運(yùn)算符(=
)與其它運(yùn)算符進(jìn)行結(jié)合。例如,將加法與賦值結(jié)合成加法賦值運(yùn)算符(+=
)。在實(shí)現(xiàn)的時(shí)候,需要把運(yùn)算符的左參數(shù)設(shè)置成 inout
類型,因?yàn)檫@個(gè)參數(shù)的值會(huì)在運(yùn)算符函數(shù)內(nèi)直接被修改。
extension Vector2D {
static func += (left: inout Vector2D, right: Vector2D) {
left = left + right
}
}
因?yàn)榧臃ㄟ\(yùn)算在之前已經(jīng)定義過了,所以在這里無需重新定義。在這里可以直接利用現(xiàn)有的加法運(yùn)算符函數(shù),用它來對(duì)左值和右值進(jìn)行相加,并再次賦值給左值:
var original = Vector2D(x: 1.0, y: 2.0)
let vectorToAdd = Vector2D(x: 3.0, y: 4.0)
original += vectorToAdd
// original 的值現(xiàn)在為 (4.0, 6.0)
注意
不能對(duì)默認(rèn)的賦值運(yùn)算符(=
)進(jìn)行重載。只有組合賦值運(yùn)算符可以被重載。同樣地,也無法對(duì)三目條件運(yùn)算符 (a ? b : c
) 進(jìn)行重載。
自定義的類和結(jié)構(gòu)體沒有對(duì)等價(jià)運(yùn)算符進(jìn)行默認(rèn)實(shí)現(xiàn),等價(jià)運(yùn)算符通常被稱為“相等”運(yùn)算符(==
)與“不等”運(yùn)算符(!=
)。對(duì)于自定義類型,Swift 無法判斷其是否“相等”,因?yàn)椤跋嗟取钡暮x取決于這些自定義類型在你的代碼中所扮演的角色。
為了使用等價(jià)運(yùn)算符能對(duì)自定義的類型進(jìn)行判等運(yùn)算,需要為其提供自定義實(shí)現(xiàn),實(shí)現(xiàn)的方法與其它中綴運(yùn)算符一樣:
extension Vector2D {
static func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
static func != (left: Vector2D, right: Vector2D) -> Bool {
return !(left == right)
}
}
上述代碼實(shí)現(xiàn)了“相等”運(yùn)算符(==
)來判斷兩個(gè) Vector2D
實(shí)例是否相等。對(duì)于 Vector2D
類型來說,“相等”意味著“兩個(gè)實(shí)例的 x
屬性和 y
屬性都相等”,這也是代碼中用來進(jìn)行判等的邏輯。示例里同時(shí)也實(shí)現(xiàn)了“不等”運(yùn)算符(!=
),它簡(jiǎn)單地將“相等”運(yùn)算符的結(jié)果進(jìn)行取反后返回。
現(xiàn)在我們可以使用這兩個(gè)運(yùn)算符來判斷兩個(gè) Vector2D
實(shí)例是否相等:
let twoThree = Vector2D(x: 2.0, y: 3.0)
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
if twoThree == anotherTwoThree {
print("These two vectors are equivalent.")
}
// 打印 “These two vectors are equivalent.”
除了實(shí)現(xiàn)標(biāo)準(zhǔn)運(yùn)算符,在 Swift 中還可以聲明和實(shí)現(xiàn)自定義運(yùn)算符??梢杂脕碜远x運(yùn)算符的字符列表請(qǐng)參考運(yùn)算符。
新的運(yùn)算符要使用 operator
關(guān)鍵字在全局作用域內(nèi)進(jìn)行定義,同時(shí)還要指定 prefix
、infix
或者 postfix
修飾符:
prefix operator +++
上面的代碼定義了一個(gè)新的名為 +++
的前綴運(yùn)算符。對(duì)于這個(gè)運(yùn)算符,在 Swift 中并沒有意義,因此我們針對(duì) Vector2D
的實(shí)例來定義它的意義。對(duì)這個(gè)示例來講,+++
被實(shí)現(xiàn)為“前綴雙自增”運(yùn)算符。它使用了前面定義的復(fù)合加法運(yùn)算符來讓矩陣對(duì)自身進(jìn)行相加,從而讓 Vector2D
實(shí)例的 x
屬性和 y
屬性的值翻倍。實(shí)現(xiàn) +++
運(yùn)算符的方式如下:
extension Vector2D {
static prefix func +++ (vector: inout Vector2D) -> Vector2D {
vector += vector
return vector
}
}
var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
let afterDoubling = +++toBeDoubled
// toBeDoubled 現(xiàn)在的值為 (2.0, 8.0)
// afterDoubling 現(xiàn)在的值也為 (2.0, 8.0)
每個(gè)自定義中綴運(yùn)算符都屬于某個(gè)優(yōu)先級(jí)組。這個(gè)優(yōu)先級(jí)組指定了這個(gè)運(yùn)算符和其他中綴運(yùn)算符的優(yōu)先級(jí)和結(jié)合性。優(yōu)先級(jí)和結(jié)合性中詳細(xì)闡述了這兩個(gè)特性是如何對(duì)中綴運(yùn)算符的運(yùn)算產(chǎn)生影響的。
而沒有明確放入優(yōu)先級(jí)組的自定義中綴運(yùn)算符會(huì)放到一個(gè)默認(rèn)的優(yōu)先級(jí)組內(nèi),其優(yōu)先級(jí)高于三元運(yùn)算符。
以下例子定義了一個(gè)新的自定義中綴運(yùn)算符 +-
,此運(yùn)算符屬于 AdditionPrecedence
優(yōu)先組:
infix operator +-: AdditionPrecedence
extension Vector2D {
static func +- (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y - right.y)
}
}
let firstVector = Vector2D(x: 1.0, y: 2.0)
let secondVector = Vector2D(x: 3.0, y: 4.0)
let plusMinusVector = firstVector +- secondVector
// plusMinusVector 是一個(gè) Vector2D 實(shí)例,并且它的值為 (4.0, -2.0)
這個(gè)運(yùn)算符把兩個(gè)向量的 x
值相加,同時(shí)用第一個(gè)向量的 y
值減去第二個(gè)向量的 y
值。因?yàn)樗举|(zhì)上是屬于“相加型”運(yùn)算符,所以將它放置 +
和 -
等默認(rèn)的中綴“相加型”運(yùn)算符相同的優(yōu)先級(jí)組中。關(guān)于 Swift 標(biāo)準(zhǔn)庫(kù)提供的運(yùn)算符,以及完整的運(yùn)算符優(yōu)先級(jí)組和結(jié)合性設(shè)置,請(qǐng)參考 Swift Standard Library Operators Reference。而更多關(guān)于優(yōu)先級(jí)組以及自定義操作符和優(yōu)先級(jí)組的語(yǔ)法,請(qǐng)參考運(yùn)算符聲明
注意
當(dāng)定義前綴與后綴運(yùn)算符的時(shí)候,我們并沒有指定優(yōu)先級(jí)。然而,如果對(duì)同一個(gè)值同時(shí)使用前綴與后綴運(yùn)算符,則后綴運(yùn)算符會(huì)先參與運(yùn)算。