基本运算符
术语
- 一元运算符
对单一操作对象操作(-a)。一元运算符分前置运算符和后置运算符,前置运算符需紧跟在操作对象之前(!b),后置运算符需紧跟在操作对象之后(c!)。 - 二元运算符
操作两个操作对象(2 + 3),是中置的,因为它们出现在两个操作对象之间。 - 三元运算符
操作三个操作对象,和 C 语言一样,Swift只有一个三元运算符,就是三目运算符(a ? b : c)。
赋值运算符
let b = 10
var a = 5
a = b // a = 10
如果赋值的右边是一个多元组,它的元素可以马上被分解成多个常量或变量:
let (x, y) = (1, 2)
// 现在 x 等于 1,y 等于 2
算术运算符
- 加法
1 + 2 - 减法
3 - 1 - 乘法
2 * 3 - 除法
4 / 2 - 求余
5 % 2 - 一元负号
-5 - 一元正号
+5
组合赋值运算符
var a = 1
a += 2 // a 现在是 3
a -= 1 // a 现在是 2
a *= 2 // a 现在是 4
a /= 2 // a 现在是 2
比较运算符
- 等于
a == b - 不等于
a != b - 大于
a > b - 小于
a < b - 大于等于
a >= b - 小于等于
a <= b
三目运算符
三目运算符的特殊在于它是有三个操作数的运算符,它的形式是问题 ? 答案 1 : 答案 2。它简洁地表达根据问题成立与否作出二选一的操作。如果问题成立,返回答案 1的结果;反之返回答案 2的结果。
var ret = true
let number = ret ? 30 : 10 // ret == true, number = 30
空合运算符
空合运算符a ?? b将对可选类型a进行空判断,如果a包含一个值就进行解封,否则就返回一个默认值b。表达式a必须是 Optional类型。默认值b的类型必须要和a存储值的类型保持一致。
var str: String?
let name = str ?? "Carson" // name = "Carson"
区间运算符
闭区间运算符
闭区间运算符a...b定义一个包含从a到b(包括a和b)的所有值的区间。a的值不能超过b。
for index in 1...5 {
print("\(index)")
}
// 1
// 2
// 3
// 4
// 5
半开区间运算符
半开区间运算符a..<b定义一个从a到b(不包括 b)的区间。 之所以称为半开区间,是因为该区间包含第一个值而不包括最后的值。
for index in 1..<5 {
print("\(index)")
}
// 1
// 2
// 3
// 4
逻辑运算符
- 逻辑非
!a
对一个布尔值取反,使得true变false,false变true。 - 逻辑与
a && b
只有a和b的值都为true时,整个表达式的值才会是true。 - 逻辑或
a || b
两个逻辑表达式的其中一个为true,整个表达式就为true。
注意:
可以组合多个逻辑运算符来表达一个复合逻辑,使用括号来明确优先级
高级运算符
位运算符
按位取反运算符
按位取反运算符~可以对一个数值的全部比特位进行取反。
let a: UInt8 = 0b00001111
let b = ~a // 等于 0b11110000
按位与运算符
按位与运算符&可以对两个数的比特位进行合并。它返回一个新的数,只有当两个数的对应位都为1的时候,新数的对应位才为1:
let a: UInt8 = 0b11111100
let b: UInt8 = 0b00111111
let c = a & b // 等于 00111100
按位或运算符
按位或运算符|可以对两个数的比特位进行比较。它返回一个新的数,只要两个数的对应位中有任意一个为1时,新数的对应位就为1:
let a: UInt8 = 0b10110010
let b: UInt8 = 0b01011110
let c = a | b // 等于 11111110
按位异或运算符
按位异或运算符^可以对两个数的比特位进行比较。它返回一个新的数,当两个数的对应位不相同时,新数的对应位就为1:
let a: UInt8 = 0b00010100
let b: UInt8 = 0b00000101
let c = a ^ b // 等于 00010001
按位左移、右移运算符
按位左移运算符<<和按位右移运算符>>可以对一个数的所有位进行指定位数的左移和右移,但是需要遵守下面定义的规则。
将一个整数左移一位,等价于将这个数乘以2,同样地,将一个整数右移一位,等价于将这个数除以2。
-
无符号整数
- 已经存在的位按指定的位数进行左移和右移。
- 任何因移动而超出整型存储范围的位都会被丢弃。
- 用 0 来填充移位后产生的空白位。
let a: UInt8 = 4 // 即二进制的 00000100 a << 1 // 00001000 a << 2 // 00010000 a << 5 // 10000000 a << 6 // 00000000 a >> 2 // 00000001 -
有符号整数的移位运算
有符号整数使用第1个比特位(通常被称为符号位)来表示这个数的正负。符号位为0代表正数,为1代表负数。
符号位为0,说明这是一个正数,另外7位则代表了十进制数值4的二进制表示。负数的存储方式略有不同。它存储的值的绝对值等于
2的n次方减去它的实际值(也就是数值位表示的值),这里的n为数值位的比特位数。一个8比特位的数有7个比特位是数值位,所以是2的7次方,即128。这是值为
-4的Int8型整数的二进制位表现形式:左移
每向左移一位就将自身的数值乘以2,每向右一位就将自身的数值除以2。
右移
当对整数进行按位右移运算时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用符号位进行填充,而不是用0。
这个行为可以确保有符号整数的符号位不会因为右移运算而改变,这通常被称为算术移位。由于正数和负数的特殊存储方式,在对它们进行右移的时候,会使它们越来越接近
0。在移位的过程中保持符号位不变,意味着负整数在接近0的过程中会一直保持为负。
溢出运算符
在默认情况下,当向一个整数赋予超过它容量的值时,Swift默认会报错,而不是生成一个无效的数。这个行为为我们在运算过大或着过小的数的时候提供了额外的安全性。
例如,UInt8型整数能容纳的有符号整数范围是0到 255,当为一个UInt8型变量赋的值超过这个范围时,系统就会报错:
var a = UInt8.max
// a 的值是 256,这是 UInt8 能容纳的最大整数
a += 1
// 这里会报错
然而,也可以选择让系统在数值溢出的时候采取截断处理,而非报错。可以使用Swift提供的三个溢出运算符来让系统支持整数溢出运算。这些运算符都是以&开头的:
- 溢出加法
&+ - 溢出减法
&- - 溢出乘法
&*
数值溢出
数值有可能出现上溢或者下溢。
这个示例演示了当我们对一个无符号整数使用溢出加法&+进行上溢运算时会发生什么:
var a = UInt8.max
// a 等于 UInt8 所能容纳的最大整数 255
a = a &+ 1
// 此时 a 等于 0
a被初始化为UInt8所能容纳的最大整数(255,以二进制表示即 11111111)。然后使用了溢出加法运算符&+对其进行加1运算。这使得它的二进制表示正好超出UInt8所能容纳的位数,也就导致了数值的溢出,如下图所示。数值溢出后,留在UInt8边界内的值是00000000,也就是十进制数值的0。

同样地,当我们对一个无符号整数使用溢出减法
&-进行下溢运算时也会产生类似的现象:
var b = UInt8.min
// b 等于 UInt8 所能容纳的最小整数 0
b = b &- 1
// 此时 b 等于 255
UInt8型整数能容纳的最小值是0,以二进制表示即00000000。当使用溢出减法运算符&-对其进行减1运算时,数值会产生下溢并被截断为11111111, 也就是十进制数值的 255。

优先级和结合性
运算符的优先级使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。
2 + 3 % 4 * 5 // 结果是 17
复合赋值运算符
复合赋值运算符将赋值运算符=与其它运算符进行结合。例如,将加法与赋值结合成加法赋值运算符+=。在实现的时候,需要把运算符的左参数设置成inout类型,因为这个参数的值会在运算符函数内直接被修改。
var a = 3
a += 2 // a == 5
a -= 1 // a == 4
a *=2 // a == 8
a /= 4 // a == 2
等价运算符
自定义的类和结构体没有对等价运算符进行默认实现,等价运算符通常被称为“相等”运算符(==)与“不等”运算符(!=)。对于自定义类型,Swift 无法判断其是否“相等”,因为“相等”的含义取决于这些自定义类型在你的代码中所扮演的角色。
let a = 3
let b = 4
if (a == b) {
// false
}
if (a != b) {
// ture
}


