运算符 是用于 检查 , 更改 或者 组合 值的 特殊符号 或 短语.
Swift 支持大多数 C语言 的运算符, 并改进了一些功能来消除常见的编码错误. 比如: 赋值运算符 (=) 不返回值, 避免使用 == 时错误的使用它. 算术运算符 +, -, *, /, % 等 检测禁止值的溢出.
Swift 还提供了 C语言 中没有的 范围运算符, 比如 a..< b 和 a... 表示值的范围.
本章主要描述 Swift 中 常用运算符, 高级运算符, 并描述如何定义您自己的 自定义运算符.
术语
运算符有 一元运算符, 二元运算符, 三元运算符.
| 名称 | 操作目标数 | 示例 | 说明 | 
|---|---|---|---|
| 一元运算符 | 1 | -a, !a | 
      出现在目标的前面或者后面 | 
| 二元运算符 | 2 | a + b | 
      出现在两个操作目标之间 | 
| 三元运算符 | 3 | a ? b ; c | 
      也叫三目运算符 | 
赋值运算符
赋值运算符 用于 初始化 或者 更新 目标的值:
1
2
3
4
let b = 10
var a = 5
a = b
// a 的值为 10
如果 赋值运算符 的右侧是一个包含多个值的 元组, 那个他的元素可以分解为多个 常量 或者 变量.
1
2
let (x, y) = (1, 2)
// x = 1,  y = 2
与 C语言 和 OC 中的 赋值运算符 不同的是, 在 Swift 中 赋值运算符 本身并不返回值. 以下代码是会报错的:
1
2
3
if x = y {
    // 此段代码是会报错的, 因为 x = y 不会返回任何的值.
}
算数运算符
Swift 为所有 数字类型 提供了四种标准 算术运算符.
| 名称 | 符号 | 
|---|---|
| 加法运算符 | + | 
    
| 减法运算符 | - | 
    
| 乘法运算符 | * | 
    
| 除法运算符 | / | 
    
