Swift Optional & Equatable+Comparable協(xié)議 & 訪問控制權(quán)限

前言

本篇文章主要講解一下,之前用到過但是沒有仔細(xì)分析的幾個知識點(diǎn) ?? Optional可選類型淑履、Equatable協(xié)議和Comparable協(xié)議,然后會補(bǔ)充關(guān)于解包的相關(guān)場景僵娃,最后大致說明一下Swift中的訪問控制權(quán)限险掀。

一、Optional

首先來看看Optional可選類型田度,這個關(guān)鍵字在Swift中主要是用來處理值缺失的情況妒御,有以下2種情況??

  1. 有值,且等于x
  2. 沒有值

1.1 源碼分析

先來看看關(guān)于Optional的底層源碼??

上圖可見镇饺,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)

1.2 匹配模式

跟我們之前講解Swift 枚舉一樣惋啃,接著我們來看看Optinal的匹配模式哼鬓,示例??

//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")
}

1.3 Optional解包

解包的意思就是獲取值魄宏,因?yàn)?code>Optional可選項(xiàng)是對做了一層“包裝”,我們得拆開這個“包裝”才能拿到存筏。解包有2種方式??

  1. 強(qiáng)制解包 ?? 好處是省事宠互,壞處是解包的值是nil會導(dǎo)致程序crash崩潰
var age: Int? = nil
print(age!)

運(yùn)行崩潰??

  1. 通過可選項(xiàng)綁定:判斷當(dāng)前的可選項(xiàng)是否有值
    • if - let ?? 如果有值椭坚,則會進(jìn)入if流程
    • guard - let ?? 如果為nil予跌,則會進(jìn)入else流程
var age: Int? = nil
// 方式一
if let age = age{
    //如果age不為nil,則打印
    print(age)
}

// 方式二
func test() {
    guard let tmp = age else {
        print("age為nil")
        return
    }
    print(tmp)
}

if-let 可以配合else使用善茎,也可以直接在if中添加return券册,表示后續(xù)代碼都是else場景下,節(jié)省了else的{}垂涯,提高代碼可讀性??

var age: Int? = nil

// 可以配合return使用烁焙, 省了else的{}
if let value = age {
    print("value \(value)")
    return
}

if let value1 = age {
    print("value1 \(value1)")
} else {
    print("age 為 nil")
}

注意:

  1. 使用return關(guān)鍵字,必須將該代碼塊放入函數(shù)體
  2. guard-let是具備守護(hù)功能耕赘,它必須搭配return使用骄蝇,排除異常情況,守護(hù)guard(作用域)之后的代碼操骡。
if-letguard-let的使用建議

實(shí)際開發(fā)過程中九火,我們希望代碼從上至下清晰表達(dá)主線流程

  • guard-let一般用于處理非主線異常情況册招,直接return出去岔激,守護(hù)主線代碼。
  • if-let一般用于處理主線重要場景是掰。
  • if-let創(chuàng)建的內(nèi)容虑鼎,僅在if 作用域內(nèi)可訪問;guard-let創(chuàng)建的內(nèi)容键痛,是供guard 作用域外訪問炫彩。

二、Equatable協(xié)議

Swift中的類型散休,可以通過遵循Equatable協(xié)議來使用相等運(yùn)算符(==)不等運(yùn)算符(!=)媒楼。

