本文主要分析Optional源碼、Equatable+Comparable協(xié)議
Optional分析
swift中的可選類型(Optional)
,用于處理值缺失
的情況怎棱,有以下兩種情況
有值,且等于x
沒有值
這點(diǎn)可以通過swift-source->Optional.swift
源碼(CMD+P
绷跑,搜索Optional)源碼來印證
@frozen
public enum Optional<Wrapped>: ExpressibleByNilLiteral {
......
//為nil
case none
......
//有值
case some(Wrapped)
......
}
- 通過源碼可知拳恋,
Optional
的本質(zhì)是enum
,當(dāng)前枚舉接收一個(gè)泛型參數(shù)Wrapped
砸捏,當(dāng)前Some的關(guān)聯(lián)值
就是當(dāng)前的Wrapper
纫普,下面兩種寫法完全等價(jià)
var age: Int? = 10
等價(jià)于
var age1: Optional<Int> = Optional(5)
- 【Optional使用模式匹配】:既然
Optional
的本質(zhì)是枚舉玩焰,那么也可以使用模式匹配
來匹配對(duì)應(yīng)的值,如下所示
//1、聲明一個(gè)可選類型的變量
var age: Int? = 10
//2拌蜘、通過模式匹配來匹配對(duì)應(yīng)的值
switch age{
case nil:
print("age 是個(gè)空值")
case .some(let val):
print("age的值是\(val)")
}
<!--或者這樣寫-->
switch age{
case nil:
print("age 是個(gè)空值")
case .some(10):
print("age的值是10")
default:
print("unKnow")
}
【Optional解包】:因?yàn)槭?code>Optional類型恩静,當(dāng)我們需要從其中拿到我們想要的值時(shí)理朋,需要對(duì)其進(jìn)行
解包
眯娱,因?yàn)楫?dāng)前的可選項(xiàng)是對(duì)值做了一層包裝的,有以下兩種方式:if let
:如果有值,則會(huì)進(jìn)入if流程guard let
:如果為nil滑废,則會(huì)進(jìn)入else流程-
1蝗肪、
強(qiáng)制解包
:好處是省事,壞處是一旦解包的值是nil蠕趁,那么程序就會(huì)崩潰強(qiáng)制解包為nil崩潰
2薛闪、通過
可選項(xiàng)綁定
:判斷當(dāng)前的可選項(xiàng)是否有值
//3、可選項(xiàng)解包
var age: Int? = nil
//3-1俺陋、強(qiáng)制解包
//如果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)
可選項(xiàng)綁定總結(jié)
作為一個(gè)開發(fā)者诱咏,有一個(gè)學(xué)習(xí)的氛圍跟一個(gè)交流圈子特別重要,這是一個(gè)我的iOS開發(fā)交流群:130 595 548缴挖,不管你是小白還是大牛都?xì)g迎入駐 袋狞,讓我們一起進(jìn)步,共同發(fā)展S澄荨(群內(nèi)會(huì)免費(fèi)提供一些群主收藏的免費(fèi)學(xué)習(xí)書籍資料以及整理好的幾百道面試題和答案文檔9堆臁)
1、使用
if let
創(chuàng)建的內(nèi)容當(dāng)中age僅僅只能在當(dāng)前if分支的大括號(hào)內(nèi)訪問2棚点、使用
guard let
定義的tmp在當(dāng)前大括號(hào)外部也是能訪問的
Equatable協(xié)議
在上面的例子中早处,可以通過==判斷兩個(gè)可選項(xiàng)是否相等,原因是因?yàn)镺ptinal在底層遵循了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標(biāo)準(zhǔn)庫中的類型
在swift中的類型砌梆,可以通過遵循Equatable
協(xié)議來使用相等運(yùn)算符(==)
和不等運(yùn)算符(!=)
來比較兩個(gè)值相等還是不相等
,Swift標(biāo)準(zhǔn)庫中絕大多數(shù)類型都默認(rèn)實(shí)現(xiàn)了Equatable
協(xié)議
例如下面的例子贬循,對(duì)于Int類型來說咸包,系統(tǒng)默認(rèn)實(shí)現(xiàn)了 ==
var age2: Int = 20
var isEqual = age1 == age2
print(isEqual)
<!--打印結(jié)果-->
false
自定義類型
對(duì)于自定義的類型
,如果想實(shí)現(xiàn) ==
甘有,應(yīng)該怎么辦呢诉儒?
-
如果像下面這樣寫,是會(huì)直接報(bào)錯(cuò)的
報(bào)錯(cuò)示意
可以通過
遵循Equatable協(xié)議實(shí)現(xiàn)
亏掀,如下所示
//2、自定義類型如何實(shí)現(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
為什么呢滤愕?其根本原因是因?yàn)樽袷亓?code>Equatable協(xié)議,系統(tǒng)默認(rèn)幫我們實(shí)現(xiàn)了==方法
-
查看
SIL
方法怜校,是否如我們猜想的一樣间影?經(jīng)過驗(yàn)證確實(shí)與我們猜測(cè)結(jié)論是一致的SIL驗(yàn)證-1
查看
__derived_struct_equals
方法的實(shí)現(xiàn)
SIL驗(yàn)證-2
疑問:如果是Class類型呢?
如果像Struct
那么寫茄茁,會(huì)報(bào)錯(cuò)魂贬,提示需要自己實(shí)現(xiàn)Equatable
協(xié)議的方法
class僅遵守Equatable協(xié)議報(bào)錯(cuò)
- class中手動(dòng)實(shí)現(xiàn)
Equatable
協(xié)議的方法
//3巩割、如果是class類型呢?需要手動(dòng)實(shí)現(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文件可以驗(yàn)證這一點(diǎn):底層是通過調(diào)用Optional的==來判斷
class的SIL驗(yàn)證
區(qū)分 == vs ===
==
相當(dāng)于 equal to键科,用于判斷兩個(gè)值是否相等
===
是用來判斷兩個(gè)對(duì)象是否是同一個(gè)實(shí)例對(duì)象
(即內(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
}
}
//===:判斷兩個(gè)對(duì)象是否是同一個(gè)
var t = CJLTeacher(age: 18, name: "CJL")
var t1 = t
t1.age = 20
print(t == t1)
<!--打印結(jié)果-->
true
除了==闻丑,還有!=以及其他的運(yùn)算符
Comparable協(xié)議
除了Equatable,還有Comparable協(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>
......
}
Struct重寫 < 運(yùn)算符
- 以struct為例茄厘,遵循
Comparable協(xié)議
,重寫 < 運(yùn)算符
//1卑硫、struct遵守Comparable協(xié)議
struct CJLTeacher: Comparable{
var age: Int
var name: String
//重載 < 符號(hào)
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
?? 空運(yùn)算符
如果當(dāng)前的變量為nil,可以在??返回一個(gè)nil時(shí)的默認(rèn)值
- 下面例子的打印結(jié)果是什么蚕断?
//?? 空運(yùn)算符
var age: Int? = nil
//?? 等價(jià)于 if le / guard let
print(age ?? 20)
<!--打印結(jié)果-->
20
- 進(jìn)入
Optional
源碼,查看??
實(shí)現(xiàn)
<!--返回T-->
@_transparent//空運(yùn)算符
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)
(即簡(jiǎn)單來說,就是??后是什么類型葛假,??返回的就是什么類型
),如下所示
-
??后面是age1障陶,而age1的類型是Int?,所以t的類型是
Int?
??是Int?
-
如果??是30呢? -- 類型是
Int
??是30
如果??是String呢? -- 會(huì)報(bào)錯(cuò)聊训,??要求類型一致(跟是否是可選類型無關(guān))
??是String
可選鏈
可選鏈 則意味著 允許在一個(gè)鏈上來訪問當(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()
運(yùn)行結(jié)果如下,因?yàn)閟為nil带斑,所以屬性和方法都不會(huì)往下執(zhí)行
可選鏈為nil的運(yùn)行結(jié)果
unsafelyUnwrapped(Optional.swift中的)
這個(gè)和強(qiáng)制解包的內(nèi)容是一致的鼓寺,如下所示
//***************7、unsafelyUnwrapped 和強(qiáng)制解包內(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é)果和強(qiáng)制解包一致勋磕,程序會(huì)崩潰
unsafelyUnwrapped為nil的崩潰示意
-
官方對(duì)其的描述如下
unsafelyUnwrapped官方說明
這里的
-O
妈候,是指target -> Build Setting -> Optimization Level
設(shè)置成-O時(shí),如果使用的是age.unsafelyUnwrapped
挂滓,則不檢查這個(gè)變量是否為nil苦银, 1、設(shè)置
Optimization Level
為Fastest, Smallest[-Os]
-
2、
edit Scheme -> Run -> Info -> Build Configuration
改為release
模式幔虏,然后再次運(yùn)行發(fā)現(xiàn)纺念,沒有崩潰,與官方所說是一致的根據(jù)官方描述調(diào)測(cè)
區(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
此時(shí)的age3的類型是Double?
as?運(yùn)行結(jié)果
-
as!
:強(qiáng)制轉(zhuǎn)換為其他類型
var age: Int = 10
//as! 強(qiáng)制轉(zhuǎn)換為其他類型
var age4 = age as! Double
print(age4)
運(yùn)行結(jié)果如下叭首,會(huì)崩潰
as!崩潰運(yùn)行結(jié)果
SIL分析
查看以下代碼的SIL文件
var age: Int = 10
var age3 = age as? Double
var age4 = age as! Double
as的SIL分析
- 常規(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é)
作為一個(gè)開發(fā)者,有一個(gè)學(xué)習(xí)的氛圍跟一個(gè)交流圈子特別重要夷都,這是一個(gè)我的iOS開發(fā)交流群:130 595 548眷唉,不管你是小白還是大牛都?xì)g迎入駐 ,讓我們一起進(jìn)步囤官,共同發(fā)展6簟(群內(nèi)會(huì)免費(fèi)提供一些群主收藏的免費(fèi)學(xué)習(xí)書籍資料以及整理好的幾百道面試題和答案文檔!)
Optional
的本質(zhì)是enum
党饮,所以可以使用模式匹配
來匹配Optional的值Optional的
解包方式
有兩種:1肝陪、
強(qiáng)制解包
:一旦為nil,程序會(huì)崩潰2刑顺、
可選值綁定
:if let
(只能在if流程的作用域內(nèi)訪問)氯窍、guard let
Equatable協(xié)議:
對(duì)于
swift標(biāo)準(zhǔn)庫中的絕大部分類型
都默認(rèn)實(shí)現(xiàn)了Equatable
協(xié)議對(duì)于自定義
Struct
類型,僅需要遵守Equatable
協(xié)議對(duì)于自定義
class
類型蹲堂,除了需要遵守Equatable
協(xié)議狼讨,還需要自己實(shí)現(xiàn)Equatable
協(xié)議的方法區(qū)分 == vs ===
==
相當(dāng)于 equal to,用于判斷兩個(gè)值是否相等
===
是用來判斷兩個(gè)對(duì)象是否是同一個(gè)實(shí)例對(duì)象
(即內(nèi)存地址指向是否一致)Comparable協(xié)議:
對(duì)于自定義類型柒竞,需要遵循
Comparable
協(xié)議政供,并重寫運(yùn)算符
??
空運(yùn)算符:??只有兩種類型,一種是T
朽基,一種是T?
布隔,主要是與 ?? 后面的返回值有關(guān)(即簡(jiǎn)單來說,就是??后是什么類型踩晶,??返回的就是什么類型
)可選鏈:允許在一個(gè)鏈上來訪問當(dāng)前的屬性/方法执泰,如果
為nil
,則不會(huì)執(zhí)行?后的屬性/方法
unsafelyUnwrapped
:與強(qiáng)制解包類似渡蜻,但是如果項(xiàng)目中設(shè)置target -> Build Setting -> Optimization Level
設(shè)置成-O時(shí),如果使用的是age.unsafelyUnwrapped
,則不檢查這個(gè)變量是否為nil區(qū)分 as茸苇、as?排苍、 as!
as
將類型轉(zhuǎn)換為其他類型as?
將類型轉(zhuǎn)換為其他可選類型
as!
強(qiáng)制轉(zhuǎn)換為其他類型使用建議:能確定使用
as!
,不能確定使用as?