Swift-高級(jí)運(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)算符都是以 & 開(kāi)頭的。

1. 位運(yùn)算符

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

1.1 Bitwise NOT Operator(按位取反運(yùn)算符)

  • 按位取反運(yùn)算符(~)對(duì)一個(gè)數(shù)值的全部比特位進(jìn)行取反。
  • 按位取反運(yùn)算符是一個(gè)前綴運(yùn)算符潮模,直接放在運(yùn)算數(shù)之前亮蛔,并且它們之間不能添加任何空格
//  UInt8 類(lèi)型的整數(shù)有 8 個(gè)比特位,可以存儲(chǔ) 0 ~ 255 之間的任意整數(shù)
let initialBits: UInt8 = 0b00001111 //  這個(gè)值等價(jià)于十進(jìn)制的 15
/*  即所有的 0 都變成了 1再登,同時(shí)所有的 1 都變成 0
invertedBits 的二進(jìn)制值為 11110000尔邓,等價(jià)于無(wú)符號(hào)十進(jìn)制數(shù)的 240
*/
let invertedBits = ~initialBits // 等于 0b11110000

1.2 Bitwise AND Operator(按位與運(yùn)算符)

  • 按位與運(yùn)算符(&) 對(duì)兩個(gè)數(shù)的比特位進(jìn)行合并。
  • 它返回一個(gè)新的數(shù)锉矢,只有當(dāng)兩個(gè)數(shù)的對(duì)應(yīng)位都為 1 的時(shí)候梯嗽,新數(shù)的對(duì)應(yīng)位才為 1。
/*
firstSixBits 和 lastSixBits 中間 4 個(gè)位的值都為 1
使用按位與運(yùn)算符之后沽损,得到二進(jìn)制數(shù)值 00111100
等價(jià)于無(wú)符號(hào)十進(jìn)制數(shù)的 60
*/
let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8  = 0b00111111
let middleFourBits = firstSixBits & lastSixBits // 等于 00111100

1.3 Bitwise OR Operator(按位或運(yùn)算符)

  • 按位或運(yùn)算符(|)可以對(duì)兩個(gè)數(shù)的比特位進(jìn)行比較灯节。
  • 它返回一個(gè)新的數(shù),只要兩個(gè)數(shù)的對(duì)應(yīng)位中有任意一個(gè)為 1 時(shí),新數(shù)的對(duì)應(yīng)位就為 1炎疆。
/*
someBits 和 moreBits 存在不同的位被設(shè)置為 1
使用按位或運(yùn)算符之后卡骂,得到二進(jìn)制數(shù)值 11111110
等價(jià)于無(wú)符號(hào)十進(jìn)制數(shù)的 254
*/
let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits // 等于 11111110

1.4 Bitwise XOR Operator(按位異或運(yùn)算符)

  • 按位異或運(yùn)算符,或稱(chēng)“排外的或運(yùn)算符”(^)形入,可以對(duì)兩個(gè)數(shù)的比特位進(jìn)行比較全跨。
  • 它返回一個(gè)新的數(shù),當(dāng)兩個(gè)數(shù)的對(duì)應(yīng)位不相同時(shí)亿遂,新數(shù)的對(duì)應(yīng)位就為 1浓若,并且對(duì)應(yīng)位相同時(shí)則為 0。
/*
firstBits 和 otherBits 都有一個(gè)自己為 1蛇数,而對(duì)方為 0 的位
按位異或運(yùn)算符將新數(shù)的這兩個(gè)位都設(shè)置為 1
在其余的位上 firstBits 和 otherBits 是相同的挪钓,所以設(shè)置為 0
*/
let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits // 等于 00010001

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

  • 按位左移運(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腿倚。

1.5.1 無(wú)符號(hào)整數(shù)的移位運(yùn)算

  • 對(duì)無(wú)符號(hào)整數(shù)進(jìn)行移位的規(guī)則如下:
    • 已存在的位按指定的位數(shù)進(jìn)行左移和右移纯出。
    • 任何因移動(dòng)而超出整型存儲(chǔ)范圍的位都會(huì)被丟棄。
    • 用 0 來(lái)填充移位后產(chǎn)生的空白位敷燎。
  • 這種方法稱(chēng)為邏輯移位暂筝。

下面的代碼演示了 Swift 中的移位運(yùn)算:展示了 11111111 << 1(即把 11111111 向左移動(dòng) 1 位),和 11111111 >> 1(即把 11111111 向右移動(dòng) 1 位)的結(jié)果硬贯。

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ù)類(lèi)型進(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 型常量來(lái)存儲(chǔ) Cascading Style Sheets(CSS)中粉色的顏色值鸵赖。
該 CSS 的顏色值 #CC6699,在 Swift 中表示為十六進(jìn)制的 0xCC6699拄衰。
然后利用按位與運(yùn)算符(&)和按位右移運(yùn)算符(>>)從這個(gè)顏色值中分解出紅(CC)它褪、綠(66)以及藍(lán)(99)三個(gè)部分。
紅色部分是通過(guò)對(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。
同樣的开财,綠色部分通過(guò)對(duì) 0xCC6699 和 0x00FF00 進(jìn)行按位與運(yùn)算得到 0x006600块促。然后將這個(gè)數(shù)向右移動(dòng) 8 位,得到 0x66床未,也就是十進(jìn)制數(shù)值的 102竭翠。
最后,藍(lán)色部分通過(guò)對(duì) 0xCC6699 和 0x0000FF 進(jìn)行按位與運(yùn)算得到 0x000099薇搁。這里不需要再向右移位斋扰,而 0x000099 也就是 0x99 ,也就是十進(jìn)制數(shù)值的 153啃洋。

1.5.2 有符號(hào)整數(shù)的移位運(yùn)算

  • 對(duì)比無(wú)符號(hào)整數(shù)传货,有符號(hào)整數(shù)的移位運(yùn)算相對(duì)復(fù)雜得多,這種復(fù)雜性源于有符號(hào)整數(shù)的二進(jìn)制表現(xiàn)形式宏娄。(為了簡(jiǎn)單起見(jiàn)问裕,以下的示例都是基于 8 比特的有符號(hào)整數(shù),但是其中的原理對(duì)任何位數(shù)的有符號(hào)整數(shù)都是通用的孵坚。)
  • 有符號(hào)整數(shù)使用第 1 個(gè)比特位(通常被稱(chēng)為符號(hào)位)來(lái)表示這個(gè)數(shù)的正負(fù)粮宛。
  • 符號(hào)位為 0 代表正數(shù),為 1 代表負(fù)數(shù)卖宠。
  • 其余的比特位(通常被稱(chēng)為數(shù)值位)存儲(chǔ)了實(shí)際的值巍杈。
  • 有符號(hào)正整數(shù)和無(wú)符號(hào)數(shù)的存儲(chǔ)方式是一樣的,都是從 0 開(kāi)始算起扛伍。
  • 負(fù)數(shù)的存儲(chǔ)方式略有不同筷畦。它存儲(chǔ) 2 的 n 次方減去其實(shí)際值的絕對(duì)值,這里的 n 是數(shù)值位的位數(shù)刺洒。一個(gè) 8 比特位的數(shù)有 7 個(gè)比特位是數(shù)值位鳖宾,所以是 2 的 7 次方,即 128逆航。
  • 負(fù)數(shù)的表示通常被稱(chēng)為二進(jìn)制補(bǔ)碼鼎文。用這種方法來(lái)表示負(fù)數(shù)乍看起來(lái)有點(diǎn)奇怪,但它有幾個(gè)優(yōu)點(diǎn)纸泡。
    • 首先漂问,如果想對(duì) -1 和 -4 進(jìn)行加法運(yùn)算赖瞒,我們只需要對(duì)這兩個(gè)數(shù)的全部 8 個(gè)比特位執(zhí)行標(biāo)準(zhǔn)的二進(jìn)制相加(包括符號(hào)位),并且將計(jì)算結(jié)果中超出 8 位的數(shù)值丟棄蚤假。
    • 其次栏饮,使用二進(jìn)制補(bǔ)碼可以使負(fù)數(shù)的按位左移和右移運(yùn)算得到跟正數(shù)同樣的效果,即每向左移一位就將自身的數(shù)值乘以 2磷仰,每向右一位就將自身的數(shù)值除以 2袍嬉。要達(dá)到此目的,對(duì)有符號(hào)整數(shù)的右移有一個(gè)額外的規(guī)則:當(dāng)對(duì)有符號(hào)整數(shù)進(jìn)行按位右移運(yùn)算時(shí)灶平,遵循與無(wú)符號(hào)整數(shù)相同的規(guī)則伺通,但是對(duì)于移位產(chǎn)生的空白位使用符號(hào)位進(jìn)行填充,而不是用 0逢享。
    • 這個(gè)行為可以確保有符號(hào)整數(shù)的符號(hào)位不會(huì)因?yàn)橛乙七\(yùn)算而改變罐监,這通常被稱(chēng)為算術(shù)移位。

由于正數(shù)和負(fù)數(shù)的特殊存儲(chǔ)方式瞒爬,在對(duì)它們進(jìn)行右移的時(shí)候弓柱,會(huì)使它們?cè)絹?lái)越接近 0。
在移位的過(guò)程中保持符號(hào)位不變侧但,意味著負(fù)整數(shù)在接近 0 的過(guò)程中會(huì)一直保持為負(fù)矢空。