絕大多數(shù)類型默認(rèn)實(shí)現(xiàn)Equatable協(xié)議`??

上圖可見乐尊,Int String Double Float均遵循了戚丸。

2.1 Optional中的Equatable協(xié)議

繼續(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
    }
  }
}

2.2 自定義類型實(shí)現(xiàn)Equatable協(xié)議

對于自定義的類型,如果想實(shí)現(xiàn) ==限府,應(yīng)該怎么辦呢夺颤?

2.2.1自定義struct類型

首先我們看看結(jié)構(gòu)體類型??

struct LGTeacher {
    var age: Int
    var name: String
}

var t = LGTeacher(age: 17, name: "Kody")
var t1 = LGTeacher(age: 18, name: "Cooci")

print(t == t1)

然后遵循Equatable協(xié)議實(shí)現(xiàn)??

上圖可知,結(jié)構(gòu)體直接實(shí)現(xiàn)Equatable協(xié)議胁勺,即可比較世澜,為什么呢?我們?nèi)タ碨IL代碼??

系統(tǒng)默認(rèn)實(shí)現(xiàn)了==方法署穗,然后調(diào)用的是__derived_struct_equals去做的比較??

2.2.2自定義Class類型

接下來我們看看類classEquatable協(xié)議實(shí)現(xiàn)寥裂,將LGPerson改為class??

報(bào)錯提示缺少初始化方法,那么添加??

接著報(bào)錯案疲,未實(shí)現(xiàn)Equatable協(xié)議封恰,那么實(shí)現(xiàn)??

屬性是Optional類型

我們查看SIL代碼??

比較調(diào)用的是Optinal的==方法??

2.2.3 結(jié)構(gòu)體嵌套類

上圖可知,結(jié)構(gòu)體嵌套類褐啡,即值類型中包含引用類型的實(shí)例诺舔,必須實(shí)現(xiàn)引用類型Equatable (==)函數(shù),查看SIL??

2.2.4 =====

在我們平常的開發(fā)中备畦,經(jīng)常會用到==低飒,但Swift中還有個===,它倆之間有什么區(qū)別呢懂盐???

  1. == 用來檢驗(yàn)值是否相等褥赊,需要遵循Equatable協(xié)議
  2. ===是用來檢驗(yàn)兩個對象是否是同一個實(shí)例對象 ??內(nèi)存地址是否相等,不支持值類型允粤,僅支持類實(shí)例(AnyObject?)使用崭倘。
@inlinable public func === (lhs: AnyObject?, rhs: AnyObject?) -> Bool
@inlinable public func !== (lhs: AnyObject?, rhs: AnyObject?) -> Bool

三、Comparable協(xié)議

Comparable協(xié)議繼承Equatable協(xié)議类垫,并多支持了其他比較方式 ?? 其中的運(yùn)算符有:< 司光、<=、>=悉患、> 残家、...、..<

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>
    ......
}

3.1 結(jié)構(gòu)體重寫 < 運(yùn)算符

修改上面的例子??

3.2 ??運(yùn)算符

??運(yùn)算符也是經(jīng)常被使用的售躁,表示 ?? 如果當(dāng)前的變量值為nil時坞淮,可以在??運(yùn)算符后面再給個默認(rèn)值,例如??

var age: Int? = nil
//?? 等價于 if let / guard let
print(age ?? 20)

我們先看看??函數(shù)的聲明??

接著我們?nèi)wift源碼中搜索?? <T>(optional:陪捷,找到實(shí)現(xiàn)??

上圖可知 ?? ??只有兩種類型回窘,一種是T,一種是T?市袖,主要是與 ?? 后面的返回值有關(guān)啡直,簡單來說 ?? ??后是什么類型烁涌,??返回的就是什么類型??

??后面是age1,而age1的類型是Int?酒觅,所以t的類型是 Int?

如果??后面是18撮执,Int類型 ?? t1的類型就是Int類型

如果??后面是String呢? ?? 會報(bào)錯,??要求類型一致舷丹,跟是否Optional類型無關(guān)抒钱。

補(bǔ)充:可選鏈

可選鏈 ?? 允許在一個鏈上來訪問當(dāng)前的屬性/方法,示例??

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

class LGTeacher{
    var name: String?
    var subject: LGSubject?
    
}

var s = LGSubject()
var t = LGTeacher()

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

修改賦值??

四颜凯、解包

接著我們來講解一下關(guān)于解包的幾個關(guān)鍵字谋币。

4.1 unsafelyUnwrapped

unsafelyUnwrappedOptional.swift中的對可選型變量的一個解包,它解包的結(jié)果強(qiáng)制解包的結(jié)果一樣的症概,例如??

// age有值
var age: Int? = 30
print(age!)
print(age.unsafelyUnwrapped)


// age1是nil
var age1: Int? = nil
print(age1!)
print(age1.unsafelyUnwrapped)

age是nil時的結(jié)果和強(qiáng)制解包一致 ?? 程序會崩潰瑞信。接下來我們看看unsafelyUnwrapped的聲明??

這里的-O,是指Build Setting中的配置選項(xiàng)Optimization Level設(shè)置成-O穴豫,如果是Fastest, Smallest[-Os]凡简,即我們使用release模式??

再次運(yùn)行??

4.2 as、 as? 和 as!

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

var age1 = age as Any
print(age1)

var age2 = age as AnyObject
print(age2)

// 打印結(jié)果
10
10
  1. as? 將類型轉(zhuǎn)換為 其他可選類型
var age: Int = 10
//as? 不確定類型是Double精肃,試著轉(zhuǎn)換下秤涩,如果轉(zhuǎn)換失敗,則返回nil
var age3 = age as? Double
print(age3)

// 打印結(jié)果
nil

注意: 此時的age3的類型是Double? ??

  1. as! ?? 強(qiáng)制轉(zhuǎn)換其他類型
var age: Int = 10
//as! 強(qiáng)制轉(zhuǎn)換為其他類型
var age4 = age as! Double
print(age4)
使用建議
  • 如果能確定類型司抱,使用as!即可筐眷。例如??
// 常規(guī)使用
var age: Any = 10
func test(_ age: Any) -> Int{
    return (age as! Int) + 1
}
print(test(age))

// 打印結(jié)果
11
  • 如果不能確定的,使用as?即可

五习柠、訪問控制權(quán)限

最后匀谣,我們再來了解一下Swift中的與訪問控制權(quán)限相關(guān)的幾個關(guān)鍵字。

5.1 private

訪問級別僅在當(dāng)前定義的作用域內(nèi)有效(單例中使用過??)资溃。

class LGTeacher{
    static let shareInstance = LGTeacher()
    private init(){}
}
var t = LGTeacher.shareInstance

5.2 filePrivate

訪問限制僅限制在當(dāng)前定義的源文件中武翎。

fileprivate class LGPartTimeTeacher: LGTeacher{
    var partTime: Double?
    init(_ partTime: Double) {
        super.init()
        self.partTime = partTime
    }
}

如果上述代碼是在Teacher.swift中,那么在main.swift無法訪問LGPartTimeTeacher溶锭。
再看下面這個示例??

上圖報(bào)錯的原因 ?? let pt 默認(rèn)的權(quán)限是 Internal的(接下來會講)宝恶,而LGPartTimeTeacher的訪問權(quán)限是fileprivate的,Internal權(quán)限大于fileprivate趴捅,系統(tǒng)不允許這樣垫毙,所以提示錯誤。

修改
  • 方式一 ?? 需要使用private / fileprivate修飾pt??
private let pt = LGPartTimeTeacher(10.0)
//或者
fileprivate let pt = LGPartTimeTeacher(10.0)
  • 方式二 ?? 如果直接定義在方法中的拱绑,可以不用訪問權(quán)限修飾符
func test(){
    let pt = LGPartTimeTeacher(10.0)
}

5.3 Internal

默認(rèn)訪問級別综芥,允許定義模塊中的任意源文件訪問,但不能被該模塊之外的任何源文件訪問(例如 import Foundation猎拨,其中Foundation就是一個模塊)膀藐。

// main.swift中
import Foundation
class LGTeacher{
    init(){}
}
let t = LGTeacher()

// 另一個custom.swift中
import AppKit
//訪問main.swift中t征峦,報(bào)錯:Expressions are not allowed at the top level
print(t)

上圖報(bào)錯原因 ?? t在main.swift文件中的默認(rèn)權(quán)限是Internal,只能在同一個模塊內(nèi)使用消请,其屬于Foundation模塊,而custom.swift文件中不能調(diào)用t类腮,是因?yàn)?code>其屬于AppKit模塊臊泰,與Foundation不是同一個模塊,所以不能訪問蚜枢。

5.4 public

開放式訪問缸逃,使我們能夠在其定義模塊任何源文件中使用代碼,并且可以從另一個源文件訪問源文件厂抽。但是只能在定義的模塊中繼承和子類重寫需频。

5.5 open

最不受限制的訪問級別,可以在任意地方筷凤、任意模塊間繼承昭殉、定義、重寫藐守。

public & open區(qū)別

這也是我們經(jīng)常容易混淆的點(diǎn)挪丢,區(qū)別主要是??

public不可繼承,open可繼承!

總結(jié)

本篇文章相對于前面所講解的Mirror卢厂、枚舉閉包知識點(diǎn)而言乾蓬,很簡單。主要介紹了Optional可選類型的概念慎恒,底層的實(shí)現(xiàn)和使用場景任内,然后順著Optional的匹配模式,講解了Equatable+Comparable協(xié)議融柬,順便總結(jié)了解包的幾種方式死嗦,最后講解了Swift不同于OC的知識點(diǎn) ?? 訪問控制權(quán)限,都是些大家在平常開發(fā)中碰到的知識點(diǎn)粒氧,希望大家掌握越走。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市靠欢,隨后出現(xiàn)的幾起案子廊敌,更是在濱河造成了極大的恐慌,老刑警劉巖门怪,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件骡澈,死亡現(xiàn)場離奇詭異,居然都是意外死亡掷空,警方通過查閱死者的電腦和手機(jī)肋殴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門囤锉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人护锤,你說我怎么就攤上這事官地。” “怎么了烙懦?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵驱入,是天一觀的道長。 經(jīng)常有香客問我氯析,道長亏较,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任掩缓,我火速辦了婚禮雪情,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘你辣。我一直安慰自己巡通,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布舍哄。 她就那樣靜靜地躺著扁达,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蠢熄。 梳的紋絲不亂的頭發(fā)上跪解,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天,我揣著相機(jī)與錄音签孔,去河邊找鬼叉讥。 笑死,一個胖子當(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
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年拆座,在試婚紗的時候發(fā)現(xiàn)自己被綠了主巍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冠息。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖孕索,靈堂內(nèi)的尸體忽然破棺而出逛艰,到底是詐尸還是另有隱情,我是刑警寧澤搞旭,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布散怖,位于F島的核電站,受9級特大地震影響选脊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜脸甘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一恳啥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丹诀,春花似錦钝的、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至枚荣,卻和暖如春碗脊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背橄妆。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工衙伶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人害碾。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓矢劲,卻偏偏與公主長得像,于是被迫代替她去往敵國和親慌随。 傳聞我的和親對象是個殘疾皇子芬沉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348

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