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<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)

  • 可以自定义新的运算符:在\color{orange}{全局作用域}使用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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容