這里的教程為Swift官方教程中文版。

高級運算符

除了之前介紹過的 基本運算符,Swift 還提供了數(shù)種可以對數(shù)值進(jìn)行復(fù)雜運算的高級運算符。它們包含了在 C 和 Objective-C 中已經(jīng)被大家所熟知的位運算符和移位運算符。

與 C 語言中的算術(shù)運算符不同,Swift 中的算術(shù)運算符默認(rèn)是不會溢出的。所有溢出行為都會被捕獲并報告為錯誤。如果想讓系統(tǒng)允許溢出行為,可以選擇使用 Swift 中另一套默認(rèn)支持溢出的運算符,比如溢出加法運算符(&+)。所有的這些溢出運算符都是以 & 開頭的。

自定義結(jié)構(gòu)體、類和枚舉時,如果也為它們提供標(biāo)準(zhǔn) Swift 運算符的實現(xiàn),將會非常有用。在 Swift 中為這些運算符提供自定義的實現(xiàn)非常簡單,運算符也會針對不同類型使用對應(yīng)實現(xiàn)。

我們不用被預(yù)定義的運算符所限制。在 Swift 中可以自由地定義中綴、前綴、后綴和賦值運算符,它們具有自定義的優(yōu)先級與關(guān)聯(lián)值。這些運算符在代碼中可以像預(yù)定義的運算符一樣使用,你甚至可以擴(kuò)展已有的類型以支持自定義運算符。

位運算符

位運算符可以操作數(shù)據(jù)結(jié)構(gòu)中每個獨立的比特位。它們通常被用在底層開發(fā)中,比如圖形編程和創(chuàng)建設(shè)備驅(qū)動。位運算符在處理外部資源的原始數(shù)據(jù)時也十分有用,比如對自定義通信協(xié)議傳輸?shù)臄?shù)據(jù)進(jìn)行編碼和解碼。

Swift 支持 C 語言中的全部位運算符,接下來會一一介紹。

Bitwise NOT Operator(按位取反運算符)

