Swift进阶(十四)高级运算符

溢出运算符(Overflow Operator)

  • Swift的算术运算符出现溢出时会抛出运行时错误
  • Swift有溢出运算符(&+ &- &*),用来支持溢出运算
    我们先来看一下UInt8Int8的最大值和最小值
print(Int8.min) // -128
print(Int8.max) // 127
print(UInt8.min) // 0
print(UInt8.max) // 255

image.png

其实溢出运算符就像是一个循环(苹果官方给出的图):
image.png

我们先看一下如果不使用溢出运算符,溢出之后会怎样:

image.png

可以看到,如果不使用溢出运算符,数据溢出之后会直接崩溃。
下面我们来使用一下溢出运算符

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
    image.png
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协议,那就可能没有==运算符:

image.png

func equal(_ 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是什么意思:
    assignmenttrue时表示:在'可选链操作中',拥有和'赋值运算符'一样的优先级。
    什么意思呢?
    首先在Swift进阶(十)可选链中我们讲过,如果可选项为nil时,等号后面的函数(也可以是别的)就不会执行。
    那么在上面的例子中,person?.point +- Point(x: 11, y: 22)这句代码表示:如果personnilPoint(x: 11, y: 22)就不会执行,不会去初始化一个Point对象

  • 注意:
    优先级组的名字是自己定义的;
    优先级组的参数要严格按照苹果的文档来设置。

苹果参考文档:
https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations

你可能感兴趣的:(Swift进阶(十四)高级运算符)