如果对C++
有了解的话,理解运算符重载(Operator Overload)就很简单。OC
不支持运算符重载,但Swift
支持。
一、溢出运算符(Overflow Operator)
Swift的算数运算符出现溢出时会抛出运行时错误。
示例代码一:
1 | print(Int8.min) // 输出:-128 |
Int8
的可表示数范围是-128~127
,UInt8
可表示数范围是0~255
。当超出可表示数范围时运行时就会报错。
Swift有溢出运算符用来支持溢出运算。
常见的溢出运算符:&+
、&-
、&*
示例代码二:
1 | var a = UInt8.max |
当数据溢出时,溢出运算符会自动循环可数范围。
官方图例:
二、运算符重载
类、结构体、枚举可以为现有的运算符提供自定义的实现,这个操作叫做运算符重载。
正常加法运算:
1 | let v1 = 10 |
如果换成非基本数值计算:
1 | struct Point { |
编译器不支持这样的写法,这时候就需要用到运算符重载。
2.1. 运算符:+
1 | struct Point { |
+
号运算符重载代码写到结构体外部也可以,只是平时建议写在内部(高内聚)。
2.2. 运算符:减号前缀(运算符放到前面)
1 | static prefix func -(p: Point) -> Point { |
2.3. 运算符:+=
1 | static func +=(p1: inout Point, p2: Point) { |
+=
运算符重载时,重载函数左边变量一定要用inout
修饰,因为要修改外部变量的内存。并且外部变量使用var
声明。由于只需要修改第一个参数的内存,所以函数不需要返回值。
2.4. 运算符:前置++
1 | static prefix func ++(p: inout Point) -> Point { |
++
写在变量前面:先加后用
2.5. 运算符:后置++
1 | static postfix func ++(p: inout Point) -> Point { |
++
写在变量后面:先用后加
2.6. 运算符:==
1 | static func ==(p1: Point, p2: Point) -> Bool { |
要想得知2个实例是否等价,一般做法是遵守Equatable
协议,重载==
运算符。
2.7. Equatable
协议
1 | public protocol Equatable { |
示例代码:
1 | struct Point: Equatable { |
无论是否遵守Equatable
协议,都可以重载==
运算符,为什么还要遵守协议呢?因为遵守协议就可以直接告诉其他人该类/结构体/枚举是支持==
运算符比较的。还有一个很重要的区别是:遵守Equatable
协议,默认重载!=
运算符,但是自定义==
运算符不会重载!=
运算符。
Swift为以下类型提供默认的Equatable
实现:
没有关联类型的枚举
1
2
3
4
5
6
7enum Answer {
case wrong
case right
}
var s1 = Answer.wrong
var s2 = Answer.right
print(s1 == s2) // 输出:false只拥有遵守
Equatable
协议关联类型的枚举1
2
3
4
5
6
7enum Answer: Equatable {
case wrong(Int)
case right
}
var s1 = Answer.wrong(10)
var s2 = Answer.wrong(10)
print(s1 == s2) // 输出:true
如果不遵守Equatable
协议关联类型的枚举:
- 只拥有遵守
Equatable
协议存储属性的结构体1
2
3
4
5
6
7
8
9
10
11struct Point: Equatable {
var x = 0, y = 0
}
let p1 = Point(x: 10, y: 20)
let p2 = Point(x: 20, y: 30)
let isTrue1 = p1 == p2
print(isTrue1) // 输出:false
let p3 = Point(x: 20, y: 30)
let isTrue2 = p2 == p3
print(isTrue2) // 输出:true
2.8. 恒等运算符===
、!==
引用类型比较存储的地址值是否相等(是否引用着同一个对象),使用恒等运算符===
,!==
(仅限引用类型)。
1 | class Person { } |
2.9. Comparable
协议
要想比较2个实例的大小,一般做法是:
- 遵守
Comparable
协议 - 重载相应的运算符
官方定义的Comparable
协议:
1 | public protocol Comparable : Equatable { |
示例代码:
1 | // score大的比较大,若score相等,age小的比较大 |
三、自定义运算符(Custom Operator)
上面的都是为已经存在的运算符进行重载,而自定义运算符是定义一个原本不存在的运算符。
自定义新的运算符在全局作用域使用operator
进行声明。
格式:
1 | prefix operator 前缀运算符 |
示例代码(定义前缀运算符+++
):
1 | prefix operator +++ |
示例代码(定义+-
运算符并设置运算符优先级):
1 | infix operator +- : PlusMinusPrecedence |
如果设置associativity: none
,并且使用了两个及以上运算符就会报错:
解决报错:
associativity
取值left
或right
- 使用一个运算符
assignment
示例:
1 | class Person { |
如果变量p
为nil
,不会继续往右执行(+-
运算符不会执行);如果不为nil
,则正常顺序执行代码(+-
运算符正常执行)。
优先级组参数说明:
associativity
结合性有三个取值:left
:从左往右开始结合计算right
:从右往左开始结合计算none
:仅限一个运算符,多个运算符会报错(例:a1 + a2 + a3
,有2个运算符编译报错)
higherThan
:哪个运算符优先级比当前定义的运算符优先级高lowerThan
:哪个运算符优先级比当前定义的运算符优先级低assignment
:true
代表在可选链操作中拥有跟赋值运算符一样的优先级
运算符优先级组描述:
参考官方文档:
1. 运算符优先级组描述:https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations
2. 高级运算符:https://docs.swift.org/swift-book/LanguageGuide/AdvancedOperators.html