如果对C++
有了解的话,理解运算符重载(Operator Overload)就很简单。OC
不支持运算符重载,但Swift
支持。
Swift的算数运算符出现溢出时会抛出运行时错误。
示例代码一:
print(Int8.min) // 输出:-128
print(Int8.max) // 输出:127
print(UInt8.min) // 输出:0
print(UInt8.max) // 输出:255
var a = UInt8.max
a += 1
print(a)
Int8
的可表示数范围是-128~127
,UInt8
可表示数范围是0~255
。当超出可表示数范围时运行时就会报错。
Swift有溢出运算符用来支持溢出运算。
常见的溢出运算符:&+
、&-
、&*
示例代码二:
var a = UInt8.max
a = a &+ 1
print(a) // 输出:0
var b = UInt8.min
b = b &- 2
print(b) // 输出:254
当数据溢出时,溢出运算符会自动循环可数范围。
官方图例:
类、结构体、枚举可以为现有的运算符提供自定义的实现,这个操作叫做运算符重载。
正常加法运算:
let v1 = 10
let v2 = 20
let v3 = v1 + v2
如果换成非基本数值计算:
struct Point {
var x = 0, y = 0
}
let p1 = Point(x: 10, y: 20)
let p2 = Point(x: 20, y: 30)
let p3 = p1 + p2
编译器不支持这样的写法,这时候就需要用到运算符重载。
struct Point {
var x = 0, y = 0
// +号运算符重载
static func +(p1: Point, p2: Point) -> Point {
Point(x: p1.x + p2.x, y: p1.y + p2.y)
}
}
let p1 = Point(x: 10, y: 20)
let p2 = Point(x: 20, y: 30)
let p3 = p1 + p2
print(p3) // 输出:Point(x: 30, y: 50)
+
号运算符重载代码写到结构体外部也可以,只是平时建议写在内部(高内聚)。
static prefix func -(p: Point) -> Point {
Point(x: -p.x, y: -p.y)
}
let p1 = Point(x: 10, y: 20)
let p2 = -p1
print(p2) // 输出:Point(x: -10, y: -20)
static func +=(p1: inout Point, p2: Point) {
p1 = Point(x: p1.x + p2.x, y: p1.y + p2.y)
}
var p1 = Point(x: 10, y: 20)
let p2 = Point(x: 20, y: 30)
p1 += p2
print(p1) // 输出:Point(x: 30, y: 50)
+=
运算符重载时,重载函数左边变量一定要用inout
修饰,因为要修改外部变量的内存。并且外部变量使用var
声明。由于只需要修改第一个参数的内存,所以函数不需要返回值。
static prefix func ++(p: inout Point) -> Point {
p += Point(x: 1, y: 1)
return p
}
var p1 = Point(x: 10, y: 20)
let p2 = ++p1
print(p2) // 输出:Point(x: 11, y: 21)
++
写在变量前面:先加后用
static postfix func ++(p: inout Point) -> Point {
let tmp = p
p += Point(x: 1, y: 1)
return tmp
}
var p1 = Point(x: 10, y: 20)
let p2 = p1++
print(p2) // 输出:Point(x: 10, y: 20)
++
写在变量后面:先用后加
static func ==(p1: Point, p2: Point) -> Bool {
(p1.x == p2.x) && (p1.y == p2.y)
}
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个实例是否等价,一般做法是遵守Equatable
协议,重载==
运算符。
Equatable
协议public protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
示例代码:
struct Point: Equatable {
var x = 0, y = 0
static func == (lhs: Self, rhs: Self) -> Bool {
// 普通写法
if lhs.x == rhs.x && lhs.y == rhs.y {
return true
}
return false
// 简写
// lhs.x == rhs.x && lhs.y == rhs.y
}
}
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
无论是否遵守Equatable
协议,都可以重载==
运算符,为什么还要遵守协议呢?因为遵守协议就可以直接告诉其他人该类/结构体/枚举是支持==
运算符比较的。还有一个很重要的区别是:遵守Equatable
协议,默认重载!=
运算符,但是自定义==
运算符不会重载!=
运算符。
Swift为以下类型提供默认的Equatable
实现:
enum Answer {
case wrong
case right
}
var s1 = Answer.wrong
var s2 = Answer.right
print(s1 == s2) // 输出:false
Equatable
协议关联类型的枚举enum Answer: Equatable {
case wrong(Int)
case right
}
var s1 = Answer.wrong(10)
var s2 = Answer.wrong(10)
print(s1 == s2) // 输出:true
Equatable
协议存储属性的结构体struct 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
===
、!==
引用类型比较存储的地址值是否相等(是否引用着同一个对象),使用恒等运算符===
,!==
(仅限引用类型)。
class Person {
}
var p1 = Person()
var p2 = Person()
print(p1 === p2) // 输出:false
print(p1 !== p2) // 输出:true
Comparable
协议要想比较2个实例的大小,一般做法是:
Comparable
协议官方定义的Comparable
协议:
public protocol Comparable : Equatable {
static func < (lhs: Self, rhs: Self) -> Bool
static func <= (lhs: Self, rhs: Self) -> Bool
static func >= (lhs: Self, rhs: Self) -> Bool
static func > (lhs: Self, rhs: Self) -> Bool
}
示例代码:
// score大的比较大,若score相等,age小的比较大
struct Student : Comparable {
var age: Int
var score: Int
init(age: Int, score: Int) {
self.age = age
self.score = score
}
static func < (lhs: Student, rhs: Student) -> Bool {
(lhs.score < rhs.score) || (lhs.score == rhs.score && lhs.age > rhs.age)
}
static func > (lhs: Student, rhs: Student) -> Bool {
(lhs.score > rhs.score) || (lhs.score == rhs.score && lhs.age < rhs.age)
}
static func <= (lhs: Student, rhs: Student) -> Bool {
!(lhs > rhs)
}
static func >= (lhs: Student, rhs: Student) -> Bool {
!(lhs < rhs)
}
}
var stu1 = Student(age: 20, score: 100)
var stu2 = Student(age: 18, score: 98)
var stu3 = Student(age: 20, score: 100)
print(stu1 > stu2) // 输出:true
print(stu1 >= stu2) // 输出:true
print(stu1 >= stu3) // 输出:true
print(stu2 < stu1) // 输出:true
print(stu2 <= stu1) // 输出:true
print(stu1 <= stu3) // 输出:false
上面的都是为已经存在的运算符进行重载,而自定义运算符是定义一个原本不存在的运算符。
自定义新的运算符在全局作用域使用operator
进行声明。
格式:
prefix operator 前缀运算符
postfix operator 后缀运算符
infix operator 中缀运算符 : 优先级组
precedencegroup 优先级组 {
associativity: 结合性(left/right/none)
higherThan: 比谁的优先级高
lowerThan: 比谁的优先级低
assignment: true代表在可选链操作中拥有跟赋值运算符一样的优先级
}
示例代码(定义前缀运算符+++
):
prefix operator +++
prefix func +++ (_ i: inout Int) {
i += 2
}
var age = 10
+++age
print(age) // 输出:12
示例代码(定义+-
运算符并设置运算符优先级):
infix operator +- : PlusMinusPrecedence
precedencegroup PlusMinusPrecedence {
associativity: none
higherThan: AdditionPrecedence
lowerThan: MultiplicationPrecedence
assignment: true
}
struct Point {
var x = 0, y = 0
static func +- (p1: Point, p2: Point) -> Point {
Point(x: p1.x + p2.x, y: p1.y - p2.y)
}
}
var p1 = Point(x: 10, y: 20)
var p2 = Point(x: 5, y: 15)
var p3 = p1 +- p2
print(p3) // 输出:Point(x: 15, y: 5)
如果设置associativity: none
,并且使用了两个及以上运算符就会报错:
解决报错:
associativity
取值left
或right
assignment
示例:
class Person {
var point: Point = Point()
}
var p: Person? = Person()
let result = p?.point +- Point(x: 10, y: 20)
print(result!) // 输出:Point(x: 10, y: -20)
如果变量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