高級運算符
與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)的行為更為簡單。
我們不用被預(yù)定義的運算符所限制。在swift當(dāng)中可以自由地定義中綴、前綴、后綴和賦值運算符,以及相應(yīng)地優(yōu)先級與結(jié)合性。這些運算符在代碼中可以像預(yù)設(shè)的運算符一樣使用,我們甚至可以擴展已有的類型以支持自定義的運算符。
位運算符
位運算符 可以操作一個數(shù)據(jù)結(jié)構(gòu)中每個獨立的位。它們通常被用在底層開發(fā)中,比如圖形編程和創(chuàng)建設(shè)備驅(qū)動。位運算符在處理外部資源數(shù)據(jù)時也十分有用,比如對自定義通信協(xié)議傳輸?shù)臄?shù)據(jù)進行編碼和解碼。
Swift支持c語言中的全部位運算符,具體如下:
- 按位取反運算符 (~)可以對一個數(shù)值的全部位進行取反:按位取反操作是一個前置運算符,需要直接放在操作數(shù)的之前,并且它們之間不能添加任何空格。
let inintialBits : UInt8 = 0b00001111
let invertedBits = ~initialsBits //等于0b11110000
按位與運算符
按位與運算符 (&)可以對兩個數(shù)的比特位進行合并。它返回一個新的數(shù),只有當(dāng)兩個操作數(shù)的對應(yīng)位都為1的時候,該數(shù)的對應(yīng)位才為 1.
let first: UInt8 = 0b11110000
let second: UInt8 = 0b00011111
let thrid = first & second //0b00010000
按位或運算符
按位或運算符(|)可以對兩個數(shù)的比特位進行比較,它返回一個新的數(shù),只有兩個操作數(shù)的對應(yīng)位中有任意一個為1時,該數(shù)的對應(yīng)位就是1.
按位異或運算符
按位異或運算符 (^)可以對兩個數(shù)的比特位進行比較。它返回一個新的數(shù),當(dāng)兩個操作數(shù)的對應(yīng)位不相同時,該數(shù)的對應(yīng)位就為 1.
let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits // 等于 00010001
按位左移/右移 運算符
按位左移運算符 (<<)和按位右移運算符 (>>)可以對一個數(shù)進行指定位數(shù)的左移和右移,但是需要遵守下面定義的規(guī)則。
對一個數(shù)進行按位左移或按位右移,相當(dāng)于對這個數(shù)進行乘以2 或除以2的運算。將一個整數(shù)左移一位,等價于對這個數(shù)乘以 2 ,同樣地,將一個整數(shù)右移一位,等價于將這個數(shù)除以 2 。
1、無符號整型進行移位操作
對無符號的整型進行移位的規(guī)則如下:
- 已經(jīng)存在的比特位按指定的位數(shù)進行左移和右移。
- 任何移動超出整型存儲邊界的位都會被丟棄。
- 用0來填充移動后產(chǎn)生的空白位。
2、有符號整型的移位操作
對比無符號整型來說,有符號整型的移位操作相對復(fù)雜的多,這種復(fù)雜性源于有符號整型的二進制形式。
有符號整數(shù)使用第1個比特位(通常被稱為符號位)來表示這個數(shù)的正負(fù)。符號為0 代表正數(shù),為1代表負(fù)數(shù)。
其余的比特位(通常被稱為數(shù)值位)存儲了這個數(shù)的真實值。有符號正整數(shù)和無符號數(shù)的存儲方式是一樣的。
0 0000 100 = 4
符號位為0,說明這是一個正數(shù),另外7位則代表了十進制數(shù)值 4 的二進制表示。
負(fù)數(shù)的存儲方式略有不同。它存儲的是 2 的n次方減去它的真實值絕對值,這里的n為數(shù)值位數(shù)。一個8位的數(shù)有7個數(shù)值位,所以是 2 的7次方,即128.
1 1111100 = -4
這次的符號位為 1 ,說明這是一個負(fù)數(shù),另外 7 位則代表了數(shù)值 124 即(124 - 4)的二進制表示。
負(fù)數(shù)的表示通常被稱為二進制補碼表示法。
使用二進制補碼可以使負(fù)數(shù)的按位左移和右移操作得到跟正數(shù)同樣的效果,即每左移一位就將自身的數(shù)值乘以2,每向右一位就將自身的數(shù)值除以2 。 要達(dá)到此目的,對有符號整數(shù)的右移有一個額外的規(guī)則:
- 當(dāng)對整數(shù)進行按位右移操作時,遵循與無符號整數(shù)相同的規(guī)則,但是對于移位產(chǎn)生的空白位使用符號位進行填充,而不是用 0 。
這個行為可以確保有符號整數(shù)的符號位不會因為右移操作而改變,這通常被稱為算術(shù)移位。
溢出運算符
在默認(rèn)情況下,當(dāng)向一個整數(shù)賦值超過它容量的值時,Swift默認(rèn)會報錯,而不是生成一個無效的數(shù)。這個行為給我們操作過大或著過小的數(shù)的時候提供了額外的安全性。
然而,也可以選擇讓系統(tǒng)的數(shù)值溢出的時候采取截斷操作,而非報錯。可以使用Swift提供的三個溢出操作符來讓系統(tǒng)支持整數(shù)溢出運算。這些操作都是以&開頭的:
- 溢出加法 &+
- 溢出減法 &-
- 溢出乘法 &*
數(shù)值溢出
數(shù)值有可能出現(xiàn)上溢 或者 下溢 。
var unsignedValue = UInt8.max
print("\(unsignedValue)") //255
unsignedValue = unsignedValue &+ 1
print("\(unsignedValue)") //0
var value2 = UInt8.min
value2 = value2 &- 1
溢出也會發(fā)生在有符號整型數(shù)值上。在對有符號整型數(shù)值進行溢出加法或溢出減法運算時,符號位爺粗腰參與計算。
var value2 = Int8.min
value2 = value2 &- 1
總結(jié):對于無符號與有符號整型數(shù)值來說,當(dāng)出行上溢時,它們會從數(shù)值所能容納的最大數(shù)變?yōu)樽钚〉臄?shù)。同樣地,當(dāng)發(fā)生下溢時,它們會從所能容納的最小數(shù)變成最大的數(shù)。
優(yōu)先級和結(jié)合性
運算符的優(yōu)先級使得一些運算符優(yōu)先于其他運算符,高優(yōu)先級的運算符會先被計算。
結(jié)合性 定義了相同優(yōu)先級的運算符是如何結(jié)合的。
運算符函數(shù)
類和結(jié)構(gòu)體可以為現(xiàn)有的操作符提供自定義的實現(xiàn),這通常被稱為運算符重載。
struct Vector2D {
var x = 0.0, y = 0.0
}
func +(left: Vector2D, right: Vector2D) -> Vector2D{
return Vector2D(x: left.x + right.x , y: left.y + right.y)
}
let vector = Vector2D(x: 3, y: 1)
let vector2 = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + vector2
前綴和后綴運算符
類與結(jié)構(gòu)體也能提供標(biāo)準(zhǔn)單目運算符的實現(xiàn)。單目運算符只有一個操作目標(biāo)。當(dāng)運算符出現(xiàn)在操作目標(biāo)之前時,它就是前綴(prefix)的(比如, -a),而當(dāng)它出現(xiàn)在操作目標(biāo)之后時,它就是后綴(postfix)的(比如,i++)
要實現(xiàn)前綴或者后綴運算符,需要在聲明運算符函數(shù)的時候在func關(guān)鍵字之前指定prefix或者postfix 限定符:
prefix func - (vecotr : Vector2D) -> Vector2D {
return Vector2D(x: -vector.x, y: -vector.y)
}
符合賦值運算符
符合賦值運算符將賦值運算符(=)與其他運算符進行結(jié)合。比如,將加法與賦值結(jié)合成加法運算符(+=) 。在實際的時候,需要把運算符的左參數(shù)設(shè)置為 inout類型,因為這個參數(shù)的值會在運行符函數(shù)本身被修改。
func += (inout left: Vector2D, right: Vector2D) {
left = left + right
}
note:不能對默認(rèn)的賦值運算符(=)進行重載。只有組合賦值符可以被重載。同樣地,也無法對三目條件運算符 a ? b : c 進行重載。
等價操作符
自定義的類和結(jié)構(gòu)體沒有對等價操作符 進行默認(rèn)實現(xiàn),等價操作符通常被稱為 “相等” 操作符(==)與 “不等” 操作符 (!=) 。 對于自定義類型,Swift無法判斷其是否“相等”,因為“相等”的含義取決與這些自定義類型在你的代碼中扮演的角色。
func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
func != (left: Vector2D, right: Vector2D) -> Bool {
return !(left == right)
}
自定義運算符
除了實現(xiàn)標(biāo)準(zhǔn)運算符,在Swift當(dāng)中還可以聲明和實現(xiàn)自定義運算符。
新的運算符要在全局作用域內(nèi),使用 operator關(guān)鍵字進行聲明,同時還要指定prefix、infix或者postfix限定符:
struct Vector2D {
var x = 0.0, y = 0.0
}
prefix operator +++ {
}
prefix func +++ (inout vector: Vector2D) ->Vector2D {
vector.x += 1
vector.y += 1
return vector
}
var begin = Vector2D(x: 1.0, y: 4.0)
+++begin
print("\(begin)")
自定義中綴運算符的優(yōu)先級和結(jié)合性
自定義的中綴(infix)運算符也可以指定優(yōu)先級和結(jié)合性。
結(jié)合性 可取的值有 left 、right 和 none 。當(dāng)左結(jié)合運算符跟其他相同優(yōu)先級的左結(jié)合運算符寫在一起時,會跟左邊的操作數(shù)進行結(jié)合。同理,當(dāng)右結(jié)合運算符跟其他相同優(yōu)先級的右結(jié)合運算符寫在一起時,會跟右邊的操作符進行結(jié)合。而非結(jié)合運算符不能跟其他相同優(yōu)先級的運算符寫在一起。
結(jié)合性默認(rèn)值時none ,優(yōu)先級如果沒有指定則默認(rèn)為 100.
struct Vector2D {
var x = 0.0, y = 0.0
}
infix operator +- {
associativity left precedence 140
}
func +- (left: Vector2D,right:Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y - right.y)
}
let value1 = Vector2D(x: 1.0, y: 2.0)
let value2 = Vector2D(x: 3.0, y: 4.0)
let value = value1 +- value2
print(value)
note:當(dāng)定義前綴與后綴操作符的時候,我們并沒有指定優(yōu)先級。然而,如果對同一個操作數(shù)同時使用前綴與后綴操作符,則后綴操作符會先被執(zhí)行。(前綴和后綴操作符不能指定結(jié)合性和優(yōu)先級)