//可選項(xiàng) Optional
//可選項(xiàng),一般也叫可選類型坪郭,它允許將值設(shè)置為nil,默認(rèn)不允許設(shè)置為nil
//在類型名稱后面加個(gè)問(wèn)號(hào)患民? 來(lái)定義一個(gè)可選項(xiàng)
var str: String? = "123"
str = nil
var age: Int? //可選值默認(rèn)就是nil拼苍,var age:Int等價(jià)于var age:Int玖姑?= nil
age = 10
age = nil
var array = [1, 15, 40, 29]
func get(_ index:Int) -> Int? { //如果函數(shù)返回值可能為nil,返回值類型就要加社付?
if index < 0 || index >= array.count {
return nil
} else {
return array[index]
}
}
print(get(1)) //Optional(15)
print(get(-1))//nil
print(get(4)) //nil
var age1:Int = 15
print(age1) //15
var age2:Int? = 15
print(age2) //Optional(15), 這個(gè)是可選類型
//強(qiáng)制解包(Forced Unwrapping)
//可選項(xiàng)是對(duì)其他類型的一層包裝,可以將它理解為一個(gè)盒子
//如果為nil邻耕,那么它是個(gè)空盒子
//如果不為nil鸥咖,那么盒子里面裝的是:被包裝類型的數(shù)據(jù)
var age3: Int? //默認(rèn)就是nil
age3 = 10
age3 = nil
把10賦值給age3,如圖相當(dāng)于Int10這個(gè)藍(lán)色的框框去掉
//如果要從可選項(xiàng)中取出被包裝的數(shù)據(jù)(將盒子里面裝的東西取出來(lái))兄世,需要使用啼辣!進(jìn)行強(qiáng)制解包
var age4: Int? = 10
var ageInt: Int = age4!
ageInt += 10
var age5: Int? = 10
var num = age5! + 20 //30
print(age5) //Optional(10)
//強(qiáng)制解包只是取值的過(guò)程,并不會(huì)修改age5本身的可選項(xiàng)值
//如果對(duì)值為nil的可選項(xiàng)(空盒子)進(jìn)行強(qiáng)制解包御滩,將會(huì)產(chǎn)生運(yùn)行時(shí)錯(cuò)誤鸥拧,crash奔潰
//報(bào)錯(cuò)信息:Fatal error: Unexpectedly found nil while unwrapping an Optional value
var age6: Int?
//age6! //這里會(huì)crash,報(bào)錯(cuò)先注釋掉了
//判斷可選項(xiàng)是否包含值
let number = Int("123")
if number != nil {
print("字符串轉(zhuǎn)換成功:\(number!)") //這里的number可能轉(zhuǎn)換失敗削解,返回0或者-1都不合適富弦,只能返回nil,所以這種通過(guò)字符串轉(zhuǎn)換成Int類型氛驮,number是可選類型腕柜,需要強(qiáng)制解包
} else {
print("字符串轉(zhuǎn)換整數(shù)失敗,返回nil")
}
// 字符串轉(zhuǎn)換整數(shù)成功:123
//var num = Int("xxx")
//可選項(xiàng)綁定
//可以使用可選項(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, summber, autumn, winter
}
//var sea = Season(rawValue: 2) 這里的sea是可選項(xiàng)蓖扑,因?yàn)楂@取枚舉傳入原始值唉铜,可能不存在對(duì)應(yīng)原始值的枚舉變量
if let season = Season(rawValue: 6) { //這里需要注意,可選項(xiàng)綁定會(huì)幫忙自動(dòng)解包律杠,獲取到的直接就是Season類型潭流,而不是Season?可選類型
switch season {
case .spring:
print("the season is spring")
default:
print("the season is other")
}
} else {
print("no such season")
}
//no such season
// 等價(jià)寫(xiě)法
if let first = Int("4") {
if let second = Int("42") {
if first < second && second < 100 {
print("\(first) < \(second) < 100")
}
}
}
//上面的寫(xiě)法等價(jià)于下面的寫(xiě)法:
if let first = Int("4"), //這里的可選項(xiàng)綁定柿赊,必須要使用, 逗號(hào)隔開(kāi)幻枉,不能直接使用&&符合拼接
let second = Int("42"),
first < second && second < 100 {
}
//while循環(huán)中使用可選項(xiàng)綁定
//遍歷數(shù)組碰声,將遇到的正數(shù)都加起來(lái),如果遇到負(fù)數(shù)或者非數(shù)字熬甫,停止遍歷
var strs = ["10", "20", "30", "abc", "-20", "30"]
var index = 0
var sum = 0
while let num = Int(strs[index]), num > 0 {
sum += num
index += 1
}
print(sum) //60 10+20+30=60
//空合并運(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
//1. 其中a必須是可選項(xiàng),
//2. b是可選項(xiàng) 或者不是可選項(xiàng)
//3. b和a的存儲(chǔ)類型必須相同
//4. 如果a 不為nil胰挑,就返回a
//5. 如果a 為nil,就返回b
//6. 如果b 不是可選項(xiàng)椿肩,返回a時(shí)自動(dòng)解包
let a: Int? = 1
let b: Int? = 2
let c = a ?? b //1
let a1: Int? = nil
let b1: Int? = 2
let c1 = a1 ?? b1 //2
let a2: Int? = nil
let b2: Int? = nil
let c2 = a2 ?? b2 //nil
let a3: Int? = 1
let b3: Int = 2
let c3 = a3 ?? b3 //c是Int, 1
let a4: Int? = nil
let b4: Int = 2
let c4 = a4 ?? b4 //2
let a5: Int? = nil
let b5: Int = 2
//如果不使用?? 運(yùn)算符
let c5: Int
if let tmp = a5 { //可選項(xiàng)會(huì)綁定失敗
c5 = tmp
} else {
c5 = b5
}
//多個(gè)??一起使用
let a6: Int? = 1
let b6: Int? = 2
let c6 = a6 ?? b6 ?? 3 //c是Int瞻颂,1
let a7: Int? = nil
let b7: Int? = 2
let c7 = a7 ?? b7 ?? 3 //c7是Int,2
let a8: Int? = nil
let b8: Int? = nil
let c8 = a8 ?? b8 ?? 3 //c8是Int郑象,3
let a9: Int? = 1
let b9: Int? = 2
let c9 = a9 ?? 3 ?? b //c9是Int贡这,這種會(huì)警告,??左邊只能是可選項(xiàng)厂榛,不可以是Int
//??跟if let配合使用
let a10: Int? = nil
let b10: Int? = 2
if let c10 = a10 ?? b10{
print(c10)
}
//類似于 if a != nil || b != nil, 并且可以自動(dòng)解包賦值給 c10
//類似這種會(huì)有多個(gè)可選項(xiàng)解包可以使用多個(gè) ?? 連接起來(lái)使用
if let c = a, let d = b {
print(c)
print(d)
}
//類似于 if a != nil && b != nil
//a自動(dòng)解包成功并且b自動(dòng)解包成功盖矫,if條件才滿足,且的關(guān)系
//if 語(yǔ)句實(shí)現(xiàn)登陸
func login(_ info: [String: String]) {
var username: String
if let tmp = info["username"] {
username = tmp
} else {
username = ""
print("請(qǐng)輸入用戶名")
return
}
var password: String
if let tmp = info["password"] {
password = tmp
} else {
password = ""
print("請(qǐng)輸入密碼")
return
}
//if username...
//if password...
print("用戶名:\(username)","密碼:\(password)","登陸ing")
}
login(["username":"jack","password":"123456"]) //用戶名:jack 密碼:123456 登陸ing
login(["password":"123456"]) //請(qǐng)輸入用戶名
login(["username":"jack"]) //請(qǐng)輸入密碼
//guard 語(yǔ)句
//當(dāng)guard 語(yǔ)句的條件為false時(shí)击奶,就會(huì)執(zhí)行大括號(hào)里面的代碼
//當(dāng)guard 語(yǔ)句為true時(shí)辈双,就會(huì)跳過(guò)guard語(yǔ)句
//guard語(yǔ)句特別適合用來(lái) “提前退出”
func test() {
guard 1>2 else {
//do something
//一定要推出當(dāng)前作用域
return
}
}
//當(dāng)使用guard語(yǔ)句進(jìn)行可選項(xiàng)綁定時(shí),綁定的常量(let),變量(var)也能在外層作用域中使用
//前面登陸的代碼可以用guard來(lái)優(yōu)化下:
func login2(_ info:[String:String]) {
guard let username = info["username"] else {
print("請(qǐng)輸入用戶名")
return
}
guard let password = info["password"] else {
print("請(qǐng)輸入密碼")
return
}
//if username...
//if password...
print("用戶名:\(username)","密碼:\(password)","登陸ing")
}
login2(["username":"jack","password":"123456"]) //用戶名:jack 密碼:123456 登陸ing
login2(["password":"123456"]) //請(qǐng)輸入用戶名
login2(["username":"jack"]) //請(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
let num2: Int = num1
if num1 != nil {
print(num1 + 6)
}
if let num3 = num1 {
print(num3)
}
//盡量不要使用担映!废士,大多數(shù)情況下使用?的可選項(xiàng)
let num4:Int! = nil //加了另萤!也是可選項(xiàng)湃密,只不過(guò)是隱式解包的可選項(xiàng)
//let num5:Int = num4
//這里會(huì)報(bào)錯(cuò):Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
//有一種情況,就是如果你希望別人傳給你的參數(shù)必須是Int值四敞,不能為nil泛源,如果為nil,就崩潰掉忿危,那就可以使用這種類型达箍,這種也是不合理,還是期望使用铺厨?缎玫。
//字符串插值
//可選項(xiàng)在字符串插值或者直接打印時(shí)硬纤,編譯器會(huì)發(fā)出警告
var age7: Int? = 10
print("my age is \(age7)") //my age is Optional(10)
//有三種方法可以消除警告
//1
print("my age is \(age7!)") //my age is 10
//2
print("my age is \(String(describing: age7))") //my age is Optional(10)
//3
print("my age is \(age7 ?? 0)") // my age is 10
//多重可選項(xiàng)
var num11: Int? = 10
var num22: Int?? = num11
var num33: Int?? = 10
print(num22 == num33) //true
image.png
var num111: Int? = nil
var num222: Int?? = num11
var num333: Int?? = nil
print(num222 == num333) //false
print(num111 == num333) //false
print((num222 ?? 1) ?? 2) //2
//這里(num222 ?? 1)強(qiáng)制解包之后是 num111,然后num111 ?? 2赃磨,因?yàn)閚um111 強(qiáng)制解包之后是nil筝家,所以最終輸入2
print((num333 ?? 1) ?? 2) //1
//這里 num333強(qiáng)值解包之后是nil,1 ?? 2邻辉,所以返回1
//可以使用lldb指令 frame variable -R 或者 fr v -R 查看區(qū)別
image.png
var num1: Int? = 10
var num2: Int?? = num1
var num3: Int?? = 10
image.png
var num1: Int? = nil
var num2: Int?? = num1
var num3: Int?? = nil
#這里有個(gè)注意點(diǎn)溪王,如果num = 后面有值才是有意義的,如果是none是沒(méi)有意義的
image.png