Optional分析
Swift中的可選類型(Optional)
,用于處理值缺失
的情況吩抓,有以下兩種情況
有值,且等于x
- 沒有值
這點可以通過Swift源碼->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(10)
Optional使用模式匹配
-
【Optional使用模式匹配】:既然
Optional
的本質(zhì)是枚舉,那么也可以使用模式匹配
來匹配對應(yīng)的值卖词,如下所示
//1巩那、聲明一個可選類型的變量
var age: Int? = nil
//2、通過模式匹配來匹配對應(yīng)的值
switch age {
case nil:
print("age 是個空值")
case .some(let val):
print("age的值是\(val)")
}
Optional解包
【Optional解包】
:因為是Optional
類型此蜈,當(dāng)我們需要從其中拿到我們想要的值時即横,需要對其進行解包
,因為當(dāng)前的可選項是對值做了一層包裝的裆赵,有以下兩種方式:
- 1东囚、
強制解包
:好處是省事,壞處是一旦解包的值是nil战授,那么程序就會崩潰
- 2页藻、通過
可選項綁定
:判斷當(dāng)前的可選項是否有值-
if let
:如果有值,則會進入if
流程 -
guard let
:如果為nil植兰,則會進入else
流程
-
//可選項解包
var age: Int? = 10
//強制解包
print(age!)
//if let 可選綁定
if let value = age {
print(value)
}
//guard let 可選綁定
guard let tmp = age else {
print("age值為nil")
return
}
print(tmp)
可選項綁定總結(jié)
- 1份帐、使用
if let
創(chuàng)建的內(nèi)容當(dāng)中value
僅僅只能在當(dāng)前if分支的大括號內(nèi)訪問 - 2、使用
guard let
定義的tmp
在當(dāng)前大括號外部也是能訪問的
unsafelyUnwrapped
這個和強制解包的內(nèi)容是一致的楣导,如下所示
//unsafelyUnwrapped 和強制解包內(nèi)容是一致的
var age: Int? = 30
print(age!)
print(age.unsafelyUnwrapped)
//如果age1是nil
var age1: Int? = nil
//print(age1!)
print(age1.unsafelyUnwrapped)
age1
是nil
的結(jié)果和強制解包一致废境,程序會崩潰
- 官方對其的描述如下
這里的-O
,是指target -> Build Setting -> Optimization Level
設(shè)置成-O時筒繁,如果使用的是age1.unsafelyUnwrapped
噩凹,則不檢查這個變量是否為nil,
- 1膝晾、設(shè)置
Optimization Level
為Fastest, Smallest[-Os]
- 2栓始、
edit Scheme -> Run -> Info -> Build Configuration
改為release
模式,然后再次運行發(fā)現(xiàn)血当,沒有崩潰幻赚,與官方所說是一致的
Equatable協(xié)議
在下面的例子中禀忆,可以通過==
判斷兩個可選項是否相等,原因是因為Optinal
在底層遵循了Equatable
協(xié)議
//Equatable協(xié)議
var age: Int? = 10
var age1: Optional<Int> = Optional(10)
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é)議來使用相等運算符(==)
和不等運算符(!=)
來比較兩個值相等還是不相等
,Swift標(biāo)準(zhǔn)庫中絕大多數(shù)類型都默認實現(xiàn)了Equatable
協(xié)議
例如下面的例子佳谦,對于Int
類型來說戴涝,系統(tǒng)默認實現(xiàn)了 ==
結(jié)構(gòu)體類型
對于自定義的類型
,如果想實現(xiàn) ==
钻蔑,應(yīng)該怎么辦呢啥刻?
- 如果像下面這樣寫,是會直接報錯的
- 可以通過
遵循Equatable協(xié)議實現(xiàn)
咪笑,如下所示
為什么呢可帽?其根本原因是因為遵守了Equatable
協(xié)議,系統(tǒng)默認幫我們實現(xiàn)了==方法
- 查看
SIL
文件
- 查看
__derived_struct_equals
方法的實現(xiàn)
Class類型
如果像Struct
那么寫窗怒,會報錯映跟,提示需要自己實現(xiàn)Equatable
協(xié)議的方法
- class中手動實現(xiàn)
Equatable
協(xié)議的方法
//class實現(xiàn)Equatable協(xié)議
class HTTeacher: Equatable {
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
static func == (lhs: HTTeacher, rhs: HTTeacher) -> Bool {
return lhs.age == rhs.age && lhs.name == rhs.name
}
}
var t1 = HTTeacher.init(age: 18, name: "name")
var t2 = HTTeacher.init(age: 19, name: "name")
print(t1 == t2)
區(qū)分 == vs ===
-
==
相當(dāng)于 equal to,用于判斷兩個值是否相等
-
===
是用來判斷兩個對象是否是同一個實例對象
(即內(nèi)存地址指向是否一致)
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é)議,重寫 < 運算符
?? 空運算符
如果當(dāng)前的變量為nil
贷洲,可以在??返回一個nil時的默認值
- 下面例子的打印結(jié)果是什么?
//?? 空運算符
var age: Int? = nil
//?? 等價于 if let / 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
,一種是T?
雁竞,主要是與 ?? 后面的返回值有關(guān)
(即簡單來說钦椭,就是??后是什么類型,??返回的就是什么類型
),如下所示
- ??后面是
age1
碑诉,而age1
的類型是Int?
彪腔,所以t
的類型是Int?
- 如果
??
是20
呢? 則t
的類型是Int
- 如果
??
是String
呢? -- 會報錯,??
要求類型一致
(跟是否是可選類型無關(guān))
可選鏈
可選鏈
則意味著 允許在一個鏈上來訪問當(dāng)前的屬性/方法,如下所示
//可選鏈
class HTTeacher{
var name: String?
var subject: HTSubject?
}
class HTSubject {
var subjectName: String?
func test() { print("test") }
}
var s = HTSubject()
var t = HTTeacher()
//可選鏈訪問屬性
if let tmp = t.subject?.subjectName {
print("tmp不為nil")
} else {
print("tmp為nil")
}
//可選鏈訪問方法
t.subject?.test()
運行結(jié)果如下进栽,因為t.subject為nil
德挣,所以屬性和方法都不會往下執(zhí)行
總結(jié)
-
Optional
的本質(zhì)是enum
,所以可以使用模式匹配
來匹配Optional的值 - Optional的
解包方式
有兩種:- 1快毛、
強制解包
:一旦為nil格嗅,程序會崩潰 - 2番挺、
可選值綁定
:if let
(只能在if流程的作用域內(nèi)訪問)、guard let
- 1快毛、
-
Equatable
協(xié)議:- 對于
swift標(biāo)準(zhǔn)庫中的絕大部分類型
都默認實現(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