- Swift 運算符
- 基本運算符
- 高級運算符(包括 C 或 Objective-C 所有按位和移位運算符是掰。)
- 與 C 的算術(shù)運算符不同,Swift 算術(shù)運算符默認是不會溢出的耍休。
- 所有溢出行為都會被捕獲并報告為錯誤刃永。
- 如果想讓系統(tǒng)允許溢出行為,可用 Swift 支持溢出的運算符
- 如溢出加法運算符(
&+
)羊精。所有溢出運算符都以&
開頭的斯够。
- 中綴囚玫、前綴、后綴和賦值運算符读规,它們具有自定義的優(yōu)先級與關(guān)聯(lián)值抓督。
- 可擴展已有的類型以支持自定義運算符。
位運算符
- 操作數(shù)據(jù)結(jié)構(gòu)中束亏,每個獨立的比特位
- 使用場景:
- 底層開發(fā)中铃在,比如圖形編程和創(chuàng)建設(shè)備驅(qū)動
- 處理外部資源的原始數(shù)據(jù),對自定義通信協(xié)議傳輸?shù)臄?shù)據(jù)進行編碼和解碼
- Swift 支持 C 語言中的全部位運算符
Bitwise NOT Operator(按位取反運算符)
-
按位取反運算符(
~
)- 對一個數(shù)值的全部比特位進行取反:
- 前綴運算符碍遍,直接放在運算數(shù)之前定铜,并且它們之間不能添加任何空格:
let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // 等于 0b11110000
Bitwise AND Operator(按位與運算符)
-
按位與運算符(
&
) 對兩個數(shù)的比特位進行合并 - 返回一個新的數(shù),只有當(dāng)兩個數(shù)的對應(yīng)位都為
1
的時候怕敬,新數(shù)的對應(yīng)位才為1
:
let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8 = 0b00111111
let middleFourBits = firstSixBits & lastSixBits // 等于 00111100
Bitwise OR Operator(按位或運算符)
-
按位或運算符(
|
)可以對兩個數(shù)的比特位進行比較 - 只要兩個數(shù)的對應(yīng)位中有任意一個為
1
時揣炕,新數(shù)的對應(yīng)位就為1
:
let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits // 等于 11111110
Bitwise XOR Operator(按位異或運算符)
-
按位異或運算符,或稱“排外的或運算符”东跪、“互斥或”(
^
) - 當(dāng)兩個數(shù)的對應(yīng)位不相同時畸陡,新數(shù)的對應(yīng)位就為
1
,并且對應(yīng)位相同時則為0
:
let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits // 等于 00010001
Bitwise Left and Right Shift Operators(按位左移虽填、右移運算符)
-
按位左移運算符(
<<
) 和 按位右移運算符(>>
) - 對一個數(shù)的所有位進行指定位數(shù)的左移和右移
- 本質(zhì):相當(dāng)于對這個數(shù)進行乘以 2 或除以 2 的運算
- 左移一位罩锐,等價于將這個數(shù)乘以 2
- 右移一位,等價于將這個數(shù)除以 2卤唉。
無符號整數(shù)的移位運算
- 無符號整數(shù)進行移位的規(guī)則:
- 已存在的位按指定的位數(shù)進行左移和右移涩惑。
- 任何因移動而超出整型存儲范圍的位都會被丟棄。
- 用
0
來填充移位后產(chǎn)生的空白位桑驱。
- 藍色的數(shù)字是被移位的竭恬,灰色的數(shù)字是被拋棄的,橙色的
0
則是被填充進來的:
[圖片上傳失敗...(image-8779b-1609951379835)]
- 演示了 Swift 中的移位運算:
let shiftBits: UInt8 = 4 // 即二進制的 00000100
shiftBits << 1 // 00001000
shiftBits << 2 // 00010000
shiftBits << 5 // 10000000
shiftBits << 6 // 00000000
shiftBits >> 2 // 00000001
- 用移位運算對其他的數(shù)據(jù)類型進行編碼和解碼:
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
有符號整數(shù)的移位運算
以下的示例基于 8 比特的有符號整數(shù)押框,但原理對任何位數(shù)的有符號整數(shù)都是通用的岔绸。
-
有符號整數(shù)用第 1 個比特位(通常被稱為符號位)表示正負。
- 符號位為
0
代表正數(shù)橡伞,為1
代表負數(shù)盒揉。
- 符號位為
其余的比特位(通常被稱為數(shù)值位)存儲了實際的值。
有符號正整數(shù)和無符號數(shù)的存儲方式是一樣的兑徘,都是從
0
開始算起刚盈。-
這是值為
4
的Int8
型整數(shù)的二進制位表現(xiàn)形式:
值為
-4
的Int8
型整數(shù)的二進制表現(xiàn)形式:
- 符號位為
1
,說明這是一個負數(shù)挂脑,另外 7 個位則代表了數(shù)值124
(即128 - 4
)的二進制表示:
- 負數(shù)的表示通常被稱為二進制補碼
- 存儲最大值:
2
的n
次方減去其實際值的絕對值 - 一個 8 比特位的數(shù)有 7 個比特位是數(shù)值位藕漱,所以是
2
的7
次方欲侮,即128
。
- 存儲最大值:
- 對
-1
和-4
進行加法運算 - 只需要對這兩個數(shù)的全部 8 個比特位執(zhí)行標準的二進制相加(包括符號位)
- 將計算結(jié)果中超出 8 位的數(shù)值丟棄:
- 二進制補碼可以使負數(shù)的按位左移和右移運算得到跟正數(shù)同樣的效果
- 要達到此目的肋联,對有符號整數(shù)的右移有一個額外的規(guī)則:
- 當(dāng)對有符號整數(shù)進行按位右移運算時威蕉,遵循與無符號整數(shù)相同的規(guī)則
- 但是對于移位產(chǎn)生的空白位使用符號位進行填充,而不是用
0
橄仍。**
- 這通常被稱為算術(shù)移位忘伞。
- 移位的過程中保持符號位不變,意味著負整數(shù)在接近零的過程中會一直保持為負沙兰。
溢出運算符 - Overflow Operators
Swift 溢出會直接報錯
Int16 有符號整數(shù)范圍是 -32768 到 32767 氓奈,當(dāng)為一個Int16 型變量賦的值超過這個范圍時,系統(tǒng)就會報錯:
var potentialOverflow = Int16.max
// potentialOverflow equals 32767, which is the maximum value an Int16 can hold
potentialOverflow += 1
// this causes an error
- 故意想要溢出來截斷可用位的數(shù)字時鼎天,也可以選擇這么做而非報錯
- Swift 提供三個算數(shù)溢出運算符來讓系統(tǒng)支持整數(shù)溢出運算舀奶。這些運算符都是以 & 開頭的:
- 溢出加法 ( &+ )
- 溢出減法 ( &- )
- 溢出乘法 ( &* )
數(shù)值溢出
上溢或者下溢。
對一個無符號整數(shù)使用溢出加法(
&+
)進行上溢運算時會發(fā)生什么:
var unsignedOverflow = UInt8.max
// unsignedOverflow 等于 UInt8 所能容納的最大整數(shù) 255
unsignedOverflow = unsignedOverflow &+ 1
// 此時 unsignedOverflow 等于 0
- 如下圖所示斋射。數(shù)值溢出后育勺,仍然留在
UInt8
邊界內(nèi)的值是00000000
,也就是十進制數(shù)值的0
罗岖。
- 使用溢出減法運算符(
&-
)的例子:
var unsignedOverflow = UInt8.min
// unsignedOverflow 等于 UInt8 所能容納的最小整數(shù) 0
unsignedOverflow = unsignedOverflow &- 1
// 此時 unsignedOverflow 等于 255
- 對其進行減
1
運算時涧至,數(shù)值會產(chǎn)生下溢并被截斷為11111111
, 也就是十進制數(shù)值的255
- 溢出也會發(fā)生在有符號整型上桑包。
- 針對有符號整型的所有溢出加法或者減法運算都是按位運算的方式執(zhí)行的
- 符號位也需要參與計算南蓬,正如 按位左移、右移運算符 所描述的哑了。
var signedOverflow = Int8.min
// signedOverflow 等于 Int8 所能容納的最小整數(shù) -128
signedOverflow = signedOverflow &- 1
// 此時 signedOverflow 等于 127
- `Int8` 型整數(shù)能容納的最小值是 `-128`赘方,以二進制表示即 `10000000`。當(dāng)使用溢出減法運算符對其進行減 `1` 運算時弱左,符號位被翻轉(zhuǎn)窄陡,得到二進制數(shù)值 `01111111`,也就是十進制數(shù)值的 `127`拆火,這個值也是 `Int8` 型整所能容納的最大值跳夭。
![](https://docs.swift.org/swift-book/_images/overflowSignedSubtraction_2x.png)
- 對于無符號與有符號整型數(shù)值來說,當(dāng)出現(xiàn)上溢時们镜,它們會從數(shù)值所能容納的最大數(shù)變成最小數(shù)币叹。
- 當(dāng)發(fā)生下溢時,它們會從所能容納的最小數(shù)變成最大數(shù)憎账。
## 優(yōu)先級和結(jié)合性
- 場景:計算順序
- 高優(yōu)先級的運算符會先被計算
- *結(jié)合性*定義了**相同優(yōu)先級的運算符**是如何結(jié)合的套硼,是與左邊結(jié)合為一組卡辰,還是與右邊結(jié)合為一組胞皱。
```swift
2 + 3 % 4 * 5
// 結(jié)果是 17
-
從左到右進行運算
- 2 + 3 = 5
- 5 % 4 = 1
- 1 * 5 = 5
與 C 語言類似邪意,在 Swift 中,乘法運算符(
*
)與取余運算符(%
)的優(yōu)先級高于加法運算符(+
)乘法運算與取余運算的優(yōu)先級相同
為這兩部分表達式都隱式地加上括號:
2 + ((3 % 4) * 5)
-
(3 % 4)
等于3
反砌,所以表達式相當(dāng)于:
2 + (3 * 5)
-
3 * 5
等于15
雾鬼,所以表達式相當(dāng)于:
2 + 15
- 因此計算結(jié)果為
17
。
運算符函數(shù)
運算符重載:類和結(jié)構(gòu)體宴树,為現(xiàn)有運算符提供自定義的實現(xiàn)
讓自定義的結(jié)構(gòu)體支持加法運算符(
+
)中綴運算符:算術(shù)加法運算符是一個二元運算符策菜,因為它是對兩個值進行運算,出現(xiàn)在兩個值中間酒贬。
定義了一個名為
Vector2D
的結(jié)構(gòu)體用來表示二維坐標向量(x, y)
定義了一個可以將兩個
Vector2D
結(jié)構(gòu)體實例進行相加的運算符函數(shù):
struct Vector2D {
var x = 0.0, y = 0.0
}
extension Vector2D {
// 接收兩個類型為 Vector2D 的參數(shù)又憨,同時有一個 Vector2D 類型的返回值
static func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
}
- 因為加法運算并不是一個向量必需的功能,所以這個類方法被定義在
Vector2D
的一個擴展中 - 函數(shù)返回一個新的
Vector2D
實例锭吨,這個實例的x
和y
分別等于作為參數(shù)的兩個實例的x
和y
的值之和
- 可以在任意兩個
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)
考蕾。這個過程如下圖示:
前綴和后綴運算符
-
一元運算符只運算一個值
- 運算符出現(xiàn)在值之前時祸憋,它就是前綴的(例如
-a
) - 出現(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)
}
}
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ù)合賦值運算符:賦值運算符(
=
)與其它運算符進行結(jié)合- 如肖卧,將加法與賦值結(jié)合成加法賦值運算符(
+=
)
- 如肖卧,將加法與賦值結(jié)合成加法賦值運算符(
- 實現(xiàn)的時候蚯窥,需要把運算符的左參數(shù)設(shè)置成
inout
類型,因為這個參數(shù)的值會在運算符函數(shù)內(nèi)直接被修改
extension Vector2D {
static func += (left: inout Vector2D, right: Vector2D) {
left = left + right
}
}
- 加法運算在之前已經(jīng)定義過了塞帐,所以在這里無需重新定義
- 直接利用現(xiàn)有的加法運算符函數(shù)沟沙,用它來對左值和右值進行相加,并再次賦值給左值
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)
不能對默認的賦值運算符(
=
)進行重載只有復(fù)合賦值運算符可以被重載
也無法對三元條件運算符 (
a ? b : c
) 進行重載
等價運算符
- 等價運算符通常被稱為相等運算符(
==
)與不等運算符(!=
) - 場景:自定義的類和結(jié)構(gòu)體對等價運算符進行默認實現(xiàn)
- 實現(xiàn)的方法與其它中綴運算符一樣, 并且增加對標準庫
Equatable
協(xié)議的遵循:
extension Vector2D: Equatable {
static func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
}
標準庫
Equatable
協(xié)議對于“不等”運算符有默認的實現(xiàn)壁榕,它簡單地將“相等”運算符的結(jié)果進行取反后返回使用
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.”
自定義運算符
- 場景:除了重載運算符矛紫,還可以聲明和實現(xiàn)自定義運算符
- 用來自定義運算符的字符列表請參考 運算符
- 語法:
-
operator
關(guān)鍵字:在全局作用域內(nèi)進行定義 - 指定
prefix
、infix
或者postfix
修飾符
-
prefix operator +++
-
+++
被實現(xiàn)為“前綴雙自增”運算符- 使用了前面定義的復(fù)合加法運算符來讓矩陣與自身進行相加
- 從而讓
Vector2D
實例的x
屬性和y
屬性值翻倍
- 實現(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)先級
- 場景:相對于前綴牌里、后綴這種單目運算符颊咬,中綴為雙目運算符
- 定義了一個新的自定義中綴運算符
+-
,此運算符屬于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ì)上是屬于“相加型”運算符态辛,所以將它放置在
+
和-
等默認中綴“相加型”運算符相同的優(yōu)先級組中麸澜。 - 關(guān)于 Swift 標準庫提供的運算符,以及完整的運算符優(yōu)先級組和結(jié)合性設(shè)置奏黑,請參考 運算符聲明炊邦。
- 而更多關(guān)于優(yōu)先級組以及自定義操作符和優(yōu)先級組的語法编矾,請參考 運算符聲明。
當(dāng)定義前綴與后綴運算符的時候馁害,我們并沒有指定優(yōu)先級窄俏。
然而,如果對同一個值同時使用前綴與后綴運算符碘菜,則后綴運算符會先參與運算凹蜈。