前言
本篇文章主要講解一下,之前用到過但是沒有仔細(xì)分析的幾個知識點(diǎn) ?? Optional
可選類型淑履、Equatable
協(xié)議和Comparable
協(xié)議,然后會補(bǔ)充關(guān)于解包
的相關(guān)場景僵娃,最后大致說明一下Swift中的訪問控制權(quán)限
险掀。
一、Optional
首先來看看Optional
可選類型田度,這個關(guān)鍵字在Swift中主要是用來處理值缺失
的情況妒御,有以下2種情況??
- 有值,且等于x
- 沒有值
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種方式??
-
強(qiáng)制解包
?? 好處是省事
宠互,壞處是解包的值是nil
會導(dǎo)致程序crash崩潰
!
var age: Int? = nil
print(age!)
運(yùn)行崩潰??
- 通過可選項(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")
}
注意:
- 使用
return
關(guān)鍵字,必須將該代碼塊放入函數(shù)體
中guard-let
是具備守護(hù)
功能耕赘,它必須搭配return
使用骄蝇,排除異常情況,守護(hù)guard(作用域)之后
的代碼操骡。
if-let
和guard-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類型
接下來我們看看類class
的Equatable協(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ū)別呢懂盐???
-
==
用來檢驗(yàn)值是否相等
褥赊,需要遵循Equatable協(xié)議
-
===
是用來檢驗(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
unsafelyUnwrapped
是Optional.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!
-
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? 不確定類型是Double精肃,試著轉(zhuǎn)換下秤涩,如果轉(zhuǎn)換失敗,則返回nil
var age3 = age as? Double
print(age3)
// 打印結(jié)果
nil
注意: 此時的age3的類型是Double?
??
-
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)粒氧,希望大家掌握越走。