与 C语言 和 OC 中的 算术运算符 不同的是, Swift 中 算术运算符 默认情况下不允许 值溢出. 你可以通过 溢出运算符 来评估是否发生溢出.
加法运算符 也支持 String 类型, 对其进行连接:
1
2
let string = "hello, " + "world"  
// string = "hello, world"
求余运算符
求余运算符 简单来说就是计算 a 除以 b 剩余的 余数, 这样一个运算符.
提示 求余运算符 在其他语言中也叫做 模运算符. 但是在 Swift 中对 负数 的运算严格来说是 求余 而不是 求模.
求余运算符 用法为:
1
2
3
4
5
9 % 4
// 结果为 1
-9 % 4
// 结果为 -1
复合赋值运算符
像 C语言 中一样, Swift 中也提供了多个操作符组合在一起的 复合赋值运算符. 比如: +=, -= 等等.
1
2
3
var a = 1
a += 2
// a 的值为 3
案例中, a += 2 是 a = a + 2 的简写.
注意 复合赋值运算符 不返回值, 因此
let b = a += 2的写法是错误的.
关于 Swift 提供的标准运算法, 可以参考 Operator Declarations
比较运算符
Swift 支持所有标准 C语言 比较运算符:
- 等于: 
==. - 不等于: 
!=. - 大于: 
>. - 小于: 
<. - 大于等于: 
>=. - 小于等于: 
<=. 
提示 Swift 中还提供了
===和!==两种运算符, 用于测试两个对象引用是否都引用自同一个对象实例.
比较运算符 返回一个 Bool 类型的值表示表达式结果是否为 true:
1
2
3
4
5
6
1 == 1   // true
2 != 1   // true
2 > 1    // true
1 < 2    // true
1 >= 1   // true
2 <= 1   // false
if 语句中的使用
比较运算符 也经常用于条件语句, 比如 if 语句:
1
2
3
4
5
6
7
let name = "world"
if name == "world" {
    print("hello, world")
} else {
    print("I'm sorry \(name), but I don't recognize you")
}
// 打印结果: "hello, world"
元组 的比较
如果两个 元组 有 相同的类型 和 相同数量的值, 则可以对其进行比较.
比较过程从左到右, 对每个元素进行比较, 直到发现两个不相等的值. 只要有一个元素值不相等, 元组 就不相等. 相反的, 只有所有的值都相等, 元组 才是相等的.
1
2
3
(1, "zebra") < (2, "apple")   // true 因为 1 < 2
(3, "apple") < (3, "bird")    // true 因为 3 == 3, "apple" < "bird"
(4, "dog") == (4, "dog")      // true 因为 4 == 4, and "dog" == "dog"
只有 运算符 可以作用于 元组 中所有的元素时候, 才可以用 运算符 进行比较. 比如:
1
2
("blue", -1) < ("purple", 1)        // OK
("blue", false) < ("purple", true)  // Error 因为 < 不能用于 Bool 类型的比较
注意 Swift 标准库中包含了少于
7个元素的 元组 的比较操作符. 如果你要对>= 7个元素的 元组 进行比较的时候, 就必须自己实现 比较操作符 了.
三目条件运算符
三目条件运算符 是由三部分组成的特殊运算符. 其结构为: question ? answer1 : answer2.
当表达式 question 的结果为 true 时, 运算表达式 answer1 并返回其运算结果. 
当表达式 question 的结果为 false 时, 运算表达式 answer2 并返回其运算结果.
三目条件运算符 也可以看错是以下事例代码的简写:
1
2
3
4
5
if question {
    answer1
} else {
    answer2
}
以下是个计算 UITableView 的 rowHeight 的案例. 
当有 header 时候 rowHeight 比 contentHeight 大 50. 
没有 header 时候 rowHeight 比 contentHeight 大 20.
1
2
3
4
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight == 90
以上案例也可以用以下的写法:
1
2
3
4
5
6
7
8
9
let contentHeight = 40
let hasHeader = true
let rowHeight: Int
if hasHeader {
    rowHeight = contentHeight + 50
} else {
    rowHeight = contentHeight + 20
}
// rowHeight == 90
三元条件运算符 可以使代码更加简洁. 但是过度使用, 也可能使代码难以阅读.
空合运算符
空合运算符 (a ?? b) 的用途为 解包 一个 **Optional ** 的 a, 如果 a != nil, 则返回 a 中存储的值, 如果 a == nil, 则返回 b. 
其中 a 必须为一个 **Optional ** 类型, 并且 b 的类型必须与 a 中存储的值的类型保持一致.
空合运算符 也可以看做是以下事例的简写:
1
a != nil ? a! : b
提示 如果
a != nil, 则b就不会被计算 (b可能是一个值, 也可能是一个表达式). 这就是所谓的 短路评估.
以下案例使用 空合运算符 对用户自定义颜色和默认颜色之间进行选择:
1
2
3
4
5
let defaultColorName = "red"
var userDefinedColorName: String?   // 默认为 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName == nil, 所以 colorNameToUse 取 defaultColorName 的值 "red"
如果案例中 userDefinedColorName = "yellow", 则此处  colorNameToUse = "yellow".
区间运算符
区间运算符(Range Operators) 也有的地方翻译为 范围运算符.
区间运算符 是一种表达值的范围的一种快捷方式, 共有三种.
| 分类 | 写法 | 意义 | 示例 | 
|---|---|---|---|
| 封闭区间 | a...b | 
      >= a && <= b | 
      1...4 包括 1, 2, 3, 4 | 
    
| 半开区间 | a..<b | 
      >= a && < b | 
      1..< 4 包括 1, 2, 3 | 
    
| 单侧区间 | a... | 
      >= a | 
      1... 包括 1, 2, 3, 4 … 并非上限无穷, 要视场景而定 | 
    
封闭区间运算符
封闭区间运算符 a...b 表示一个 a 到 b 的范围, 且包含 a 和 b. 其中 a <= b 是必要条件.
闭合区间运算符 在遍历所有值的范围时非常有用,例如for-in循环:
1
2
3
4
5
6
7
8
for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25
半开区间运算符
半开区间运算符 a..< b 同样表示 a 到 b 的范围, 但是其包含 a 但不包含 b. a <= b 依旧是必要条件. r如果 a == b 则范围为 空区间.
半开区间运算符 在遍历数组时候非常有用但不限于数组:
1
2
3
4
5
6
7
8
9
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
    print("Person \(i + 1) is called \(names[i])")
}
// Person 1 is called Anna
// Person 2 is called Alex
// Person 3 is called Brian
// Person 4 is called Jack
单侧区间运算符
单侧区间运算符 a... 表示从 a 开始在一个方向上尽可能继续的范围. 比如: 一个范围包含从索引 a 到数组末尾的所有元素. 在这种情况下可以忽略操作符一侧的值.
1
2
3
4
5
6
7
8
9
10
11
12
for name in names[2...] {
    print(name)
}
// Brian
// Jack
for name in names[...2] {
    print(name)
}
// Anna
// Alex
// Brian
单侧区间运算符 还有另一种写法: ..<a. 如下事例:
1
2
3
4
5
for name in names[..<2] {
    print(name)
}
// Anna
// Alex
单侧区间运算符 可以用于其他上下文中, 而不仅仅是下标. 要根据上下文才能理解其具有的具体意义. 由于其范围是无限的, 因此在循环过程中需要显式的为其提供停止循环的条件.
单侧区间运算符 还可以检查范围内是否包含了特定的值.
1
2
3
4
let range = ...5
range.contains(7)   // false
range.contains(4)   // true
range.contains(-1)  // true
逻辑运算符
逻辑运算符 用于 修改 或者 组合 布尔逻辑值 true / false.
Swift 支持基于 C语言 的三种逻辑运算符.
| 名称 | 符号 | 使用方法 | 
|---|---|---|
| 与 | && | 
      a && b | 
    
| 或 | || | 
      a || b | 
    
| 非 | ! | 
      
“非” 运算符
非 运算符 !a 对一个布尔值进行反转. 比如: true 变为 false, false 变为 true.
非 运算符是一个 前缀运算符. 它出现在操作值的前面. 用法如下:
1
2
3
4
5
let allowedEntry = false
if !allowedEntry {
    print("拒绝访问")
}
// 打印结果: "拒绝访问"
提示 在本例中,仔细选择布尔常量和变量名有助于保持代码的可读性和简洁性, 同时避免双重否定或混淆逻辑语句.
“与” 运算符
在 a && b 表达式中只有 a 和 b 两个表达式的结果同时为 true 时, 整个表达式的结果才为 true. 
在 a 和 b 中任意一个表达式的结果为 false, 整个表达式的结果就为 false.
如下示例中, 任意一个 Bool 值为 false 都不允许访问:
1
2
3
4
5
6
7
8
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
    print("Welcome!")
} else {
    print("禁止访问")
}
// 打印结果: "禁止访问"
“或” 运算符
或 运算符 为中缀运算符. 可以用于创建逻辑表达式 a || b, 其中只要 a 或者 b 任意一个表达式的结果为 true 整个逻辑表达式的结果就为 true. 只有 a 和 b 同时为 fase 时, 整个逻辑表达式的结果才为 false.
或 运算符 与 与 运算符相同, 都使用 短路评估 来求值.
与 运算符表达式中, 如果 a == false 则 a && b == false, b 的结果已经无法影响表达式的结果.
或 运算符表达式中, 如果 a == true 则 a || b == true, b 的结果也已经无法影响表达式的结果.
如下示例中, hasDoorKey 为 false, knowsOverridePassword 为 true, 因此整个表达式的结果为 true:
1
2
3
4
5
6
7
8
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("禁止访问")
}
// 打印结果: "Welcome!"
逻辑运算符混用
你可以用多个逻辑运算符来创建更长的复合表达式:
1
2
3
4
5
6
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("禁止访问")
}
// 打印结果: "Welcome!"
根据 enteredDoorCode、passedRetinaScan和 hasDoorKey 的值,前两个子表达式为 false. 但是, knowsOverridePassword 是 true,所以整个复合表达式的值仍然为 true.
提示 Swift 逻辑运算符
&&和||是左关联的,这意味着具有多个逻辑运算符的复合表达式首先计算最左边的子表达式.
括号的使用
在符合逻辑表达式中, 使用括号可以使复杂的逻辑表达式的意图变得易于理解. 比如在上面的符合逻辑表达式中加入括号:
1
2
3
4
5
6
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("禁止访问")
}
// 打印结果: "Welcome!"
在括号中的 enteredDoorCode && passedRetinaScan 被认为是整个表达式中逻辑独立的. 复合表达式的整体结果未发生变化, 但是其总体意图却更加清晰了.