可選型 就像是 薛定諤的貓
前言
作為一個(gè)Swifter 的初學(xué)者瞬欧,弄清楚 <font color=red>可選型(Optional)</font> 至關(guān)重要
我們來看一下 官方對 Optional 的定義:
A type that represents either a wrapped value or nil, the absence of a value.
public enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
/// The presence of a value, stored as `Wrapped`.
/// 以包裝形式存在的值
case some(Wrapped)
...
}
也就是說 Optional 是 一種含有2個(gè)類型的枚舉,它要么為空鳍烁,要么有值
有值: var statusCode: Int? = 404
空值: var errorCode: Int? = nil
等價(jià)于
var errorCode: Int? (默認(rèn)可選型 值為nil)
?
類比
當(dāng)我們在代碼里聲明一個(gè)可選型的類型時(shí),用 敬扛?
來執(zhí)行
let number: Int? = 10 // number 此時(shí)就是可選型
當(dāng)我們想要用到 number 這個(gè)值的時(shí)候晰洒,我們?nèi)ピ囍蛴∷玫?: Optional(10)
當(dāng)我們用 number
去與同類型的值相加時(shí) let a = number + 10
,此時(shí)就會(huì)得到一個(gè)編譯時(shí)的錯(cuò)誤
也就是說,可選型 Optional(10) 不能直接參與運(yùn)算舔哪。
?
強(qiáng)制解包 !
在 可選型 的后面通過增加!
的形式, 解開被欢顷?
包裝的類型 number
let a = number! + 10
# 得到a 的類型是 Int槽棍,而不是Int捉蚤?,就代表此時(shí)a 類型明確 是Int
那么反觀 number ,當(dāng)我們打印的時(shí)候發(fā)現(xiàn)炼七,number 還是 Optional(10)
# 意味著 強(qiáng)制解包 并不會(huì)影響 原來被包裝的類型
如果 let number: Int? = nil 的話缆巧,則強(qiáng)制解包 number! 會(huì)崩潰
?
我們來類比一下薛定諤的貓
一只貓?jiān)谙渥永铮嬖谥环N既死又活的狀態(tài)豌拙,直到打開的時(shí)候我們才知道結(jié)果
薛定諤的貓 | 可選型 | let number: Int? = 10 |
---|---|---|
貓 | 包裝值的類型 | Int? |
箱子 | 包裝值存放的盒子 | Int? 存放的地方 |
陕悬? | 套貓的繩子 | ? |
按傅! | 開盒子的鑰匙 | 捉超! |
?
在這里, 我們把 死活狀態(tài) 類比為貓的 存在與否
? 比作套索 唯绍,我們用套索去盒子里圈喵星人的時(shí)候拼岳,我們并不知道盒子里有沒有喵星人
! 比作開盒子的鑰匙,開不開就看你控不控制的住了
?
擼er 們 况芒,不斬?zé)o名之輩惜纸,不開無貓之門
但實(shí)際上,沒打開盒子的時(shí)候绝骚,最多存在著2種情況
1. Int? 的值為 nil 喵星人不在
2. Int? 的值為 10 喵星人活蹦亂跳耐版,ok 擼它
我們不能貿(mào)然的用鑰匙
去開門
你擼貓的動(dòng)作都準(zhǔn)備好了
發(fā)現(xiàn)沒有貓, 你難不難受?
你能想象你在輸入 一大串密碼后 查看開你的銀行卡余額
發(fā)現(xiàn)一毛沒有
你會(huì)怎么樣?
你會(huì)崩潰啊压汪,系統(tǒng)也會(huì)崩潰啊
但如果當(dāng)我們確定有喵星人的時(shí)候粪牲,我們是可以開門把喵星人揪出來的,
雖然我們把貓從盒子里拿出來了止剖,但是盒子又恢復(fù)了初始狀態(tài),里面還是有2種可能
結(jié)論:
1.當(dāng)我們 不確定可選型 有沒有值 的時(shí)候虑瀑,最好是不要去 強(qiáng)制解包
2.可選型有值,用!
去取值滴须,反之不取
3.強(qiáng)制解包 并不影響 被解包的類型舌狗,依舊還是可選類型
?
可選綁定 if let
解包除了強(qiáng)制解包這種粗魯?shù)男袨橥猓€有可選綁定
if let value = someOptional {
// do next
// 就是說如果 someOptional 有值扔水,才會(huì)進(jìn) if 循環(huán)
// value 是 someOptional強(qiáng)制解包后的值痛侍,類型是確定的
// value 的作用域 僅限于 {} 內(nèi)
// 可以是 if let / if var
}
舉個(gè)例子:
let number = Int("777")
// 這里Int("777") 也是Int 類型 初始化的一種
// 返回的是一個(gè)可選型
// 因?yàn)槿绻麄髯址?"zzz" 就無法轉(zhuǎn)為Int,結(jié)果是不確定的
if let a = number {
print("轉(zhuǎn)Int 成功",a) // 此時(shí)的a 是 number 解包后的值 777,而不是 Optional(777)
} else {
print("轉(zhuǎn)Int 失敗 ")
}
* 如果有多個(gè)條件的可選值需要共同成立主届,比如
if let a = Int("1"),
let b = Int("2") {
print(a + b)
}
// 省略一個(gè)if 赵哲,中間以 逗號 拼接
if let 配合 while 使用
// 計(jì)算數(shù)組內(nèi) 為正數(shù)的值 的總數(shù)
var array = ["10","10","-10","a"]
var index = 0
var sum = 0
while let s1 = Int(array[index]),s1 > 0 {
sum += s1
index += 1
}
print(sum)
?
guard let
如果說 if let
是 走到最后
,那么 guard let
就代表著 <font color=red>提前退出</font>
guard 相比 if 來說君丁,用起來更加舒服
guard let value = someOptional else {
// value 沒有值枫夺,才會(huì)進(jìn) else
// value 的作用域 不限于{}
// 可以是 guard let / guard var
// must do return
// guard 必須要返回 退出 當(dāng)前作用域
}
print(value) // 這里的value 是 someOptional 解包后的值
舉個(gè)例子:
let number = Int("777")
guard let num1 = number else {
print("轉(zhuǎn)換失敗")
return
}
print(num1 + 10)
// 能來到這里就說明 num1 是有值的,類型是確定的
// 打印 787
guard 相較于 if
- 層次比較分明
- 變量作用域更加廣泛
- 提前退出绘闷,效率更高
?
空合并 運(yùn)算符 ??
系統(tǒng)的定義是這樣的:
func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?) rethrows -> T?
舉個(gè)例子:
表達(dá)式 a ?? b // 它意味著:如果a 不為空橡庞,返回a,否則返回b
- 這里有一些注意點(diǎn):
1. a 必須是可選型印蔗,從定義中可以看到第一個(gè)參數(shù).. optional: T? , 就證明了a 必須是可選型
2. a 和 b 的類型 除了nil 這個(gè)類型以外扒最,另外一個(gè)類型必須是一致的
比如:
let a: Int? = 10
let b: Int? = 20
let c = a ?? b
// c = Optional(10)
// a 和 b 都是可選型,另外一個(gè)類型都是 Int
3.b 可以是可選型,也可以不是
比如:
let a: Int? = 10
let b: Int = 20
let c = a ?? b
// c = 10
4.如果a 不為nil华嘹,返回a吧趣,此時(shí)系統(tǒng)并不會(huì) 牽扯到 b,b是多余的.
# 這里我們 注意 defaultValue: @autoclosure () 第二個(gè)參數(shù) 是帶有自動(dòng)閉包的
# 自動(dòng)閉包讓你能夠延遲求值耙厚,因?yàn)橹钡侥阏{(diào)用這個(gè)閉包强挫,代碼段才會(huì)被執(zhí)行
5.如果a 不為nil ,但b 不是可選型薛躬,返回a 的時(shí)候 a會(huì)自動(dòng)解包
什么意思呢俯渤?比如
let a: Int? = 10
let b: Int = 20
let c = a ?? b
// a 是可選值 不為nil ,b 是 Int泛豪,非可選型
// 此時(shí)c = a! ,因?yàn)??? 后面的 b不是可選型,所以c 自動(dòng)解包
- 結(jié)論
其實(shí)從官方定義可以看出稠诲,.... T?) rethrows -> T?
?? 的結(jié)果返回什么類型,取決于 參數(shù) b 的類型
?
隱式解包
有時(shí)候诡曙,在特定的情況下臀叙,一個(gè) Optional 可以被確保永遠(yuǎn)都有值 (或者說理應(yīng)永遠(yuǎn)都有值)
這時(shí)候每次使用都進(jìn)行判空和解包,就顯得很多余
在 可選型 初始化的時(shí)候 后面直接加 !价卤,在編譯的時(shí)候就 讓其自動(dòng)解包
你會(huì)覺得這 提莫 不就是 另一種形式 的強(qiáng)制解包嗎劝萤?
強(qiáng)制解包可選型
var aString: String? = "ttt"
aString! // 強(qiáng)制解包 "ttt"
隱式解包可選型
var aString: String! = "ttt" // 隱式解包
* 依舊可以置nil,因?yàn)閍String 依舊是可選型
aString! // 強(qiáng)制解包 "ttt"
aString // Optional("ttt")
aString = nil
* 如果將aString 賦值給bString慎璧,則 bString不需要解包了
let bString: String = aString
bString = "ttt"
那么問題來了
1.如果我知道盒子里有喵星人床嫌,我還用套索 費(fèi)勁的 去抓它嗎?換言之 我還需要用可選型修飾嗎胸私?
2.為什么不直接用鑰匙打開盒子的門呢厌处?為什么不直接定義為String類型呢?
3.我如果確定它一直有值岁疼,從某種意義上說阔涉,那它不就丟失了可選型的初衷嗎?
我的理解,不知道對不對瑰排,還請指正
xib 中的控件我們都應(yīng)該清楚贯要,當(dāng)我們拖拽出一個(gè)button 的時(shí)候 ,會(huì)發(fā)現(xiàn):
@IBOutlet weak var btn: UIButton!
這個(gè)btn 就是 隱式解包椭住,對吧崇渗,就是說用到這個(gè)btn 的時(shí)候,自動(dòng)解包拿到btn
-
這個(gè)地方為什么要
隱式解包
呢京郑?我認(rèn)為:- 每個(gè)屬性初始化的時(shí)候都是要有值的宅广,或者是可選的,或者隱式解包的
- 拖拽出來的btn 在 awakeFormNib 初始化結(jié)束之后 才會(huì)被設(shè)置屬性
- 初始化之后 大概率是要用的吧傻挂,不然也不會(huì)去拖線
- 也就是說btn 肯定是會(huì)存在的乘碑,但是要在初始化之后挖息,除非你把連的線砍了
-
那為什么不用
?
修飾呢- 大概率是因?yàn)橛媒鹁埽棵看味家?強(qiáng)制解包很麻煩,所以這里的屬性用!
結(jié)論:
隱式解包不是最安全的做法套腹,慎用
參考:When Should You Use Implicitly Unwrapped Optionals
?
字符串插值
有時(shí)候我們在打印一個(gè)字符串的時(shí)候绪抛,有可選型的插值,則會(huì)給出警告
var age: Int? = 10
# 三個(gè)fix 對應(yīng)下面3中解決方法
print("zzz is \(age!)") // 強(qiáng)制解包
print("zzz is \(String(describing: age))") // String 初始化
print("zzz is \(age ?? 20)") // 空合并給默認(rèn)值
?
后續(xù)有新的繼續(xù)補(bǔ)充...
個(gè)人筆記 有問題請多多指教
thanks for reading
....