按位取反運算符(~對一個數(shù)值的全部比特位進(jìn)行取反:

Art/bitwiseNOT_2x.png

按位取反運算符是一個前綴運算符,直接放在運算數(shù)之前,并且它們之間不能添加任何空格:

let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // 等于 0b11110000

UInt8 類型的整數(shù)有 8 個比特位,可以存儲 0 ~ 255 之間的任意整數(shù)。這個例子初始化了一個 UInt8 類型的整數(shù),并賦值為二進(jìn)制的 00001111,它的前 4 位為 0,后 4 位為 1。這個值等價于十進(jìn)制的 15

接著使用按位取反運算符創(chuàng)建了一個名為 invertedBits 的常量,這個常量的值與全部位取反后的 initialBits 相等。即所有的 0 都變成了 1,同時所有的 1 都變成 0。invertedBits 的二進(jìn)制值為 11110000,等價于無符號十進(jìn)制數(shù)的 240。

Bitwise AND Operator(按位與運算符)

按位與運算符(& 對兩個數(shù)的比特位進(jìn)行合并。它返回一個新的數(shù),只有當(dāng)兩個數(shù)的對應(yīng)位1 的時候,新數(shù)的對應(yīng)位才為 1

Art/bitwiseAND_2x.png

在下面的示例當(dāng)中,firstSixBitslastSixBits 中間 4 個位的值都為 1。使用按位與運算符之后,得到二進(jìn)制數(shù)值 00111100,等價于無符號十進(jìn)制數(shù)的 60

let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8  = 0b00111111
let middleFourBits = firstSixBits & lastSixBits // 等于 00111100

Bitwise OR Operator(按位或運算符)

按位或運算符(|可以對兩個數(shù)的比特位進(jìn)行比較。它返回一個新的數(shù),只要兩個數(shù)的對應(yīng)位中有任意一個1 時,新數(shù)的對應(yīng)位就為 1

Art/bitwiseOR_2x.png

在下面的示例中,someBitsmoreBits 存在不同的位被設(shè)置為 1。使用按位或運算符之后,得到二進(jìn)制數(shù)值 11111110,等價于無符號十進(jìn)制數(shù)的 254

let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits // 等于 11111110

Bitwise XOR Operator(按位異或運算符)

按位異或運算符,或稱“排外的或運算符”(^),可以對兩個數(shù)的比特位進(jìn)行比較。它返回一個新的數(shù),當(dāng)兩個數(shù)的對應(yīng)位不相同時,新數(shù)的對應(yīng)位就為 1,并且對應(yīng)位相同時則為 0

Art/bitwiseXOR_2x.png

在下面的示例當(dāng)中,firstBitsotherBits 都有一個自己為 1,而對方為 0 的位。按位異或運算符將新數(shù)的這兩個位都設(shè)置為 1。在其余的位上 firstBitsotherBits 是相同的,所以設(shè)置為 0

let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits // 等于 00010001

Bitwise Left and Right Shift Operators(按位左移、右移運算符)

按位左移運算符(<<按位右移運算符(>>可以對一個數(shù)的所有位進(jìn)行指定位數(shù)的左移和右移,但是需要遵守下面定義的規(guī)則。

對一個數(shù)進(jìn)行按位左移或按位右移,相當(dāng)于對這個數(shù)進(jìn)行乘以 2 或除以 2 的運算。將一個整數(shù)左移一位,等價于將這個數(shù)乘以 2,同樣地,將一個整數(shù)右移一位,等價于將這個數(shù)除以 2。

無符號整數(shù)的移位運算

對無符號整數(shù)進(jìn)行移位的規(guī)則如下:

  1. 已存在的位按指定的位數(shù)進(jìn)行左移和右移。
  2. 任何因移動而超出整型存儲范圍的位都會被丟棄。
  3. 0 來填充移位后產(chǎn)生的空白位。

這種方法稱為邏輯移位。

以下這張圖展示了 11111111 << 1(即把 11111111 向左移動 1 位),和 11111111 >> 1(即把 11111111 向右移動 1 位)的結(jié)果。藍(lán)色的數(shù)字是被移位的,灰色的數(shù)字是被拋棄的,橙色的 0 則是被填充進(jìn)來的:

Art/bitshiftUnsigned_2x.png

下面的代碼演示了 Swift 中的移位運算:

let shiftBits: UInt8 = 4 // 即二進(jìn)制的 00000100
shiftBits << 1           // 00001000
shiftBits << 2           // 00010000
shiftBits << 5           // 10000000
shiftBits << 6           // 00000000
shiftBits >> 2           // 00000001

可以使用移位運算對其他的數(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

這個示例使用了一個命名為 pinkUInt32 型常量來存儲 Cascading Style Sheets(CSS)中粉色的顏色值。該 CSS 的顏色值 #CC6699,在 Swift 中表示為十六進(jìn)制的 0xCC6699。然后利用按位與運算符(&)和按位右移運算符(>>)從這個顏色值中分解出紅(CC)、綠(66)以及藍(lán)(99)三個部分。

紅色部分是通過對 0xCC66990xFF0000 進(jìn)行按位與運算后得到的。0xFF0000 中的 0 部分“掩蓋”了 OxCC6699 中的第二、第三個字節(jié),使得數(shù)值中的 6699 被忽略,只留下 0xCC0000

然后,將這個數(shù)向右移動 16 位(>> 16)。十六進(jìn)制中每兩個字符占用 8 個比特位,所以移動 16 位后 0xCC0000 就變?yōu)?0x0000CC。這個數(shù)和 0xCC 是等同的,也就是十進(jìn)制數(shù)值的 204。

同樣的,綠色部分通過對 0xCC66990x00FF00 進(jìn)行按位與運算得到 0x006600。然后將這個數(shù)向右移動 8 位,得到 0x66,也就是十進(jìn)制數(shù)值的 102。

最后,藍(lán)色部分通過對 0xCC66990x0000FF 進(jìn)行按位與運算得到 0x000099。這里不需要再向右移位,而 0x000099 也就是 0x99 ,也就是十進(jìn)制數(shù)值的 153。

有符號整數(shù)的移位運算

對比無符號整數(shù),有符號整數(shù)的移位運算相對復(fù)雜得多,這種復(fù)雜性源于有符號整數(shù)的二進(jìn)制表現(xiàn)形式。(為了簡單起見,以下的示例都是基于 8 比特的有符號整數(shù),但是其中的原理對任何位數(shù)的有符號整數(shù)都是通用的。)

有符號整數(shù)使用第 1 個比特位(通常被稱為符號位)來表示這個數(shù)的正負(fù)。符號位為 0 代表正數(shù),為 1 代表負(fù)數(shù)。

其余的比特位(通常被稱為數(shù)值位)存儲了實際的值。有符號正整數(shù)和無符號數(shù)的存儲方式是一樣的,都是從 0 開始算起。這是值為 4Int8 型整數(shù)的二進(jìn)制位表現(xiàn)形式:

Art/bitshiftSignedFour_2x.png

符號位為 0(代表這是一個“正數(shù)”),另外 7 位則代表了十進(jìn)制數(shù)值 4 的二進(jìn)制表示。

負(fù)數(shù)的存儲方式略有不同。它存儲 2n 次方減去其實際值的絕對值,這里的 n 是數(shù)值位的位數(shù)。一個 8 比特位的數(shù)有 7 個比特位是數(shù)值位,所以是 27 次方,即 128。

這是值為 -4Int8 型整數(shù)的二進(jìn)制表現(xiàn)形式:

Art/bitshiftSignedMinusFour_2x.png

這次的符號位為 1,說明這是一個負(fù)數(shù),另外 7 個位則代表了數(shù)值 124(即 128 - 4)的二進(jìn)制表示:

Art/bitshiftSignedMinusFourValue_2x.png

負(fù)數(shù)的表示通常被稱為二進(jìn)制補(bǔ)碼。用這種方法來表示負(fù)數(shù)乍看起來有點奇怪,但它有幾個優(yōu)點。

首先,如果想對 -1-4 進(jìn)行加法運算,我們只需要對這兩個數(shù)的全部 8 個比特位執(zhí)行標(biāo)準(zhǔn)的二進(jìn)制相加(包括符號位),并且將計算結(jié)果中超出 8 位的數(shù)值丟棄:

Art/bitshiftSignedAddition_2x.png

其次,使用二進(jìn)制補(bǔ)碼可以使負(fù)數(shù)的按位左移和右移運算得到跟正數(shù)同樣的效果,即每向左移一位就將自身的數(shù)值乘以 2,每向右一位就將自身的數(shù)值除以 2。要達(dá)到此目的,對有符號整數(shù)的右移有一個額外的規(guī)則:當(dāng)對有符號整數(shù)進(jìn)行按位右移運算時,遵循與無符號整數(shù)相同的規(guī)則,但是對于移位產(chǎn)生的空白位使用符號位進(jìn)行填充,而不是用 0。

Art/bitshiftSigned_2x.png

這個行為可以確保有符號整數(shù)的符號位不會因為右移運算而改變,這通常被稱為算術(shù)移位。

由于正數(shù)和負(fù)數(shù)的特殊存儲方式,在對它們進(jìn)行右移的時候,會使它們越來越接近 0。在移位的過程中保持符號位不變,意味著負(fù)整數(shù)在接近 0 的過程中會一直保持為負(fù)。

溢出運算符

當(dāng)向一個整數(shù)類型的常量或者變量賦予超過它容量的值時,Swift 默認(rèn)會報錯,而不是允許生成一個無效的數(shù)。這個行為為我們在運算過大或者過小的數(shù)時提供了額外的安全性。

例如,Int16 型整數(shù)能容納的有符號整數(shù)范圍是 -3276832767。當(dāng)為一個 Int16 類型的變量或常量賦予的值超過這個范圍時,系統(tǒng)就會報錯:

var potentialOverflow = Int16.max
// potentialOverflow 的值是 32767,這是 Int16 能容納的最大整數(shù)
potentialOverflow += 1
// 這里會報錯

在賦值時為過大或者過小的情況提供錯誤處理,能讓我們在處理邊界值時更加靈活。

然而,當(dāng)你希望的時候也可以選擇讓系統(tǒng)在數(shù)值溢出的時候采取截斷處理,而非報錯。Swift 提供的三個溢出運算符來讓系統(tǒng)支持整數(shù)溢出運算。這些運算符都是以 & 開頭的:

  • 溢出加法 &+
  • 溢出減法 &-
  • 溢出乘法 &*

數(shù)值溢出

數(shù)值有可能出現(xiàn)上溢或者下溢。

這個示例演示了當(dāng)我們對一個無符號整數(shù)使用溢出加法(&+)進(jìn)行上溢運算時會發(fā)生什么:

var unsignedOverflow = UInt8.max
// unsignedOverflow 等于 UInt8 所能容納的最大整數(shù) 255
unsignedOverflow = unsignedOverflow &+ 1
// 此時 unsignedOverflow 等于 0

unsignedOverflow 被初始化為 UInt8 所能容納的最大整數(shù)(255,以二進(jìn)制表示即 11111111)。然后使用溢出加法運算符(&+)對其進(jìn)行加 1 運算。這使得它的二進(jìn)制表示正好超出 UInt8 所能容納的位數(shù),也就導(dǎo)致了數(shù)值的溢出,如下圖所示。數(shù)值溢出后,仍然留在 UInt8 邊界內(nèi)的值是 00000000,也就是十進(jìn)制數(shù)值的 0

Art/overflowAddition_2x.png

當(dāng)允許對一個無符號整數(shù)進(jìn)行下溢運算時也會產(chǎn)生類似的情況。這里有一個使用溢出減法運算符(&-)的例子:

var unsignedOverflow = UInt8.min
// unsignedOverflow 等于 UInt8 所能容納的最小整數(shù) 0
unsignedOverflow = unsignedOverflow &- 1
// 此時 unsignedOverflow 等于 255

UInt8 型整數(shù)能容納的最小值是 0,以二進(jìn)制表示即 00000000。當(dāng)使用溢出減法運算符對其進(jìn)行減 1 運算時,數(shù)值會產(chǎn)生下溢并被截斷為 11111111, 也就是十進(jìn)制數(shù)值的 255。

Art/overflowUnsignedSubtraction_2x.png

溢出也會發(fā)生在有符號整型上。針對有符號整型的所有溢出加法或者減法運算都是按位運算的方式執(zhí)行的,符號位也需要參與計算,正如 按位左移、右移運算符 所描述的。

var signedOverflow = Int8.min
// signedOverflow 等于 Int8 所能容納的最小整數(shù) -128
signedOverflow = signedOverflow &- 1
// 此時 signedOverflow 等于 127

Int8 型整數(shù)能容納的最小值是 -128,以二進(jìn)制表示即 10000000。當(dāng)使用溢出減法運算符對其進(jìn)行減 1 運算時,符號位被翻轉(zhuǎn),得到二進(jìn)制數(shù)值 01111111,也就是十進(jìn)制數(shù)值的 127,這個值也是 Int8 型整所能容納的最大值。

Art/overflowSignedSubtraction_2x.png

對于無符號與有符號整型數(shù)值來說,當(dāng)出現(xiàn)上溢時,它們會從數(shù)值所能容納的最大數(shù)變成最小數(shù)。同樣地,當(dāng)發(fā)生下溢時,它們會從所能容納的最小數(shù)變成最大數(shù)。

優(yōu)先級和結(jié)合性

運算符的優(yōu)先級使得一些運算符優(yōu)先于其他運算符;它們會先被執(zhí)行。

結(jié)合性定義了相同優(yōu)先級的運算符是如何結(jié)合的,也就是說,是與左邊結(jié)合為一組,還是與右邊結(jié)合為一組??梢詫⑵淅斫鉃椤八鼈兪桥c左邊的表達(dá)式結(jié)合的”,或者“它們是與右邊的表達(dá)式結(jié)合的”。

當(dāng)考慮一個復(fù)合表達(dá)式的計算順序時,運算符的優(yōu)先級和結(jié)合性是非常重要的。舉例來說,運算符優(yōu)先級解釋了為什么下面這個表達(dá)式的運算結(jié)果會是 17。

2 + 3 % 4 * 5
// 結(jié)果是 17

如果你直接從左到右進(jìn)行運算,你可能認(rèn)為運算的過程是這樣的:

  • 2 + 3 = 5
  • 5 % 4 = 1
  • 1 * 5 = 5

但是正確答案是 17 而不是 5。優(yōu)先級高的運算符要先于優(yōu)先級低的運算符進(jìn)行計算。與 C 語言類似,在 Swift 中,乘法運算符(*)與取余運算符(%)的優(yōu)先級高于加法運算符(+)。因此,它們的計算順序要先于加法運算。

而乘法運算與取余運算的優(yōu)先級相同。這時為了得到正確的運算順序,還需要考慮結(jié)合性。乘法運算與取余運算都是左結(jié)合的??梢詫⑦@考慮成,從它們的左邊開始為這兩部分表達(dá)式都隱式地加上括號:

2 + ((3 % 4) * 5)

(3 % 4) 等于 3,所以表達(dá)式相當(dāng)于:

2 + (3 * 5)

3 * 5 等于 15,所以表達(dá)式相當(dāng)于:

2 + 15

因此計算結(jié)果為 17。

有關(guān) Swift 標(biāo)準(zhǔn)庫提供的操作符信息,包括操作符優(yōu)先級組和結(jié)核性設(shè)置的完整列表,請參見 操作符聲明。

注意

相對 C 語言和 Objective-C 來說,Swift 的運算符優(yōu)先級和結(jié)合性規(guī)則更加簡潔和可預(yù)測。但是,這也意味著它們相較于 C 語言及其衍生語言并不是完全一致。在對現(xiàn)有的代碼進(jìn)行移植的時候,要注意確保運算符的行為仍然符合你的預(yù)期。

運算符函數(shù)

類和結(jié)構(gòu)體可以為現(xiàn)有的運算符提供自定義的實現(xiàn)。這通常被稱為運算符重載。

下面的例子展示了如何讓自定義的結(jié)構(gòu)體支持加法運算符(+)。算術(shù)加法運算符是一個二元運算符,因為它是對兩個值進(jìn)行運算,同時它還可以稱為中綴運算符,因為它出現(xiàn)在兩個值中間。

例子中定義了一個名為 Vector2D 的結(jié)構(gòu)體用來表示二維坐標(biāo)向量 (x, y),緊接著定義了一個可以將兩個 Vector2D 結(jié)構(gòu)體實例進(jì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)
    }
}

該運算符函數(shù)被定義為 Vector2D 上的一個類方法,并且函數(shù)的名字與它要進(jìn)行重載的 + 名字一致。因為加法運算并不是一個向量必需的功能,所以這個類方法被定義在 Vector2D 的一個擴(kuò)展中,而不是 Vector2D 結(jié)構(gòu)體聲明內(nèi)。而算術(shù)加法運算符是二元運算符,所以這個運算符函數(shù)接收兩個類型為 Vector2D 的參數(shù),同時有一個 Vector2D 類型的返回值。

在這個實現(xiàn)中,輸入?yún)?shù)分別被命名為 leftright,代表在 + 運算符左邊和右邊的兩個 Vector2D 實例。函數(shù)返回了一個新的 Vector2D 實例,這個實例的 xy 分別等于作為參數(shù)的兩個實例的 xy 的值之和。

這個類方法可以在任意兩個 Vector2D 實例中間作為中綴運算符來使用:

let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector 是一個新的 Vector2D 實例,值為 (5.0, 5.0)

這個例子實現(xiàn)兩個向量 (3.0,1.0)(2.0,4.0) 的相加,并得到新的向量 (5.0,5.0)。這個過程如下圖示:

Art/vectorAddition_2x.png

前綴和后綴運算符

上個例子演示了一個二元中綴運算符的自定義實現(xiàn)。類與結(jié)構(gòu)體也能提供標(biāo)準(zhǔn)一元運算符的實現(xiàn)。一元運算符只運算一個值。當(dāng)運算符出現(xiàn)在值之前時,它就是前綴的(例如 -a),而當(dāng)它出現(xiàn)在值之后時,它就是后綴的(例如 b!)。

要實現(xiàn)前綴或者后綴運算符,需要在聲明運算符函數(shù)的時候在 func 關(guān)鍵字之前指定 prefix 或者 postfix 修飾符:

extension Vector2D {
    static prefix func - (vector: Vector2D) -> Vector2D {
        return Vector2D(x: -vector.x, y: -vector.y)
    }
}

這段代碼為 Vector2D 類型實現(xiàn)了一元運算符(-a)。由于該運算符是前綴運算符,所以這個函數(shù)需要加上 prefix 修飾符。

對于簡單數(shù)值,一元負(fù)號運算符可以對它們的正負(fù)性進(jìn)行改變。對于 Vector2D 來說,該運算將其 xy 屬性的正負(fù)性都進(jìn)行了改變:

let positive = Vector2D(x: 3.0, y: 4.0)
let negative = -positive
// negative 是一個值為 (-3.0, -4.0) 的 Vector2D 實例
let alsoPositive = -negative
// alsoPositive 是一個值為 (3.0, 4.0) 的 Vector2D 實例

復(fù)合賦值運算符

復(fù)合賦值運算符將賦值運算符(=)與其它運算符進(jìn)行結(jié)合。例如,將加法與賦值結(jié)合成加法賦值運算符(+=)。在實現(xiàn)的時候,需要把運算符的左參數(shù)設(shè)置成 inout 類型,因為這個參數(shù)的值會在運算符函數(shù)內(nèi)直接被修改。

在下面的例子中,對 Vector2D 實例實現(xiàn)了一個加法賦值運算符函數(shù):

extension Vector2D {
    static func += (left: inout Vector2D, right: Vector2D) {
        left = left + right
    }
}

因為加法運算在之前已經(jīng)定義過了,所以在這里無需重新定義。在這里可以直接利用現(xiàn)有的加法運算符函數(shù),用它來對左值和右值進(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)

注意

不能對默認(rèn)的賦值運算符(=)進(jìn)行重載。只有復(fù)合賦值運算符可以被重載。同樣地,也無法對三元條件運算符 (a ? b : c) 進(jìn)行重載。

等價運算符

通常情況下,自定義的類和結(jié)構(gòu)體沒有對等價運算符進(jìn)行默認(rèn)實現(xiàn),等價運算符通常被稱為相等運算符(==)與不等運算符(!=)。

為了使用等價運算符對自定義的類型進(jìn)行判等運算,需要為“相等”運算符提供自定義實現(xiàn),實現(xiàn)的方法與其它中綴運算符一樣, 并且增加對標(biāo)準(zhǔn)庫 Equatable 協(xié)議的遵循:

extension Vector2D: Equatable {
    static func == (left: Vector2D, right: Vector2D) -> Bool {
        return (left.x == right.x) && (left.y == right.y)
    }
}

上述代碼實現(xiàn)了“相等”運算符(==)來判斷兩個 Vector2D 實例是否相等。對于 Vector2D 來說,“相等”意味著“兩個實例的 xy 都相等”,這也是代碼中用來進(jìn)行判等的邏輯。如果你已經(jīng)實現(xiàn)了“相等”運算符,通常情況下你并不需要自己再去實現(xiàn)“不等”運算符(!=)。標(biāo)準(zhǔn)庫對于“不等”運算符提供了默認(rèn)的實現(xiàn),它簡單地將“相等”運算符的結(jié)果進(jìn)行取反后返回。

現(xiàn)在我們可以使用這兩個運算符來判斷兩個 Vector2D 實例是否相等:

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ù)簡單情況下,您可以使用 Swift 為您提供的等價運算符默認(rèn)實現(xiàn)。Swift 為以下數(shù)種自定義類型提供等價運算符的默認(rèn)實現(xiàn):

  • 只擁有存儲屬性,并且它們?nèi)甲裱?Equatable 協(xié)議的結(jié)構(gòu)體
  • 只擁有關(guān)聯(lián)類型,并且它們?nèi)甲裱?Equatable 協(xié)議的枚舉
  • 沒有關(guān)聯(lián)類型的枚舉

在類型原始的聲明中聲明遵循 Equatable 來接收這些默認(rèn)實現(xiàn)。

下面為三維位置向量 (x, y, z) 定義的 Vector3D 結(jié)構(gòu)體,與 Vector2D 類似。由于 x,yz 屬性都是 Equatable 類型,Vector3D 獲得了默認(rèn)的等價運算符實現(xiàn)。

struct Vector3D: Equatable {
    var x = 0.0, y = 0.0, z = 0.0
}

let twoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
if twoThreeFour == anotherTwoThreeFour {
    print("These two vectors are also equivalent.")
}
// 打印“These two vectors are also equivalent.”

自定義運算符

除了實現(xiàn)標(biāo)準(zhǔn)運算符,在 Swift 中還可以聲明和實現(xiàn)自定義運算符??梢杂脕碜远x運算符的字符列表請參考 運算符。

新的運算符要使用 operator 關(guān)鍵字在全局作用域內(nèi)進(jìn)行定義,同時還要指定 prefixinfix 或者 postfix 修飾符:

prefix operator +++

上面的代碼定義了一個新的名為 +++ 的前綴運算符。對于這個運算符,在 Swift 中并沒有已知的意義,因此在針對 Vector2D 實例的特定上下文中,給予了它自定義的意義。對這個示例來講,+++ 被實現(xiàn)為“前綴雙自增”運算符。它使用了前面定義的復(fù)合加法運算符來讓矩陣與自身進(jìn)行相加,從而讓 Vector2D 實例的 x 屬性和 y 屬性值翻倍。你可以像下面這樣通過對 Vector2D 添加一個 +++ 類方法,來實現(xià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)

