Swift進階:可選類型Optional & Equatable+Comparable協(xié)議

swift 進階之路:學(xué)習(xí)大綱

本文轉(zhuǎn)載:Style_月月 http://www.reibang.com/p/7e57947119c8

Optional分析

swift中的可選類型(Optional),用于處理值缺失的情況八毯,有以下兩種情況

  • 有值,且等于x

  • 沒有值

這點可以通過swift-source->Optional.swift源碼(CMD+P主之,搜索Optional)源碼來印證

@frozen
public enum Optional<Wrapped>: ExpressibleByNilLiteral {
    ......
  //為nil
  case none

    ......
  //有值
  case some(Wrapped)

  ......
}

  • 通過源碼可知洪己,Optional的本質(zhì)是enum,當(dāng)前枚舉接收一個泛型參數(shù)Wrapped,當(dāng)前Some的關(guān)聯(lián)值就是當(dāng)前的Wrapper翘贮,下面兩種寫法完全等價
var age: Int? = 10
等價于
var age1: Optional<Int> = Optional(5)

  • 【Optional使用模式匹配】:既然Optional的本質(zhì)是枚舉,那么也可以使用模式匹配來匹配對應(yīng)的值爆惧,如下所示
//1狸页、聲明一個可選類型的變量
var age: Int? = 10
//2、通過模式匹配來匹配對應(yīng)的值
switch age{
    case nil:
        print("age 是個空值")
    case .some(let val):
        print("age的值是\(val)")
}

<!--或者這樣寫-->
switch age{
    case nil:
        print("age 是個空值")
    case .some(10):
        print("age的值是10")
    default:
        print("unKnow")
}

  • 【Optional解包】:因為是Optional類型扯再,當(dāng)我們需要從其中拿到我們想要的值時芍耘,需要對其進行解包,因為當(dāng)前的可選項是對值做了一層包裝的熄阻,有以下兩種方式:
    • 1斋竞、強制解包:好處是省事,壞處是一旦解包的值是nil秃殉,那么程序就會崩潰

      image
    • 2坝初、通過可選項綁定:判斷當(dāng)前的可選項是否有值

      • if let:如果有值,則會進入if流程

      • guard let:如果為nil钾军,則會進入else流程

//3脖卖、可選項解包
var age: Int? = nil

//3-1、強制解包
//如果age為nil巧颈,則程序崩潰
print(age!)

//3-2畦木、可選值綁定
<!--方式一-->
if let age = age{
    //如果age不為nil,則打印
    print(age)
}
<!--方式二-->
guard let tmp = age else {
    print("age為nil")
    return
}
print(tmp)

可選項綁定總結(jié)

  • 1砸泛、使用if let創(chuàng)建的內(nèi)容當(dāng)中age僅僅只能在當(dāng)前if分支的大括號內(nèi)訪問

  • 2十籍、使用guard let定義的tmp在當(dāng)前大括號外部也是能訪問的

Equatable協(xié)議

在上面的例子中蛆封,可以通過==判斷兩個可選項是否相等,原因是因為Optinal在底層遵循了Equatable協(xié)議

var age: Int? = 10
var age1: Optional<Int> = Optional(5)

age == age1

  • 繼續(xù)回到Optional源碼中勾栗,可以看到Optional遵循了Equatable協(xié)議
extension Optional: Equatable where Wrapped: Equatable {

    ......

    @inlinable
  public static func ==(lhs: Wrapped?, rhs: Wrapped?) -> Bool {
    switch (lhs, rhs) {
    case let (l?, r?):
      return l == r
    case (nil, nil):
      return true
    default:
      return false
    }
  }
}

swift標準庫中的類型

在swift中的類型惨篱,可以通過遵循Equatable協(xié)議來使用相等運算符(==)不等運算符(!=)比較兩個值相等還是不相等,Swift標準庫中絕大多數(shù)類型都默認實現(xiàn)了Equatable協(xié)議

例如下面的例子围俘,對于Int類型來說砸讳,系統(tǒng)默認實現(xiàn)了 ==

var age2: Int = 20
var isEqual = age1 == age2
print(isEqual)

<!--打印結(jié)果-->
false

自定義類型

對于自定義的類型,如果想實現(xiàn) ==界牡,應(yīng)該怎么辦呢簿寂?

  • 如果像下面這樣寫,是會直接報錯的

    image
  • 可以通過遵循Equatable協(xié)議實現(xiàn)宿亡,如下所示

//2常遂、自定義類型如何實現(xiàn)Equatable協(xié)議
struct CJLTeacher: Equatable{
    var age: Int
    var name: String
}
var t = CJLTeacher(age: 18, name: "CJL")
var t1 = CJLTeacher(age: 19, name: "CJL")
print(t == t1)

<!--打印結(jié)果-->
false
//如果將t1中的age改成18,打印結(jié)果是什么
true

為什么呢挽荠?其根本原因是因為遵守了Equatable協(xié)議克胳,系統(tǒng)默認幫我們實現(xiàn)了==方法

  • 查看SIL方法,是否如我們猜想的一樣圈匆?經(jīng)過驗證確實與我們猜測結(jié)論是一致的

    image
    • 查看__derived_struct_equals方法的實現(xiàn)

      image

疑問:如果是Class類型呢漠另?

如果像Struct那么寫,會報錯跃赚,提示需要自己實現(xiàn)Equatable協(xié)議的方法

image
  • class中手動實現(xiàn)Equatable協(xié)議的方法
//3酗钞、如果是class類型呢?需要手動實現(xiàn)Equatable協(xié)議的方法
class CJLTeacher: Equatable{

    var age: Int
    var name: String

    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }

    static func == (lhs: CJLTeacher, rhs: CJLTeacher) -> Bool {
        return lhs.age == rhs.age && lhs.name == rhs.name
    }

}
var t = CJLTeacher(age: 18, name: "CJL")
var t1 = CJLTeacher(age: 19, name: "CJL")
print(t == t1)

  • 如果class中的age和name都是Optional呢来累?
//4砚作、如果class中的屬性都是可選類型呢?底層是調(diào)用Optional的==來判斷
class CJLTeacher: Equatable{

    var age: Int?
    var name: String?

    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }

    static func == (lhs: CJLTeacher, rhs: CJLTeacher) -> Bool {
        return lhs.age == rhs.age && lhs.name == rhs.name
    }
}
var t = CJLTeacher(age: 18, name: "CJL")
var t1 = CJLTeacher(age: 19, name: "CJL")
print(t == t1)

查看其SIL文件可以驗證這一點:底層是通過調(diào)用Optional的==來判斷

image

區(qū)分 == vs ===

  • == 相當(dāng)于 equal to嘹锁,用于判斷兩個值是否相等

  • === 是用來判斷 兩個對象是否是同一個實例對象(即內(nèi)存地址指向是否一致)

class CJLTeacher: Equatable{

    var age: Int?
    var name: String?

    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }

    static func == (lhs: CJLTeacher, rhs: CJLTeacher) -> Bool {
        return lhs.age == rhs.age && lhs.name == rhs.name
    }
}
//===:判斷兩個對象是否是同一個
var t = CJLTeacher(age: 18, name: "CJL")
var t1 = t
t1.age = 20
print(t == t1)

<!--打印結(jié)果-->
true

除了==葫录,還有!=以及其他的運算符

Comparable協(xié)議

除了Equatable,還有Comparable協(xié)議,其中的運算符有:< 领猾、<=米同、>=、> 摔竿、...面粮、..<、

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
}
extension Comparable {
    public static func ... (minimum: Self, maximum: Self) -> ClosedRange<Self>
    ......
}

Struct重寫 < 運算符

  • 以struct為例继低,遵循Comparable協(xié)議,重寫 < 運算符
//1熬苍、struct遵守Comparable協(xié)議
struct CJLTeacher: Comparable{

    var age: Int
    var name: String

    //重載 < 符號
    static func < (lhs: CJLTeacher, rhs: CJLTeacher) -> Bool {
        return lhs.age < rhs.age
    }
}
var t = CJLTeacher(age: 18, name: "CJL")
var t1 = CJLTeacher(age: 19, name: "CJL")
print(t < t1)

