溢出运算符(Overflow Operator)
- Swift的算术运算符出现溢出时会抛出运行时错误
- Swift有溢出运算符(
&+
&-
&*
),用来支持溢出运算
我们先来看一下UInt8
和Int8
的最大值和最小值
print(Int8.min) // -128
print(Int8.max) // 127
print(UInt8.min) // 0
print(UInt8.max) // 255
其实
溢出运算符
就像是一个循环(苹果官方给出的图):我们先看一下如果不使用溢出运算符
,溢出之后会怎样:
可以看到,如果不使用
溢出运算符
,数据溢出之后会直接崩溃。下面我们来使用一下
溢出运算符
:
var min = UInt8.min
print(min &- 1)
/*输出结果*/
255
var max = UInt8.max
print(max &+ 1)
/*输出结果*/
0
print(max &* 2) // 等价于 max &+ max
/*输出结果*/
254
可以看到,最小值再减一就是最大值,最大值再加一就是最小值,这刚好就是一个循环。
运算符重载(OPerator Overload)
- 类、结构体、枚举可以为现有的运算符提供自定义的实现,这个操作叫做:运算符重载
下面以结构体
为例:
struct Point {
var x: Int, y: Int
}
我们知道直接将两个结构体
相加是不可以的,那么我们如何通过运算符重载来实现两个结构体
相加呢?方法如下:
- 方法一: 直接在外面重载运算符
func +(p1: Point, p2: Point) -> Point {
Point(x: p1.x + p2.x, y: p1.y + p2.y)
}
let p = Point(x: 10, y: 20) + Point(x: 11, y: 22)
print(p)
/*输出结果*/
Point(x: 21, y: 42)
- 方法二:在
结构体
内部重载运算符
在这里要注意,在结构体
内部重载运算符,必须加上static
struct Point {
var x: Int, y: Int
static func + (p1: Point, p2: Point) -> Point {
Point(x: p1.x + p2.x, y: p1.y + p2.y)
}
}
一般情况下,建议使用第二种方法来重载运算符
我们再来重载一下其他的运算符:
struct Point {
var x: Int, y: Int
static func + (p1: Point, p2: Point) -> Point {
Point(x: p1.x + p2.x, y: p1.y + p2.y)
}
static func - (p1: Point, p2: Point) -> Point {
Point(x: p1.x - p2.x, y: p1.y - p2.y)
}
static func += (p1: inout Point, p2: Point) {
p1 = p1 + p2
}
// 前缀运算符
static prefix func ++ (p: inout Point) -> Point {
p += Point(x: 1, y: 1)
return p
}
// 后缀运算符
static postfix func ++ (p: inout Point) -> Point {
let tmp = p
p += Point(x: 1, y: 1)
return tmp
}
static func == (p1: Point, p2: Point) -> Bool {
(p1.x == p2.x) && (p1.y == p2.y)
}
}
Equatable
- 要想知道两个实例是否等价,一般做法是遵守
Equatable
协议,重载==
运算符;与此同时,等价于重载了!=
运算符
我们来看一下Equatable
的定义:
public protocol Equatable {
/// Returns a Boolean value indicating whether two values are equal.
///
/// Equality is the inverse of inequality. For any values `a` and `b`,
/// `a == b` implies that `a != b` is `false`.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
static func == (lhs: Self, rhs: Self) -> Bool
}
下面我们自定义一个类
,来遵守以下Equatable
协议
class Person: Equatable {
var age: Int
init(_ age: Int) {
self.age = age
}
static func == (lhs: Person, rhs: Person) -> Bool {
lhs.age == rhs.age
}
}
这里大家可能会有一个疑问,我直接重加==
运算符就可以了,为什么还要遵守Equatable
协议呢?
其实,只是单纯的重载==
运算符,确实是没什么区别。可是遵守Equatable
协议是有一定的含义的:
① 可以明确的高速使用者,当前类
(或者 结构体
枚举
) 是可以使用==
运算符的。
② 我们再上一篇文章Swift进阶(十三)泛型中讲过,关联类型
类型必须遵守Equatable
协议,如果想被关联,那就要遵守。
③ 在定义泛型函数,来比较两个对象是否相等时,泛型要遵守Equatable
协议,否则会报错,因为泛型可以是任意类型,如果没有遵守Equatable
协议,那就可能没有==
运算符:
func equal<T: Equatable>(_ a: T, _ b: T) -> Bool {
a == b
}
equal(Person(10), Person(20))
Swift 为以下类型提供默认的
Equatable
实现
① 没有关联类型
的枚举
② 只拥有遵守Equatable
协议关联类型
的枚举
③ 只拥有遵守Equatable
协议存储属性
的结构体
引用类型比较存储的地址值是否相等(是否引用着同一个对象),使用恒等运算符
===
、!==
,注意此时不用去遵守Equatable
协议`
class Person {
var age: Int
init(_ age: Int) {
self.age = age
}
}
var p1 = Person(10)
var p2 = Person(10)
print(p1 === p2) // false
p2 = p1
print(p1 === p2) // true
Comparable
- 要想比较两个实例的大小,一般做法是:
□ 遵守Comparable
协议
□ 重载相应的运算符
先来看一下Comparable
协议:
public protocol Comparable : Equatable {
/// Returns a Boolean value indicating whether the value of the first
/// argument is less than that of the second argument.
///
/// This function is the only requirement of the `Comparable` protocol. The
/// remainder of the relational operator functions are implemented by the
/// standard library for any type that conforms to `Comparable`.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
static func < (lhs: Self, rhs: Self) -> Bool
/// Returns a Boolean value indicating whether the value of the first
/// argument is less than or equal to that of the second argument.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
static func <= (lhs: Self, rhs: Self) -> Bool
/// Returns a Boolean value indicating whether the value of the first
/// argument is greater than or equal to that of the second argument.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
static func >= (lhs: Self, rhs: Self) -> Bool
/// Returns a Boolean value indicating whether the value of the first
/// argument is greater than that of the second argument.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
static func > (lhs: Self, rhs: Self) -> Bool
}
会发现Comparable
协议有四个重载运算符,下面我们来实现以下:
// 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 s1 = Student(age: 10, score: 100)
var s2 = Student(age: 10, score: 120)
var s3 = Student(age: 9, score: 110)
print(s1 > s2) // false
print(s1 >= s2) // false
print(s1 >= s3) // false
print(s1 <= s3) // true
自定义运算符(Custom OPerator)
- 可以自定义新的运算符:在使用
operator
进行声明
□prefix operator
前缀运算符
□postfix operator
后缀运算符
□infix operator
中缀运算符 : 优先级组
precedencegroup 优先级组 {
associativity: 结合性('left'、'right'、'none'),表示运算符运算的方向,或者不允许多个一起使用
higherThan: 比谁的优先级高
lowerThan: 比谁的优先级底
assignment: true 代表在'可选链操作中',拥有和'赋值运算符'一样的优先级
}
precedencegroup MyOperator {
associativity: none
higherThan: AdditionPrecedence
lowerThan: MultiplicationPrecedence
assignment: true
}
prefix operator +++
infix operator +- : MyOperator
struct Point {
var x: Int
var y: Int
static prefix func +++ (point: inout Point) -> Point {
point = Point(x: point.x + point.x, y: point.y + point.y)
return point
}
static func +- (p1: Point, p2: Point) -> Point {
return Point(x: p1.x + p2.x, y: p1.y - p2.y)
}
static func +- (p1: Point?, p2: Point) ->Point {
return Point(x: p1?.x ?? 0 + p2.x, y: p1?.y ?? 0 - p2.y)
}
}
struct Person {
var point: Point
}
var person: Person? = Person(point: Point(x: 10, y: 20))
person?.point +- Point(x: 11, y: 22)
这里解释一下
优先级组
里面的assignment
是什么意思:
□assignment
为true
时表示:在'可选链操作中',拥有和'赋值运算符'一样的优先级。
什么意思呢?
首先在Swift进阶(十)可选链中我们讲过,如果可选项为nil
时,等号后面的函数(也可以是别的)
就不会执行。
那么在上面的例子中,person?.point +- Point(x: 11, y: 22)
这句代码表示:如果person
为nil
,Point(x: 11, y: 22)
就不会执行,不会去初始化一个Point对象
。注意:
□优先级组
的名字是自己定义的;
□优先级组
的参数要严格按照苹果的文档来设置。
苹果参考文档:
https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations