為了表示我對簡書『飽醉豚』事件的不滿,簡書不再更新,后續(xù)有文章只更新 個人博客和 掘金
本文首發(fā)于我的個人博客
定義
- 可選項,一般也叫可選類型,它允許將值設置為nil
- 在類型名稱后面加個問號? 來定義一個可選項
var name: String? = "Jack"
name = nil
var age: Int? // 默認就是nil
age = 10
age = nil
強制解包
- 可選項是對其他類型的一層包裝,可以將它理解為一個盒子
- 如果為nil,那么它是個空盒子
- 如果不為nil,那么盒子里裝的是:被包裝類型的數(shù)據(jù)
var age: Int? // 默認就是nil
age = 10
age = nil
- 如果要從可選項中取出被包裝的數(shù)據(jù)(將盒子里裝的東西取出來),需要使用感嘆號! 進行強制解包
var age: Int? = 10
let ageInt: Int = age!
ageInt += 10
- 如果對值為nil的可選項(空盒子)進行強制解包,將會產(chǎn)生運行時錯誤
var age: Int?
age!// 報錯:Fatal error: Unexpectedly found nil while unwrapping an Optional value
判斷可選項是否包含值
let number = Int("123")
if number != nil {
print("字符串轉換整數(shù)成功:\(number!)")
} else {
print("字符串轉換整數(shù)失敗")
}
// 字符串轉換整數(shù)成功:123
可選項綁定
- 可以使用可選項綁定來判斷可選項是否包含值
- 如果包含就自動解包,把值賦給一個臨時的常量(let)或者變量(var),并返回true,否則返回false
if let number = Int("123") {
print("字符串轉換整數(shù)成功:\(number)")
// number是強制解包之后的Int值
// number作用域僅限于這個大括號
} else {
print("字符串轉換整數(shù)失敗")
}
// 字符串轉換整數(shù)成功:123
eg:
enum Season : Int {
case spring = 1, summer, autumn, winter
}
if let season = Season(rawValue: 6) {
switch season {
case .spring:
print("the season is spring")
default:
print("the season is other")
}
} else {
print("no such season")
}
// no such season
等價寫法
可選項綁定中,如果多個條件比如下面
if let first = Int("4") {
if let second = Int("42") {
if first < second && second < 100 {
print("\(first) < \(second) < 100")
}
}
}
// 4 < 42 < 100
可以用 , 分割開,看起來更簡單
if let first = Int("4"),
let second = Int("42"),
first < second && second < 100 {
print("\(second) < \(second) < 100")
}
// 4 < 42 < 100
while循環(huán)中使用可選項綁定
- 有如下需求
//遍歷數(shù)組,將遇到的正數(shù)都加起來,如果遇到負數(shù)或者非數(shù)字,停止遍歷
//遍歷數(shù)組,將遇到的正數(shù)都加起來,如果遇到負數(shù)或者非數(shù)字,停止遍歷
// var strs = ["10", "20", "abc", "-20", "30"]
var index = 0
var sum = 0
while let num = Int(strs[index]), num > 0 {
sum += num
index += 1
}
print(sum)
空合并運算符 ??(Nil-Coalescing Operator)
eg:
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 是可選項
- b 是可選項 或者 不是可選項
- b 跟 a 的存儲類型必須相同
- 如果 a 不為nil,就返回 a
- 如果 a 為nil,就返回 b
- 如果 b 不是可選項,返回 a 時會自動解包
規(guī)律: 返回的類型取決于b
舉例如下:
let a: Int? = 1
let b: Int? = 2
let c = a ?? b // c是Int? , Optional(1)
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
let a: Int? = nil
let b: Int? = 2
let c = a ?? b // c是Int? , Optional(2)
let a: Int? = nil
let b: Int = 2
// 如果不使用??運算符
let c: Int
if let tmp = a {
c = tmp
} else {
c=b
}
// 使用 ?? 運算符
let c = a ?? b // c是Int? , nil
多個 ?? 一起使用
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 , 2
let a: Int? = nil
let b: Int? = nil
let c = a ?? b ?? 3 // c是Int , 3
??跟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
guard語句
- 當guard語句的條件為false時,就會執(zhí)行大括號里面的代碼
- 當guard語句的條件為true時,就會跳過guard語句
- guard語句特別適合用來“提前退出”
guard 條件 else {
// do something....
退出當前作用域
// return、break、continue、throw error }
- 當使用guard語句進行可選項綁定時,綁定的常量(let)、變量(var)也能在外層作用域中使用
假設我們有個登陸的需求,要求輸入賬號,密碼。缺一不可。
用if語句書寫
//if語句實現(xiàn)登陸
func login(_ info: [String : String]) {
let username: String
if let tmp = info["username"] {
username = tmp
} else {
print("請輸入用戶名")
return
}
let password: String
if let tmp = info["password"] {
password = tmp
} else {
print("請輸入密碼")
return
}
// 能來到這里,說明,username和password都是有值的
// if username ....
// if password ....
print("用戶名:\(username)", "密碼:\(password)", "登陸ing")
}
// 調(diào)用
login(["username" : "jack", "password" : "123456"]) // 用戶名:jack 密碼:123456 登陸ing
login(["password" : "123456"]) // 請輸入密碼
login(["username" : "jack"]) // 請輸入用戶名
如果用guard來書寫
func login(_ info: [String : String]) {
guard let username = info["username"] else {
print("請輸入用戶名")
return
}
guard let password = info["password"] else {
print("請輸入密碼")
return
}
// if username ....
// if password ....
print("用戶名:\(username)", "密碼:\(password)", "登陸ing")
}
隱式解包(Implicitly Unwrapped Optional)
- 在某些情況下,可選項一旦被設定值之后,就會一直擁有值
- 在這種情況下,可以去掉檢查,也不必每次訪問的時候都進行解包,因為它能確定每次訪問的時候都有值
- 可以在類型后面加個感嘆號 ! 定義一個隱式解包的可選項
let num1: Int! = 10
let num2: Int = num1
if num1 != nil {
print(num1 + 6) // 16
}
if let num3 = num1 {
print(num3) //10
}
注意不能設置為nil
let num1: Int! = nil
// Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
let num2: Int = num1
盡量不要使用這個強制解包。
除非你設計接口,不希望接收空值,如果別人傳Nil過來,直接報錯
字符串插值
- 可選項在字符串插值或者直接打印時,編譯器會發(fā)出警告
var age: Int? = 10
print("My age is \(age)")
- 至少有3種方法消除警告
print("My age is \(age!)")
// My age is 10
print("My age is \(String(describing: age))")
// My age is Optional(10)
print("My age is \(age ?? 0)")
// My age is 10
多重可選項
- 可以使用lldb指令 frame variable –R 或者 fr v –R 查看區(qū)別
var num1: Int? = 10
var num2: Int?? = num1
var num3: Int?? = 10
print(num2 == num3) // true
還有下面這種
var num1: Int? = nil
var num2: Int?? = num1
var num3: Int?? = nil
print(num2 == num3) // false
(num2 ?? 1) ?? 2 // 2
(num3 ?? 1) ?? 2 // 1
參考資料: