認(rèn)識(shí)可選值
之前我們?cè)趯懘a過(guò)程中早就接觸過(guò)可選值捡多,比如我們?cè)诖a中這樣定義:
class IFLPerson {
var name: String?
}
當(dāng)前的name锁荔,我們就稱之為可選值
var name: String? == var name: Optional<String>
這兩種寫法是等同的
nameOptional的本質(zhì)是什么吵瞻,我們直接跳轉(zhuǎn)到 源碼 打開(kāi) Optional.swift 文件
既然Optional本質(zhì)是枚舉,那么我們也可以實(shí)現(xiàn)一個(gè)自定義的Optional
比如給定任一自然數(shù)嗤军,如果自然數(shù)是奇數(shù)則返回,否則返回nil, 該如何設(shè)計(jì)
enum IFLOptional<Value> {
case some(Value)
case none
}
func getOddValue(_ value: Int) -> IFLOptional<Int> {
if value % 2 == 0 {
return .none
}
return .some(value)
}
// 給定一個(gè)數(shù)組,刪除數(shù)組中的所有奇數(shù)
var array = [1, 2, 3, 4, 5, 6, 7, 8, 101]
for elem in array {
let value = getOddValue(elem)
switch value {
case let .some(value):
array.remove(at: array.firstIndex(of: value)!)
case .none:
print("\(value) not exist")
}
}
print("-------------")
print(array)
結(jié)果
[2, 4, 6, 8]
如果我們把上面的 getOddValue 返回值更換一下细睡,其實(shí)就和系統(tǒng)的Optional 使用沒(méi)什么差別
func getOddValue(_ value: Int) -> Int? {
if value % 2 == 0 {
return .none
}
return .some(value)
}
其實(shí)就是利用當(dāng)前編譯器的類型檢查來(lái)達(dá)到語(yǔ)法書寫層面的安全性
當(dāng)然如果每一個(gè)可選值都用模式匹配的方式來(lái)獲取值叹哭,在代碼書寫上就比較繁瑣忍宋,
我們還可以使用 if let 的方式來(lái)進(jìn)行可選值綁定
if let value = value {
array.remove(at: array.firstIndex(of: value))
}
還可以使用 guard let
如果 if let 憑空多了一個(gè)分支,guard let 是降低分支層次的辦法
可選鏈
我們都知道在OC 中我們給一個(gè)nil 對(duì)象發(fā)送消息什么也不會(huì)發(fā)生
Swift中我們是沒(méi)辦法向一個(gè)nil對(duì)象直接發(fā)送消息的风罩,但是借助可選鏈可以達(dá)到類似的效果
let str: String? = "abc"
let upperStr = str?.uppercased() // Optional<"ABC">
var str1: String?
let upperStr1 = str1?.uppercased() // nil
print(upperStr)
print(upperStr1)
結(jié)果
Optional("ABC")
nil
再來(lái)看下面這段代碼輸出什么
let str: String? = "aBc"
let lowerStr = str?.uppercased().lowercased()
var str1: String?
let lowerStr1 = str1?.uppercased().lowercased()
print(lowerStr)
print(lowerStr1)
結(jié)果
Optional("abc")
nil
同樣的 可選鏈對(duì)于下標(biāo)和函數(shù)調(diào)用 也適用
var closure: ((Int) -> ())?
closure?(1) // closure為nil 不執(zhí)行
closure為nil 不執(zhí)行
let dict: [String: Any]? = ["key1": 1, "key2": 2]
print(dict?["key1"])
print(dict?["key3"])
結(jié)果
Optional(1)
nil
?? 運(yùn)算符 (空合并運(yùn)算符)
( a ?? b ) 將對(duì)可選類型 a 進(jìn)行空判斷糠排,如果 a 包含一個(gè)值就進(jìn)行解包,否則就返回 一個(gè)默認(rèn)值 b .
- 表達(dá)式 a 必須是 Optional 類型
- 默認(rèn)值 b 的類型必須要和 a 存儲(chǔ)值的類型保持一致
運(yùn)算符重載
在源碼中我們可以看到除了重載了 ?? 運(yùn)算符超升,
Optional 類型還重載了 == , ?= 等 等運(yùn)算符入宦,實(shí)際開(kāi)發(fā)中我們可以通過(guò)重載運(yùn)算符簡(jiǎn)化我們的表達(dá)式
比如在開(kāi)發(fā)中我們定義了一個(gè)二維向量,這個(gè)時(shí)候我們想對(duì)兩個(gè)向量進(jìn)行基本的操作室琢,
那么我們就可以通過(guò)重載運(yùn)算符來(lái)達(dá)到我們的目的
struct Vector {
let x: Int
let y: Int
}
extension Vector {
static func + (firstVector: Vector, secondVector: Vector) -> Vector {
return Vector(x: firstVector.x + secondVector.x, y: firstVector.y + secondVector.y)
}
static prefix func - (vector: Vector) -> Vector {
return Vector(x: -vector.x, y: -vector.y)
}
static func - (firstVector: Vector, secondVector: Vector) -> Vector {
return firstVector + -secondVector
}
}
let v1 = Vector(x: 5, y: 10)
let v2 = Vector(x: 3, y: 4)
let v3 = v1 + v2
let v4 = v1 - v2
let v5 = -v1
print(v3)
print(v4)
print(v5)
結(jié)果
Vector(x: 8, y: 14)
Vector(x: 2, y: 6)
Vector(x: -5, y: -10)
隱士解析可選類型
隱式解析可選類型是可選類型的一種乾闰,使用的過(guò)程中和非可選類型無(wú)異
它們之間唯一 的區(qū)別是,隱式解析可選類型是你告訴 Swift 編譯器盈滴,在運(yùn)行時(shí)訪問(wèn)時(shí)涯肩,值不會(huì) 為 nil, 但是如果你導(dǎo)致nil值,運(yùn)行報(bào)錯(cuò)
var age: Int
var age1: Int!
age = nil // 報(bào)錯(cuò) 'nil' cannot be assigned to type 'Int'
age1 = nil
var age: Int巢钓?
var age1: Int!
age = nil // 改為可選值 就可以 賦值nil了
age1 = nil
let x = age1 % 2 // 這里不需要做解包的操作病苗,編譯器已經(jīng)幫我們做了
其實(shí)日常開(kāi)發(fā)中,我們比較常見(jiàn)的一種隱士可選類型
@IBOutlet weak var button: UIButton!
IBOutlet類型是Xcode強(qiáng)制為可選類型的竿报,因?yàn)樗皇窃诔跏蓟瘯r(shí)賦值的铅乡,而是在加載視圖的時(shí)候
你可以把它設(shè)置為普通的可選類型,但是如果這個(gè)視圖加載正確烈菌,它是不會(huì)為空的
與可選值有關(guān)的高階函數(shù)
- map : 這個(gè)方法接受一個(gè)閉包阵幸,如果可選值有內(nèi)容則調(diào)用這個(gè)閉包進(jìn)行轉(zhuǎn)換
var dict = ["one": "1", "two": "2"]
let result = dict["one"].map{ Int($0) }
print(result)
結(jié)果
Optional(Optional(1))
上面的代碼中我們從字典中取出字符串”1”花履,并將其轉(zhuǎn)換為Int類型,
但因?yàn)镾tring轉(zhuǎn)換成 Int不一定能成功挚赊,所以返回的是Int?類型诡壁,
而且字典通過(guò)鍵不一定能取得到值,所以map 返回的也是一個(gè)Optional荠割,
所以最后上述代碼result的類型為 Int?? 類型
那么如何把我們的雙重可選展平開(kāi)來(lái)妹卿,這個(gè)時(shí)候我們就需要使用到
- flatMap: 可以把結(jié)果展平成為單個(gè)可選值
var dict = ["one": "1", "two": "2"]
let result = dict["one"].flatMap{ Int($0) }
print(result)
結(jié)果
Optional(1)
- 注意,這個(gè)方法是作用在Optional的方法蔑鹦,而不是作用在 Sequence上的
- 作用在Sequence上的flatMap 在 Swift4.1 中被更名為 compactMap, 該方法可以將序列中的nil過(guò)濾出去
let array1 = ["1", "2", "3", nil]
let result2 = array1.compactMap {
$0
}
print(result2)
let array2 = ["1", "2", "3", "four"]
let result3 = array2.compactMap {
Int($0)
}
print(result3)
結(jié)果
["1", "2", "3"]
[1, 2, 3]
元類型夺克、AnyClass、Self (self)
- AnyObject: 代表任意類的 instance嚎朽,類的類型铺纽,僅類遵守的協(xié)議
- Any: 代表任意類型,包括 funcation 類型或者 Optional 類型
- AnyClass: 代表任意實(shí)例的類型