2. 溢出運(yùn)算符

  • 當(dāng)向一個(gè)整數(shù)類(lèi)型的常量或者變量賦予超過(guò)它容量的值時(shí),Swift 默認(rèn)會(huì)報(bào)錯(cuò)禀横,而不是允許生成一個(gè)無(wú)效的數(shù)屁药。
  • 這個(gè)行為為我們?cè)谶\(yùn)算過(guò)大或者過(guò)小的數(shù)時(shí)提供了額外的安全性。
  • 例如柏锄,Int16 型整數(shù)能容納的有符號(hào)整數(shù)范圍是 -32768 到 32767酿箭。當(dāng)為一個(gè) Int16 類(lèi)型的變量或常量賦予的值超過(guò)這個(gè)范圍時(shí),系統(tǒng)就會(huì)報(bào)錯(cuò):
var potentialOverflow = Int16.max
// potentialOverflow 的值是 32767绢彤,這是 Int16 能容納的最大整數(shù)
potentialOverflow += 1
// 這里會(huì)報(bào)錯(cuò)
  • 在賦值時(shí)為過(guò)大或者過(guò)小的情況提供錯(cuò)誤處理七问,能讓我們?cè)谔幚磉吔缰禃r(shí)更加靈活。
  • 然而茫舶,當(dāng)你希望的時(shí)候也可以選擇讓系統(tǒng)在數(shù)值溢出的時(shí)候采取截?cái)嗵幚恚菆?bào)錯(cuò)刹淌。
  • Swift 提供的三個(gè)溢出運(yùn)算符來(lái)讓系統(tǒng)支持整數(shù)溢出運(yùn)算饶氏。這些運(yùn)算符都是以 & 開(kāi)頭的:
    • 溢出加法 &+
    • 溢出減法 &-
    • 溢出乘法 &*

2. 1數(shù)值溢出

  • 數(shù)值有可能出現(xiàn)上溢或者下溢。
  • 這個(gè)示例演示了當(dāng)我們對(duì)一個(gè)無(wú)符號(hào)整數(shù)使用溢出加法(&+)進(jìn)行上溢運(yùn)算時(shí)會(huì)發(fā)生什么:
//   unsignedOverflow 被初始化為 UInt8 所能容納的最大整數(shù)(255有勾,以二進(jìn)制表示即 11111111)
var unsignedOverflow = UInt8.max    // unsignedOverflow 等于 UInt8 所能容納的最大整數(shù) 255
/*
使用溢出加法運(yùn)算符(&+)對(duì)其進(jìn)行加 1 運(yùn)算
這使得它的二進(jìn)制表示正好超出 UInt8 所能容納的位數(shù)疹启,也就導(dǎo)致了數(shù)值的溢出
*/
unsignedOverflow = unsignedOverflow &+ 1    // 此時(shí) unsignedOverflow 等于 0
  • 當(dāng)允許對(duì)一個(gè)無(wú)符號(hào)整數(shù)進(jìn)行下溢運(yùn)算時(shí)也會(huì)產(chǎn)生類(lèi)似的情況。
  • 這里有一個(gè)使用溢出減法運(yùn)算符(&-)的例子:
//  UInt8 型整數(shù)能容納的最小值是 0蔼卡,以二進(jìn)制表示即 00000000
var unsignedOverflow = UInt8.min    // unsignedOverflow 等于 UInt8 所能容納的最小整數(shù) 0
//  當(dāng)使用溢出減法運(yùn)算符對(duì)其進(jìn)行減 1 運(yùn)算時(shí)
//  數(shù)值會(huì)產(chǎn)生下溢并被截?cái)酁?11111111喊崖, 也就是十進(jìn)制數(shù)值的 255
unsignedOverflow = unsignedOverflow &- 1    // 此時(shí) unsignedOverflow 等于 255
  • 溢出也會(huì)發(fā)生在有符號(hào)整型上。
  • 針對(duì)有符號(hào)整型的所有溢出加法或者減法運(yùn)算都是按位運(yùn)算的方式執(zhí)行的,符號(hào)位也需要參與計(jì)算荤懂,正如 按位左移茁裙、右移運(yùn)算符 所描述的。
//  Int8 型整數(shù)能容納的最小值是 -128节仿,以二進(jìn)制表示即 10000000
var signedOverflow = Int8.min   // signedOverflow 等于 Int8 所能容納的最小整數(shù) -128
/*  
當(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 型整所能容納的最大值
*/
signedOverflow = signedOverflow &- 1    // 此時(shí) signedOverflow 等于 127
  • 對(duì)于無(wú)符號(hào)與有符號(hào)整型數(shù)值來(lái)說(shuō)矾瘾,當(dāng)出現(xiàn)上溢時(shí),它們會(huì)從數(shù)值所能容納的最大數(shù)變成最小數(shù)箭启。
  • 同樣地壕翩,當(dāng)發(fā)生下溢時(shí),它們會(huì)從所能容納的最小數(shù)變成最大數(shù)傅寡。

3. 優(yōu)先級(jí)和結(jié)合性

  • 運(yùn)算符的優(yōu)先級(jí)使得一些運(yùn)算符優(yōu)先于其他運(yùn)算符戈泼;它們會(huì)先被執(zhí)行。
  • 結(jié)合性定義了相同優(yōu)先級(jí)的運(yùn)算符是如何結(jié)合的赏僧,也就是說(shuō)大猛,是與左邊結(jié)合為一組,還是與右邊結(jié)合為一組淀零。
  • 可以將其理解為“它們是與左邊的表達(dá)式結(jié)合的”挽绩,或者“它們是與右邊的表達(dá)式結(jié)合的”。
  • 當(dāng)考慮一個(gè)復(fù)合表達(dá)式的計(jì)算順序時(shí)驾中,運(yùn)算符的優(yōu)先級(jí)和結(jié)合性是非常重要的唉堪。舉例來(lái)說(shuō),運(yùn)算符優(yōu)先級(jí)解釋了為什么下面這個(gè)表達(dá)式的運(yùn)算結(jié)果會(huì)是 17肩民。
2 + 3 % 4 * 5   // 結(jié)果是 17
  • 而乘法運(yùn)算與取余運(yùn)算的優(yōu)先級(jí)相同唠亚。
  • 這時(shí)為了得到正確的運(yùn)算順序,還需要考慮結(jié)合性持痰。
  • 乘法運(yùn)算與取余運(yùn)算都是左結(jié)合的灶搜。
  • 可以將這考慮成,從它們的左邊開(kāi)始為這兩部分表達(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茅郎。

4. 運(yùn)算符函數(shù)

  • 類(lèi)和結(jié)構(gòu)體可以為現(xiàn)有的運(yùn)算符提供自定義的實(shí)現(xiàn)。這通常被稱(chēng)為運(yùn)算符重載鹏溯。
  • 下面的例子展示了如何讓自定義的結(jié)構(gòu)體支持加法運(yùn)算符(+)。
  • 算術(shù)加法運(yùn)算符是一個(gè)二元運(yùn)算符淹仑,因?yàn)樗菍?duì)兩個(gè)值進(jìn)行運(yùn)算丙挽,同時(shí)它還可以稱(chēng)為中綴運(yùn)算符肺孵,因?yàn)樗霈F(xiàn)在兩個(gè)值中間。