自定義中綴運算符的優(yōu)先級

每個自定義中綴運算符都屬于某個優(yōu)先級組。優(yōu)先級組指定了這個運算符相對于其他中綴運算符的優(yōu)先級和結(jié)合性。優(yōu)先級和結(jié)合性 中詳細(xì)闡述了這兩個特性是如何對中綴運算符的運算產(chǎn)生影響的。

而沒有明確放入某個優(yōu)先級組的自定義中綴運算符將會被放到一個默認(rèn)的優(yōu)先級組內(nèi),其優(yōu)先級高于三元運算符。

以下例子定義了一個新的自定義中綴運算符 +-,此運算符屬于 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 是一個 Vector2D 實例,并且它的值為 (4.0, -2.0)

這個運算符把兩個向量的 x 值相加,同時從第一個向量的 y 中減去第二個向量的 y 。因為它本質(zhì)上是屬于“相加型”運算符,所以將它放置在 +- 等默認(rèn)中綴“相加型”運算符相同的優(yōu)先級組中。關(guān)于 Swift 標(biāo)準(zhǔn)庫提供的運算符,以及完整的運算符優(yōu)先級組和結(jié)合性設(shè)置,請參考 運算符聲明。而更多關(guān)于優(yōu)先級組以及自定義操作符和優(yōu)先級組的語法,請參考 運算符聲明。

注意

當(dāng)定義前綴與后綴運算符的時候,我們并沒有指定優(yōu)先級。然而,如果對同一個值同時使用前綴與后綴運算符,則后綴運算符會先參與運算。

? 訪問控制
?