<!--打印結(jié)果-->
true

?? 空運算符

如果當(dāng)前的變量為nil,可以在??返回一個nil時的默認值

  • 下面例子的打印結(jié)果是什么?
//?? 空運算符
var age: Int? = nil
//?? 等價于 if le / guard let
print(age ?? 20)

<!--打印結(jié)果-->
20

  • 進入Optional源碼,查看??實現(xiàn)
<!--返回T-->
@_transparent//空運算符
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T)
    rethrows -> T {
  switch optional {
  case .some(let value):
    return value
  case .none:
    return try defaultValue()
  }
}

<!--返回T?-->
@_transparent
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?)
    rethrows -> T? {
  switch optional {
  case .some(let value):
    return value
  case .none:
    return try defaultValue()
  }
}

從源碼中分析柴底,??只有兩種類型婿脸,一種是T,一種是柄驻,主要是與 ?? 后面的返回值有關(guān)(即簡單來說狐树,就是??后是什么類型,??返回的就是什么類型),如下所示

  • ??后面是age1鸿脓,而age1的類型是Int?抑钟,所以t的類型是 Int?

    image
  • 如果??是30呢? -- 類型是Int

    image
  • 如果??是String呢? -- 會報錯,??要求類型一致(跟是否是可選類型無關(guān))

    image

可選鏈

可選鏈 則意味著 允許在一個鏈上來訪問當(dāng)前的屬性/方法,如下所示

//***************6野哭、可選鏈***************
class CJLTeacher{
    var name: String?
    var subject: CJLSubject?

}

class CJLSubject {
    var subjectName: String?
    func test(){print("test")}
}

var s = CJLSubject()
var t = CJLTeacher()

//可選鏈訪問屬性
if let tmp = t.subject?.subjectName{
    print("tmp不為nil")
}else{
    print("tmp為nil")
}
//可選鏈訪問方法
t.subject?.test()

運行結(jié)果如下在塔,因為s為nil,所以屬性和方法都不會往下執(zhí)行

image

unsafelyUnwrapped(Optional.swift中的)

這個和強制解包的內(nèi)容是一致的虐拓,如下所示

//***************7心俗、unsafelyUnwrapped 和強制解包內(nèi)容是一致的
var age: Int? = 30
print(age!)
print(age.unsafelyUnwrapped)

<!--打印結(jié)果-->
30
30

//***************如果age是nil
var age: Int? = 30
print(age!)
print(age.unsafelyUnwrapped)

age是nil的結(jié)果和強制解包一致傲武,程序會崩潰

image
  • 官方對其的描述如下

    image

    這里的-O蓉驹,是指target -> Build Setting -> Optimization Level設(shè)置成-O時,如果使用的是age.unsafelyUnwrapped揪利,則不檢查這個變量是否為nil态兴,

  • 1、設(shè)置Optimization LevelFastest, Smallest[-Os]

  • 2疟位、edit Scheme -> Run -> Info -> Build Configuration改為release模式瞻润,然后再次運行發(fā)現(xiàn),沒有崩潰甜刻,與官方所說是一致的

    image

區(qū)分as绍撞、 as? 和 as!

  • as 將類型轉(zhuǎn)換為其他類型
var age: Int = 10

var age1 = age as Any
print(age1)

var age2 = age as AnyObject
print(age2)

<!--打印結(jié)果-->
10
10

  • as? 將類型轉(zhuǎn)換為 其他可選類型
var age: Int = 10
//as?
//as? 不確定類型是Double,試著轉(zhuǎn)換下得院,如果轉(zhuǎn)換失敗傻铣,則返回nil
var age3 = age as? Double
print(age3)

<!--打印結(jié)果-->
nil

此時的age3的類型是Double?

image
  • as! :強制轉(zhuǎn)換為其他類型
var age: Int = 10
//as! 強制轉(zhuǎn)換為其他類型
var age4 = age as! Double
print(age4)

運行結(jié)果如下,會崩潰

image

SIL分析

查看以下代碼的SIL文件

var age: Int = 10
var age3 = age as? Double
var age4 = age as! Double