//  一個(gè)名為 Vector2D 的結(jié)構(gòu)體用來(lái)表示二維坐標(biāo)向量 (x, y)
struct Vector2D {
    var x = 0.0, y = 0.0
}
?
//  一個(gè)可以將兩個(gè) Vector2D 結(jié)構(gòu)體實(shí)例進(jìn)行相加的運(yùn)算符函數(shù)
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è)類(lèi)方法颜阐,并且函數(shù)的名字與它要進(jìn)行重載的 + 名字一致平窘。
因?yàn)榧臃ㄟ\(yùn)算并不是一個(gè)向量必需的功能,所以這個(gè)類(lèi)方法被定義在 Vector2D 的一個(gè)擴(kuò)展中瞬浓,而不是 Vector2D 結(jié)構(gòu)體聲明內(nèi)初婆。
而算術(shù)加法運(yùn)算符是二元運(yùn)算符,所以這個(gè)運(yùn)算符函數(shù)接收兩個(gè)類(lèi)型為 Vector2D 的參數(shù)猿棉,同時(shí)有一個(gè) Vector2D 類(lèi)型的返回值磅叛。
在這個(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è)類(lèi)方法可以在任意兩個(gè) Vector2D 實(shí)例中間作為中綴運(yùn)算符來(lái)使用:
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)

4.1 前綴和后綴運(yùn)算符

  • 類(lèi)與結(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 類(lèi)型實(shí)現(xiàn)了一元運(yùn)算符(-a)撒桨。由于該運(yùn)算符是前綴運(yùn)算符查刻,所以這個(gè)函數(shù)需要加上 prefix 修飾符。

  • 對(duì)于簡(jiǎn)單數(shù)值凤类,一元負(fù)號(hào)運(yùn)算符可以對(duì)它們的正負(fù)性進(jìn)行改變穗泵。對(duì)于 Vector2D 來(lái)說(shuō),該運(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í)例

4.2 復(fù)合賦值運(yùn)算符

  • 復(fù)合賦值運(yùn)算符將賦值運(yùn)算符(=)與其它運(yùn)算符進(jìn)行結(jié)合谜疤。
  • 例如佃延,將加法與賦值結(jié)合成加法賦值運(yùn)算符(+=)。
  • 在實(shí)現(xiàn)的時(shí)候茎截,需要把運(yùn)算符的左參數(shù)設(shè)置成 inout 類(lèi)型苇侵,因?yàn)檫@個(gè)參數(shù)的值會(huì)在運(yùn)算符函數(shù)內(nèi)直接被修改。
  • 在下面的例子中企锌,對(duì) Vector2D 實(shí)例實(shí)現(xiàn)了一個(gè)加法賦值運(yùn)算符函數(shù):
extension Vector2D {
    static func += (left: inout Vector2D, right: Vector2D) {
        left = left + right
    }
}
  • 因?yàn)榧臃ㄟ\(yùn)算在之前已經(jīng)定義過(guò)了,所以在這里無(wú)需重新定義于未。
  • 在這里可以直接利用現(xiàn)有的加法運(yùn)算符函數(shù)撕攒,用它來(lái)對(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)
注意
    1. 不能對(duì)默認(rèn)的賦值運(yùn)算符(=)進(jìn)行重載。只有復(fù)合賦值運(yùn)算符可以被重載抖坪。
    2. 同樣地萍鲸,也無(wú)法對(duì)三元條件運(yùn)算符 (a ? b : c) 進(jìn)行重載。

4.3 等價(jià)運(yùn)算符

  • 通常情況下擦俐,自定義的類(lèi)和結(jié)構(gòu)體沒(méi)有對(duì)等價(jià)運(yùn)算符進(jìn)行默認(rèn)實(shí)現(xiàn)脊阴,等價(jià)運(yùn)算符通常被稱(chēng)為相等運(yùn)算符(==)與不等運(yùn)算符(!=)。
  • 為了使用等價(jià)運(yùn)算符對(duì)自定義的類(lèi)型進(jìn)行判等運(yùn)算蚯瞧,需要為“相等”運(yùn)算符提供自定義實(shí)現(xiàn)嘿期,實(shí)現(xiàn)的方法與其它中綴運(yùn)算符一樣, 并且增加對(duì)標(biāo)準(zhǔn)庫(kù) Equatable 協(xié)議的遵循:
extension Vector2D: Equatable {
    static func == (left: Vector2D, right: Vector2D) -> Bool {
        return (left.x == right.x) && (left.y == right.y)
    }
}

上述代碼實(shí)現(xiàn)了“相等”運(yùn)算符( = = ) 來(lái)判斷兩個(gè) Vector2D 實(shí)例是否相等。
對(duì)于 Vector2D 來(lái)說(shuō)埋合,“相等”意味著“兩個(gè)實(shí)例的 x 和 y 都相等”备徐,這也是代碼中用來(lái)進(jìn)行判等的邏輯。
如果你已經(jīng)實(shí)現(xiàn)了“相等”運(yùn)算符甚颂,通常情況下你并不需要自己再去實(shí)現(xiàn)“不等”運(yùn)算符(!=)蜜猾。
標(biāo)準(zhǔn)庫(kù)對(duì)于“不等”運(yùn)算符提供了默認(rèn)的實(shí)現(xiàn),它簡(jiǎn)單地將“相等”運(yùn)算符的結(jié)果進(jìn)行取反后返回振诬。

  • 現(xiàn)在我們可以使用這兩個(gè)運(yùn)算符來(lái)判斷兩個(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ù)簡(jiǎn)單情況下蹭睡,您可以使用 Swift 為您提供的等價(jià)運(yùn)算符默認(rèn)實(shí)現(xiàn)。Swift 為以下數(shù)種自定義類(lèi)型提供等價(jià)運(yùn)算符的默認(rèn)實(shí)現(xiàn):
    • 只擁有存儲(chǔ)屬性赶么,并且它們?nèi)甲裱?Equatable 協(xié)議的結(jié)構(gòu)體
    • 只擁有關(guān)聯(lián)類(lèi)型肩豁,并且它們?nèi)甲裱?Equatable 協(xié)議的枚舉
    • 沒(méi)有關(guān)聯(lián)類(lèi)型的枚舉
  • 在類(lèi)型原始的聲明中聲明遵循 Equatable 來(lái)接收這些默認(rèn)實(shí)現(xiàn)。
  • 下面為三維位置向量 (x, y, z) 定義的 Vector3D 結(jié)構(gòu)體禽绪,與 Vector2D 類(lèi)似蓖救。由于 x,y 和 z 屬性都是 Equatable 類(lèi)型印屁,Vector3D 獲得了默認(rèn)的等價(jià)運(yùn)算符實(shí)現(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.”

5. 自定義運(yùn)算符

  • 除了實(shí)現(xiàn)標(biāo)準(zhǔn)運(yùn)算符,在 Swift 中還可以聲明和實(shí)現(xiàn)自定義運(yùn)算符雄人。
  • 可以用來(lái)自定義運(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 中并沒(méi)有已知的意義旗吁,因此在針對(duì) Vector2D 實(shí)例的特定上下文中踩萎,給予了它自定義的意義。
對(duì)這個(gè)示例來(lái)講很钓,+++ 被實(shí)現(xiàn)為“前綴雙自增”運(yùn)算符香府。
它使用了前面定義的復(fù)合加法運(yùn)算符來(lái)讓矩陣與自身進(jìn)行相加董栽,從而讓 Vector2D 實(shí)例的 x 屬性和 y 屬性值翻倍。

  • 你可以像下面這樣通過(guò)對(duì) Vector2D 添加一個(gè) +++ 類(lèi)方法企孩,來(lái)實(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)

5.1 自定義中綴運(yùn)算符的優(yōu)先級(jí)

  • 每個(gè)自定義中綴運(yùn)算符都屬于某個(gè)優(yōu)先級(jí)組锭碳。
  • 優(yōu)先級(jí)組指定了這個(gè)運(yùn)算符相對(duì)于其他中綴運(yùn)算符的優(yōu)先級(jí)和結(jié)合性。
  • 優(yōu)先級(jí)和結(jié)合性 中詳細(xì)闡述了這兩個(gè)特性是如何對(duì)中綴運(yùn)算符的運(yùn)算產(chǎn)生影響的勿璃。
  • 而沒(méi)有明確放入某個(gè)優(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í)組中。

注意
    1. 當(dāng)定義前綴與后綴運(yùn)算符的時(shí)候胁编,我們并沒(méi)有指定優(yōu)先級(jí)厢钧。
    2. 然而,如果對(duì)同一個(gè)值同時(shí)使用前綴與后綴運(yùn)算符嬉橙,則后綴運(yùn)算符會(huì)先參與運(yùn)算早直。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市市框,隨后出現(xiàn)的幾起案子霞扬,更是在濱河造成了極大的恐慌,老刑警劉巖枫振,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喻圃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡粪滤,警方通過(guò)查閱死者的電腦和手機(jī)斧拍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)杖小,“玉大人肆汹,你說(shuō)我怎么就攤上這事∮枞ǎ” “怎么了昂勉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)扫腺。 經(jīng)常有香客問(wèn)我岗照,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任谴返,我火速辦了婚禮煞肾,結(jié)果婚禮上咧织,老公的妹妹穿的比我還像新娘嗓袱。我一直安慰自己,他們只是感情好习绢,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布渠抹。 她就那樣靜靜地躺著,像睡著了一般闪萄。 火紅的嫁衣襯著肌膚如雪梧却。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天败去,我揣著相機(jī)與錄音放航,去河邊找鬼。 笑死圆裕,一個(gè)胖子當(dāng)著我的面吹牛广鳍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吓妆,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼赊时,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了行拢?” 一聲冷哼從身側(cè)響起祖秒,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎舟奠,沒(méi)想到半個(gè)月后竭缝,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沼瘫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年抬纸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晕鹊。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡松却,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出溅话,到底是詐尸還是另有隱情晓锻,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布飞几,位于F島的核電站砚哆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏屑墨。R本人自食惡果不足惜躁锁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一纷铣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧战转,春花似錦搜立、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至刁标,卻和暖如春颠通,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背膀懈。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工顿锰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人启搂。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓硼控,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親狐血。 傳聞我的和親對(duì)象是個(gè)殘疾皇子淀歇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 中文文檔 一、位運(yùn)算符 位操作符通常在諸如圖像處理和創(chuàng)建設(shè)備驅(qū)動(dòng)等底層開(kāi)發(fā)中使用匈织,使用它可以單獨(dú)操作數(shù)據(jù)結(jié)構(gòu)中原始...
    伯wen閱讀 1,490評(píng)論 0 2
  • 本章將會(huì)介紹 模塊和源文件訪(fǎng)問(wèn)級(jí)別訪(fǎng)問(wèn)控制語(yǔ)法自定義類(lèi)型子類(lèi)常量缀匕、變量纳决、屬性、下標(biāo)構(gòu)造器協(xié)議擴(kuò)展泛型類(lèi)型別名位運(yùn)算...
    寒橋閱讀 883評(píng)論 0 2
  • 案例代碼下載 高級(jí)運(yùn)算符 除了基本運(yùn)算符中描述的運(yùn)算符之外乡小,Swift還提供了幾個(gè)執(zhí)行更復(fù)雜值操作的高級(jí)運(yùn)算符阔加。這...
    酒茶白開(kāi)水閱讀 858評(píng)論 0 0
  • 高級(jí)運(yùn)算符 文檔地址 作為 基本運(yùn)算符 的補(bǔ)充,Swift 提供了幾個(gè)高級(jí)運(yùn)算符執(zhí)行對(duì)數(shù)傳值進(jìn)行更加復(fù)雜的操作满钟。這...
    hrscy閱讀 842評(píng)論 0 2
  • 給定一系列正整數(shù)胜榔,請(qǐng)按要求對(duì)數(shù)字進(jìn)行分類(lèi),并輸出以下 5 個(gè)數(shù)字:A1??= 能被 5 整除的數(shù)字中所有偶數(shù)的和湃番;...
    胖胖胖到不能呼吸閱讀 730評(píng)論 0 0