可選項(xiàng)(Optional)
- 可選項(xiàng)店溢,一般也叫做可選類型墙基,它允許將值設(shè)置為nil
- 在類型名稱后面加個(gè)
問(wèn)號(hào)?
來(lái)定義一個(gè)可選項(xiàng)
var name: String = "Jack"
name = nil
var age: Int?
age = 10
age = nil
C語(yǔ)言中添谊,變量聲明之后财喳,會(huì)自動(dòng)被賦予初始值,Swift則不會(huì)這樣斩狱,但是Swift的Optional則會(huì)有默認(rèn)的初始值nil
func test() {
var age: Int -->沒(méi)有初始值耳高,必須賦值之后才能使用
var weight: Double? --> 默認(rèn)初始值為nil, 等價(jià)于 var weight: Double? = nil
}
使用場(chǎng)景所踊,有可能需要使用nil的地方
var array = [1,13,45,33]
func get(_ index: Int) -> Int? {
if index < 0 || index >= array.count {
return nil
}
return array[index]
}
print(get(1))// Optional(15)
print(get(-1))// nil
print(get(5)) // nil
強(qiáng)制解包(Forced Unwrapping)
- 可選項(xiàng)是對(duì)其他類型的一層包裝泌枪,可以將它理解成一個(gè)盒子
- 如果為
nil
,那么它是個(gè)空盒子 - 如果不為
nil
秕岛,那么盒子里裝的是:被包裝類型的數(shù)據(jù)
- 如果為
var age: Int? // 默認(rèn)就是nil
age = 10
age = nil
- 如果要從可選項(xiàng)中取出被包裝的數(shù)據(jù)(將盒子里裝的東西取出來(lái))碌燕,需要使用
感嘆號(hào)误证!
進(jìn)行強(qiáng)制解包
var speed: Int? = 10
var speedInt: Int = speed!
speedInt += 10
- 如果對(duì)值為
nil
的可選項(xiàng)(空盒子)進(jìn)行強(qiáng)制解包,將會(huì)產(chǎn)生運(yùn)行時(shí)錯(cuò)誤
var age: Int?
age!
運(yùn)行報(bào)錯(cuò):Fatal error: Unexpectedly found nil while unwrapping an Optional value: file Optional.xcplaygroundpage
判斷可選項(xiàng)是否包含值
let number = Int("123")
if number != nil {
print("字符串轉(zhuǎn)換整數(shù)成功:\(number!)")
} else {
print("字符串轉(zhuǎn)換整數(shù)失敗")
}
可選項(xiàng)綁定(Optional Binding)
- 可以使用
可選項(xiàng)綁定
來(lái)判斷選項(xiàng)是否包含值
如果包含就自動(dòng)解包修壕,把值賦給一個(gè)臨時(shí)的常量(let
)或者變量(var
)愈捅,并返回true
,否則返回false
if let number = Int("123") {
print("字符串轉(zhuǎn)換整數(shù)成功: \(number)")
-->number是強(qiáng)制解包之后的Int值
-->number的作用僅限于這個(gè)大括號(hào)
} else {
print("字符串轉(zhuǎn)換整數(shù)失敗")
}
用在枚舉值中
enum Season: Int {
case spring = 1, summer, autumn, winter
}
if let season = Season(rawValue: 6) { -->有可能得到對(duì)應(yīng)的case慈鸠, 有可能得不到蓝谨,那么結(jié)果就為空
switch season {
case .spring:
print("the season is spring")
default:
print("the season is other")
}
} else {
print("no such season")
}
注意下面的等價(jià)用法
if let first = Int("4") {
if let second = Int("42") {
if first < second && second < 100 {
print("\(first) < \(second) < 100")
}
}
}
======等價(jià)寫法如下, 通過(guò)逗號(hào)來(lái)分割 帶可選項(xiàng)綁定的 if判斷條件
if let first = Int("4"),
let second = Int("42"),
first < second && second < 100 {
print("\(first) < \(second) < 100")
}
while循環(huán)中使用可選項(xiàng)綁定
---->遍歷數(shù)組,將畫遇到的整數(shù)都加起來(lái)青团,如果遇到負(fù)數(shù)或者非數(shù)字譬巫,停止便利
var strs = ["10", "20", "abc", "-30", "30"]
var index = 0
var sum = 0
while let num = Int(strs[index]), num > 0 {
sum += num
index += 1
}
print(sum) // 30
空合并運(yùn)算符??(Nil-Coalescing Operator)
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?) rethrows -> T?
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T
a ?? b
a
必須是可選項(xiàng)b
是可選項(xiàng) 或者 不是可選項(xiàng)b
跟a
的存儲(chǔ)類型必須相同- 如果
a
不為nil
,就返回a- 如果
a
為nil
壶冒,就返回b- 如果
b
不是可選項(xiàng)缕题,返回a
時(shí)回自動(dòng)解包
let a: Int? = 1
let b: Int? = 2
let c = a ?? b //c是Int?, Optional(1)
let a: Int? = nil
let b: Int? = 2
let c = a ?? b //c是Int?, Optional(2)
let a: Int? = nil
let b: Int? = nil
let c = a ?? b //c是Int?, nil
let a: Int? = 1
let b: Int = 2
let c = a ?? b //c是Int, 1
let a: Int? = nil
let b: Int = 2
let c = a ?? b //c是Int, 2
如果沒(méi)有??
,有些代碼寫起來(lái)就比較麻煩
let a: Int? = nil
let b: Int = 2
let c: Int
if let tmp = a {
c = tmp
} else {
c = b
}
多個(gè)??一起使用
從左往右算
let a: Int? = 1
let b: Int? = 2
let c = a ?? b ?? 3 //c是Int胖腾, 1
let a: Int? = nil
let b: Int? = 2
let c = a ?? b ?? 3 //c是Int烟零, 1
let a: Int? = nil
let b: Int? = nil
let c = a ?? b ?? 3 //c是Int, 1
??跟if let配合使用
let a: Int? = nil
let b: Int? = 2
if let c = a ?? b {
print(c)
}
-->類似于 if a != nil || b != nil {}
if let c = a, let d = b {
print(c)
print(d)
}
-->類似于 if a != nil && b != nil {}
if語(yǔ)句實(shí)現(xiàn)登錄
func login(_ info: [String : String]) {
let username: String
if let tmp = info["username"] {
username = tmp
} else {
print("請(qǐng)輸入用戶名")
return
}
let password: String
if let tmp = info["password"] {
password = tmp
} else {
print("請(qǐng)輸入密碼")
return
}
print("用戶名:\(username)","密碼:\(password)", "登錄ing")
}
login(["username": "jack", "password": "123456"])
login(["password": "123456"])
login(["username": "jack"])
************運(yùn)行結(jié)果
用戶名:jack 密碼:123456 登錄ing
請(qǐng)輸入用戶名
請(qǐng)輸入密碼
guard語(yǔ)句
上面的登錄業(yè)務(wù)通過(guò)guard
來(lái)實(shí)現(xiàn)
func login(_ info: [String : String]) {
guard let username = info["username"] else {
print("請(qǐng)輸入用戶名")
return
}
guard let password = info["password"] else {
print("請(qǐng)輸入密碼")
return
}
print("用戶名:\(username)","密碼:\(password)", "登錄ing")
}
login(["username": "jack", "password": "123456"])
login(["password": "123456"])
login(["username": "jack"])
***********運(yùn)行結(jié)果
用戶名:jack 密碼:123456 登錄ing
請(qǐng)輸入用戶名
請(qǐng)輸入密碼
隱式解包(Implicitly Unwrapped Optional)
- 在某些情況下咸作,可選項(xiàng)一旦被設(shè)定值之后锨阿,就會(huì)一直擁有值
- 在這種情況下,可以去掉檢查记罚,也不必每次訪問(wèn)的時(shí)候都進(jìn)行解包墅诡,因?yàn)樗艽_定每次訪問(wèn)的時(shí)候都有值
- 可以在類型后面加個(gè)
感嘆號(hào)!
定義一個(gè)隱式解包的可選項(xiàng)
let num1: Int! = 10 //如果能確定某個(gè)變量會(huì)一直不為nil桐智,可以使用這種聲明方式末早,
let num2: Int = num1 // 可以直接使用,系統(tǒng)會(huì)自動(dòng)解包后賦值说庭,當(dāng)然也可以使用強(qiáng)制解包方式然磷,但是num1的本質(zhì)還是一個(gè)optional
if num1 != nil {
print(num1 + 6)
}
if let num3 = num1 {
print(num3)
}
如果給隱式解包類型的optional
變量賦值nil
,當(dāng)該變量被賦值給其他變量/常量式刊驴,會(huì)出現(xiàn) 運(yùn)行時(shí)報(bào)錯(cuò)
let num4: Int! = nil
let num5: Int = num4 -->注意姿搜,程序執(zhí)行完這一句才會(huì)報(bào)錯(cuò),在執(zhí)行這一句的時(shí)候捆憎,會(huì)對(duì)num4進(jìn)行隱式解包舅柜,結(jié)果發(fā)現(xiàn)它內(nèi)部是nil,因此把nil取出來(lái)賦值給 num5: Int 的時(shí)候就報(bào)錯(cuò)了躲惰,因?yàn)?num5 不能為空
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
上述的案例致份,讓人感覺使用帶!的optional存在很多風(fēng)險(xiǎn),其實(shí)用下面的方式莫不是更保險(xiǎn)點(diǎn)嗎础拨,
var num5: Int = 10
let num5: int = num56
這樣至少在程序運(yùn)行之前知举,就能看到錯(cuò)誤瞬沦,如果有的話。
實(shí)際上雇锡,我們大部分場(chǎng)景下逛钻,也都是推薦使用帶
?
的optional
,那用!
聲明的optional
存在的意義是什么呢锰提?
- 如果你提供一套api給外界使用曙痘,并且期望使用者嚴(yán)格遵守你的要求不要傳
nil
過(guò)來(lái),并且認(rèn)為使用者在錯(cuò)誤使用的時(shí)候而導(dǎo)致程序直接報(bào)錯(cuò)崩潰就是你期待的立肘,那么你可以使用這種用法边坤。除此之外,還是不用為妙谅年。
字符串插值
可選項(xiàng)在字符串插值或者直接打印的時(shí)候茧痒,編譯器會(huì)發(fā)出警告
var age: Int? = 10
print("My age is \(age)")//直接進(jìn)行字符串插值,會(huì)產(chǎn)生編譯器警告
//?String interpolation produces a debug description for an optional value; did you mean to make this explicit??
3種常用的方法可以消除警告
var age: Int? = 10
print("My age is \(age!)")
print("My age is \(String(describing: age))")
print("My age is \(age ?? 0)")
多重可選項(xiàng)
var num1: Int? = 10
var num2: Int?? = num1
var num3: Int?? = 10
print(num2 == num3) // true
num1
融蹂、num2
旺订、num3
的結(jié)構(gòu)分別如下
再看如下
var num1: Int? = nil
var num2: Int?? = num1
var num3: Int?? = nil
print(num2 == num3) // false
(num2 ?? 1) ?? 2 //1
(num1 ?? 1) ?? 2 //2
可以使用LLDB指令
frame variable -R
或者fr v -R
來(lái)查看變量的內(nèi)部信息。
有關(guān)Swift的可選項(xiàng)就整理到這里超燃。