Swift進階(十四)高級運算符

溢出運算符(Overflow Operator)

  • Swift的算術(shù)運算符出現(xiàn)溢出時會拋出運行時錯誤
  • Swift有溢出運算符(&+ &- &*),用來支持溢出運算
    我們先來看一下UInt8Int8的最大值和最小值
print(Int8.min) // -128
print(Int8.max) // 127
print(UInt8.min) // 0
print(UInt8.max) // 255

image.png

其實溢出運算符就像是一個循環(huán)(蘋果官方給出的圖):
image.png

我們先看一下如果不使用溢出運算符,溢出之后會怎樣:

image.png

可以看到,如果不使用溢出運算符,數(shù)據(jù)溢出之后會直接崩潰。
下面我們來使用一下溢出運算符

var min = UInt8.min
print(min &- 1)
/*輸出結(jié)果*/
255

var max = UInt8.max
print(max &+ 1)
/*輸出結(jié)果*/
0

print(max &* 2) // 等價于 max &+ max
/*輸出結(jié)果*/
254

可以看到,最小值再減一就是最大值,最大值再加一就是最小值,這剛好就是一個循環(huán)。

運算符重載(OPerator Overload)

  • 類、結(jié)構(gòu)體、枚舉可以為現(xiàn)有的運算符提供自定義的實現(xiàn),這個操作叫做:運算符重載
    下面以結(jié)構(gòu)體為例:
struct Point {
    var x: Int, y: Int
}

我們知道直接將兩個結(jié)構(gòu)體相加是不可以的,那么我們?nèi)绾瓮ㄟ^運算符重載來實現(xiàn)兩個結(jié)構(gòu)體相加呢?方法如下:

  • 方法一: 直接在外面重載運算符
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)
/*輸出結(jié)果*/
Point(x: 21, y: 42)
  • 方法二:在結(jié)構(gòu)體內(nèi)部重載運算符
    在這里要注意,在結(jié)構(gòu)體內(nèi)部重載運算符,必須加上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協(xié)議,重載==運算符;與此同時,等價于重載了!=運算符
    我們來看一下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協(xié)議

class Person: Equatable {
    var age: Int
    init(_ age: Int) {
        self.age = age
    }
    
    static func == (lhs: Person, rhs: Person) -> Bool {
        lhs.age == rhs.age
    }
}

這里大家可能會有一個疑問,我直接重加==運算符就可以了,為什么還要遵守Equatable協(xié)議呢?
其實,只是單純的重載==運算符,確實是沒什么區(qū)別??墒亲袷?code>Equatable協(xié)議是有一定的含義的:
① 可以明確的高速使用者,當(dāng)前(或者 結(jié)構(gòu)體 枚舉) 是可以使用==運算符的。
② 我們再上一篇文章Swift進階(十三)泛型中講過,關(guān)聯(lián)類型類型必須遵守Equatable協(xié)議,如果想被關(guān)聯(lián),那就要遵守。
③ 在定義泛型函數(shù),來比較兩個對象是否相等時,泛型要遵守Equatable協(xié)議,否則會報錯,因為泛型可以是任意類型,如果沒有遵守Equatable協(xié)議,那就可能沒有==運算符:

image.png

func equal<T: Equatable>(_ a: T, _ b: T) -> Bool {
    a == b
}
equal(Person(10), Person(20))
  • Swift 為以下類型提供默認(rèn)的Equatable實現(xiàn)
    ① 沒有關(guān)聯(lián)類型枚舉
    ② 只擁有遵守Equatable協(xié)議關(guān)聯(lián)類型枚舉
    ③ 只擁有遵守Equatable協(xié)議存儲屬性結(jié)構(gòu)體

  • 引用類型比較存儲的地址值是否相等(是否引用著同一個對象),使用恒等運算符===、!==,注意此時不用去遵守Equatable協(xié)議`

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協(xié)議
    □ 重載相應(yīng)的運算符
    先來看一下Comparable協(xié)議:
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
}

會發(fā)現(xiàn)Comparable協(xié)議有四個重載運算符,下面我們來實現(xiàn)以下:

// 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 中綴運算符 : 優(yōu)先級組
precedencegroup 優(yōu)先級組 {
    associativity: 結(jié)合性('left'、'right'、'none'),表示運算符運算的方向,或者不允許多個一起使用
    higherThan: 比誰的優(yōu)先級高
    lowerThan: 比誰的優(yōu)先級底
    assignment: true 代表在'可選鏈操作中',擁有和'賦值運算符'一樣的優(yōu)先級
}
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)
  • 這里解釋一下優(yōu)先級組里面的assignment是什么意思:
    assignmenttrue時表示:在'可選鏈操作中',擁有和'賦值運算符'一樣的優(yōu)先級。
    什么意思呢?
    首先在Swift進階(十)可選鏈中我們講過,如果可選項為nil時,等號后面的函數(shù)(也可以是別的)就不會執(zhí)行。
    那么在上面的例子中,person?.point +- Point(x: 11, y: 22)這句代碼表示:如果personnil,Point(x: 11, y: 22)就不會執(zhí)行,不會去初始化一個Point對象。

  • 注意:
    優(yōu)先級組的名字是自己定義的;
    優(yōu)先級組的參數(shù)要嚴(yán)格按照蘋果的文檔來設(shè)置。

蘋果參考文檔:
https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、錯誤處理 1.1、錯誤類型語法錯誤(編譯報錯)邏輯錯誤運行時錯誤(可能會導(dǎo)致閃退,一般也叫做異常) 1.2、自...
    IIronMan閱讀 695評論 0 3
  • 溢出運算符(Overflow Operator) Swift的算數(shù)運算符出現(xiàn)溢出時會拋出運行時錯誤 Swift又溢...
    曹來東閱讀 306評論 0 1
  • 1. 溢出運算符 Swift 的算數(shù)運算符出現(xiàn)溢出時,會拋出運行時錯誤 Swift 有溢出運算符(&+、&-、&*...
    Aliv丶Zz閱讀 282評論 0 0
  • 1.介紹 Swift 提供了一些對值進行更加復(fù)雜操作的高級運算符,作為基本運算符的補充,這些運算包括你在 C 或 ...
    happy神悅閱讀 237評論 0 0
  • 一. 溢出運算符(Overflow Operator) Swift的算數(shù)運算符出現(xiàn)溢出時會拋出運行時錯誤 Swif...
    Imkata閱讀 522評論 0 1

友情鏈接更多精彩內(nèi)容