image
  • 常規(guī)使用:如果可以明確類型祥绞,則可以直接使用as!
//常規(guī)使用
var age: Any = 10
func test(_ age: Any) -> Int{
    return (age as! Int) + 1
}
print(test(age))

<!--打印結(jié)果-->
11

使用建議

  • 如果能確定的類型非洲,使用 as! 即可

  • 如果是不能確定的,使用 as? 即可

總結(jié)

  • Optional的本質(zhì)是enum蜕径,所以可以使用模式匹配來匹配Optional的值

  • Optional的解包方式有兩種:

    • 1两踏、強制解包:一旦為nil,程序會崩潰

    • 2兜喻、可選值綁定if let (只能在if流程的作用域內(nèi)訪問)梦染、guard let

  • Equatable協(xié)議:

    • 對于swift標準庫中的絕大部分類型都默認實現(xiàn)了Equatable協(xié)議

    • 對于自定義Struct類型,僅需要遵守Equatable協(xié)議

    • 對于自定義class類型朴皆,除了需要遵守Equatable協(xié)議弓坞,還需要自己實現(xiàn)Equatable協(xié)議的方法

  • 區(qū)分 == vs ===

    • == 相當(dāng)于 equal to隧甚,用于判斷兩個值是否相等

    • === 是用來判斷 兩個對象是否是同一個實例對象(即內(nèi)存地址指向是否一致)

  • Comparable協(xié)議:

    • 對于自定義類型,需要遵循Comparable協(xié)議渡冻,并重寫運算符

    • ??空運算符:??只有兩種類型戚扳,一種是T,一種是T?族吻,主要是與 ?? 后面的返回值有關(guān)(即簡單來說帽借,就是??后是什么類型,??返回的就是什么類型

  • 可選鏈:允許在一個鏈上來訪問當(dāng)前的屬性/方法超歌,如果為nil砍艾,則不會執(zhí)行?后的屬性/方法

  • unsafelyUnwrapped:與強制解包類似,但是如果項目中設(shè)置target -> Build Setting -> Optimization Level設(shè)置成-O時巍举,如果使用的是age.unsafelyUnwrapped脆荷,則不檢查這個變量是否為nil

  • 區(qū)分 as、as?懊悯、 as!

    • as 將類型轉(zhuǎn)換為其他類型

    • as? 將類型轉(zhuǎn)換為 其他可選類型

    • as! 強制轉(zhuǎn)換為其他類型

    • 使用建議:能確定使用as!蜓谋,不能確定使用as?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市炭分,隨后出現(xiàn)的幾起案子桃焕,更是在濱河造成了極大的恐慌,老刑警劉巖捧毛,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件观堂,死亡現(xiàn)場離奇詭異,居然都是意外死亡呀忧,警方通過查閱死者的電腦和手機师痕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來而账,“玉大人胰坟,你說我怎么就攤上這事「Q铮” “怎么了腕铸?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铛碑。 經(jīng)常有香客問我狠裹,道長,這世上最難降的妖魔是什么汽烦? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任涛菠,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘俗冻。我一直安慰自己礁叔,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布迄薄。 她就那樣靜靜地躺著琅关,像睡著了一般。 火紅的嫁衣襯著肌膚如雪讥蔽。 梳的紋絲不亂的頭發(fā)上涣易,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天,我揣著相機與錄音冶伞,去河邊找鬼新症。 笑死,一個胖子當(dāng)著我的面吹牛响禽,可吹牛的內(nèi)容都是我干的徒爹。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼芋类,長吁一口氣:“原來是場噩夢啊……” “哼隆嗅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起梗肝,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤榛瓮,失蹤者是張志新(化名)和其女友劉穎铺董,沒想到半個月后巫击,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡精续,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年坝锰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片重付。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡顷级,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出确垫,到底是詐尸還是另有隱情弓颈,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布删掀,位于F島的核電站翔冀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏披泪。R本人自食惡果不足惜纤子,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧控硼,春花似錦泽论、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至幔妨,卻和暖如春抄瓦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背陶冷。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工钙姊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人埂伦。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓煞额,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沾谜。 傳聞我的和親對象是個殘疾皇子膊毁,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容