Swift底層原理探索3----可選項

可選項(Optional)

  • 可選項,通常也叫作可選類型,它容許將值設置爲nil
  • 在類型名稱後面加個問號?來定義一個可選項
var name: String = "Jack"
name = nil

var age: Int?
age = 10
age = nil
複製代碼

C語言中,變量聲明以後,會自動被賦予初始值,Swift則不會這樣,可是Swift的Optional則會有默認的初始值nilspring

func test() {
    var age: Int -->沒有初始值,必須賦值以後才能使用
    var weight: Double?  --> 默認初始值爲nil, 等價於  var weight: Double? = nil
}
複製代碼

使用場景,有可能須要使用nil的地方swift

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
複製代碼

強制解包(Forced Unwrapping)

  • 可選項是對其餘類型的一層包裝,能夠將它理解成一個盒子
    1. 若是爲nil,那麼它是個空盒子
    2. 若是不爲nil,那麼盒子裏裝的是:被包裝類型的數據
var age: Int? // 默認就是nil
age = 10
age = nil
複製代碼

image

  • 若是要從可選項中取出被包裝的數據(將盒子裏裝的東西取出來),須要使用感嘆號!進行強制解包
var speed: Int? = 10
var speedInt: Int = speed!
speedInt += 10
複製代碼
  • 若是對值爲nil的可選項(空盒子)進行強制解包,將會產生運行時錯誤
var age: Int?
age!

運行報錯:Fatal error: Unexpectedly found nil while unwrapping an Optional value: file Optional.xcplaygroundpage
複製代碼

判斷可選項是否包含值

let number = Int("123")
if number != nil {
    print("字符串轉換整數成功:\(number!)")
} else {
    print("字符串轉換整數失敗")
}
複製代碼

可選項綁定(Optional Binding)

  • 可使用可選項綁定來判斷選項是否包含值

若是包含就自動解包,把值賦給一個臨時的常量(let)或者變量(var),並返回true,不然返回falseapi

if let number = Int("123") {
    print("字符串轉換整數成功: \(number)")
    -->number是強制解包以後的Int值
    -->number的做用僅限於這個大括號
} else {
    print("字符串轉換整數失敗")
}
複製代碼

用在枚舉值中數組

enum Season: Int {
    case spring = 1, summer, autumn, winter
}
if let season = Season(rawValue: 6) {  -->有可能獲得對應的case, 有可能得不到,那麼結果就爲空
    switch season {
    case .spring:
        print("the season is spring")
    default:
        print("the season is other")
    }
}  else {
       print("no such season")
}
複製代碼

注意下面的等價用法markdown

if let first = Int("4") {
    if let second = Int("42") {
        if first < second && second < 100 {
            print("\(first) < \(second) < 100")
        }
    }
}

======等價寫法以下, 經過逗號來分割 帶可選項綁定的 if判斷條件

if let first = Int("4"),
    let second = Int("42"),
    first < second && second < 100 {
    print("\(first) < \(second) < 100")
}
複製代碼

while循環中使用可選項綁定

---->遍歷數組,將畫遇到的整數都加起來,若是遇到負數或者非數字,中止便利
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
複製代碼

空合併運算符??(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 ?? bapp

  • a必須是可選項
  • b是可選項 或者 不是可選項
  • ba的存儲類型必須相同
  • 若是a不爲nil,就返回a
  • 若是anil,就返回b
  • 若是b不是可選項,返回a時回自動解包
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
複製代碼

若是沒有??,有些代碼寫起來就比較麻煩ui

let a: Int? = nil
let b: Int = 2
let c: Int
if let tmp = a {
    c = tmp
} else {
    c = b
}
複製代碼

多個??一塊兒使用

從左往右算this

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語句實現登陸

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
    }

    print("用戶名:\(username)","密碼:\(password)", "登陸ing")
}

login(["username": "jack", "password": "123456"])
login(["password": "123456"])
login(["username": "jack"])
************運行結果
用戶名:jack 密碼:123456 登陸ing
請輸入用戶名
請輸入密碼
複製代碼

guard語句

上面的登陸業務經過guard來實現spa

func login(_ info: [String : String]) {
    guard let username = info["username"] else {
        print("請輸入用戶名")
        return
    }

    guard let password = info["password"] else {
        print("請輸入密碼")
        return
    }

    print("用戶名:\(username)","密碼:\(password)", "登陸ing")
}

login(["username": "jack", "password": "123456"])
login(["password": "123456"])
login(["username": "jack"])
***********運行結果
用戶名:jack 密碼:123456 登陸ing
請輸入用戶名
請輸入密碼
複製代碼

隱式解包(Implicitly Unwrapped Optional)

  • 在某些狀況下,可選項一旦被設定值以後,就會一直擁有值
  • 在這種狀況下,能夠去掉檢查,也沒必要每次訪問的時候都進行解包,由於它能肯定每次訪問的時候都有值
  • 能夠在類型後面加個感嘆號!定義一個隱式解包的可選項
let num1: Int! = 10 //若是能肯定某個變量會一直不爲nil,可使用這種聲明方式,
let num2: Int = num1 // 能夠直接使用,系統會自動解包後賦值,固然也可使用強制解包方式,可是num1的本質仍是一個optional
if num1 != nil {
    print(num1 + 6)
}

if let num3 = num1 {
    print(num3)
}


複製代碼

若是給隱式解包類型的optional變量賦值nil,當該變量被賦值給其餘變量/常量式,會出現 運行時報錯debug

let num4: Int! = nil
let num5: Int = num4  -->注意,程序執行完這一句纔會報錯,在執行這一句的時候,會對num4進行隱式解包,結果發現它內部是nil,所以把nil取出來賦值給  num5: Int 的時候就報錯了,由於 num5 不能爲空
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
複製代碼

上述的案例,讓人感受使用帶!的optional存在不少風險,其實用下面的方式莫不是更保險點嗎,

var num5: Int = 10
let num5: int = num56
複製代碼

這樣至少在程序運行以前,就能看到錯誤,若是有的話。

實際上,咱們大部分場景下,也都是推薦使用帶?optional,那用!聲明的optional存在的意義是什麼呢?

  • 若是你提供一套api給外界使用,而且指望使用者嚴格遵照你的要求不要傳nil過來,而且認爲使用者在錯誤使用的時候而致使程序直接報錯崩潰就是你期待的,那麼你可使用這種用法。除此以外,仍是不用爲妙。

字符串插值

可選項在字符串插值或者直接打印的時候,編譯器會發出警告

var age: Int? = 10
print("My age is \(age)")//直接進行字符串插值,會產生編譯器警告
//️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)")
複製代碼

多重可選項

var num1: Int? = 10
var num2: Int?? = num1
var num3: Int?? = 10
print(num2 == num3) // true
複製代碼

num1num2num3的結構分別以下 image

再看以下

var num1: Int? = nil
var num2: Int?? = num1
var num3: Int?? = nil
print(num2 == num3) // false

(num2 ?? 1) ?? 2  //1
(num1 ?? 1) ?? 2  //2
複製代碼

image

可使用LLDB指令 frame variable -R 或者 fr v -R來查看變量的內部信息。

有關Swift的可選項就整理到這裏。

相關文章
相關標籤/搜索