Swift系列五 - 可選項


可選項,一般也叫可選類型,它允許將值設為nil

一、定義可選項

平時開發中,如果我們需要把一個變量置空時只需要把變量賦值一個nil即可:

上面嘗試后不行,那怎么把一個變量置空呢?
答案:把變量設置可選類型即可

如何定義可選類型(可選項)?

  • 在類型后面加個問號?
  • 定義可選項后變量默認就是nil
var age: Int?

// 等價
var age: Int? = nil;

案例:數組越界

var array = [1, 15, 20, 30]
func get(_ index: Int) -> Int? {
    if index < 0 || array.count <= index {
        return nil
    }
    return array[index]
}
print(get(4)) // 輸出:nil
print(get(2)) // 輸出:Optional(20)

注意:上面代碼最后一行輸入Optional(20), 為什么會被加上Optional,這樣還能作為一個Int進行運算么?當然不可以,因為被加上Optional后就是可選類型了,如果要使用里面的值,需要進行強制解包。

二、強制解包

可選項是對其他類型的一層包裝,可以將它理解為一個盒子:

  • 如果為nil,那么它就是個空盒子;
  • 如果不為nil,那么盒子里裝的就是被包裝類型的數據
  • 如果要從可選項中取出被包裝的數據(將盒子里裝的東西取出來),需要使用感嘆號(!)進行強制解包;
  • 在取出的可選類型的變量后面加上!即可。
var array = [1, 15, 20, 30]
func get(_ index: Int) -> Int? {
    if index < 0 || array.count <= index {
        return nil
    }
    return array[index]
}
let num1 = get(1)! 
let num2 = get(2)!
let result = num1 + num2
print(result) // 輸出:35

// 等價
let num1 = get(1)
let num2 = get(2)
let result = num1! + num2!

如果對值為nil的可選項(空盒子)進行強制解包,將會產生運行時錯誤

var age: Int?
let num = age!
print(num)

運行結果:

解決辦法:

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

三、可選項綁定

如果包含值就自動解包,把值賦給一個臨時的常量(let)或變量(var),並返回true,否則返回false

// 判斷是否為nil
let number = Int("123kkk")
if number != nil {
    print("轉換成功:\(number!)")
} else {
    print("轉換失敗")
}
/*
 輸出:轉換失敗
 */

// 使用可選項綁定
if let number = Int("123") {
    print("轉換成功:\(number)")
} else {
    print("轉換失敗")
}
/*
 輸出:轉換成功:123
 */

注意:number的作用域僅限后面緊跟的大括號。

當一個變量是可選項時,Xcode會提示:

示例一:

if let first = Int("12") {
    if let second = Int("34") {
        if first < second && second < 100 {
            print("\(first) < \(second) < 100")
        }
    }
}
/*
 輸出:12 < 34 < 100
 */

示例一的等價寫法:

if let first = Int("12"),
   let second = Int("34"),
   first < second && second < 100  {
    print("\(first) < \(second) < 100")
}
/*
 輸出:12 < 34 < 100
 */

注意:可選項綁定在if條件中,只能使用逗號進行隔開。

while循環中使用可選項綁定
場景:遍歷數組,將遇到的整數都加起來,如果遇到負數或者非數字,停止遍歷。

示例:

var strs = ["10", "20", "-20", "ab", "30"]
var index = 0
var sum = 0
while let num = Int(strs[index]), num > 0 {
    sum += num
    index += 1
}
print(sum);

輸出:30

四、空合並運算符??

Swift對空合並運算符的定義:

public func ?? <t>(optional: T?, defaultValue: @autoclosure () throws -&gt; T?) rethrows -&gt; T?
public func ?? <t>(optional: T?, defaultValue: @autoclosure () throws -&gt; T) rethrows -&gt; T

格式: a ?? b

  • a是可選項;
  • b是可選項或者不是可選項;
  • b和a的存儲類型必須相同;
  • 如果a不為nil,就返回a;
    如果a為nil,就返回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

// 等價寫法
let a: Int? = nil
let b: Int = 2
let c: Int
if let tmp = a {
    c = tem
} else {
    c = b
}

通過上面示例可以看到,空合並運算符返回什么類型,取決於運算符后面的類型。

4.1. 多個??一起使用

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

4.2. ??根if let配合使用

let a: Int? = nil
let b: Int? = 2
if let c = a ?? b {
    print(c);
}
// 類似於if a != nil || b != nil

let a: Int? = nil
let b: Int? = 2
if let c = a,
   let d = b {
    print(c);
    print(d);
}
// 類似於if a != nil &amp;&amp; b != nil

五、guard的使用

格式:

guard 條件 else {
    // ToDo
    退出當前作用域
    // return、break、continue、throw error
}

特點:

  • 當條件為false時,執行大括號里面的代碼;當條件為true時,就會跳過guard語句;
  • guard語句必須有退出指令;
  • guard語句適合用來”提前退出“;
  • 當使用guard語句進行可選項綁定時,綁定的常量(let)、變量(var)也能在外層作用域中使用。

簡單登錄案例:

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": "idbeny", "password": "123456"])
login(["password": "123456"])
login(["username": "idbeny"])

/*
 輸出:
 用戶名:idbeny, 密碼:123456, 登陸ing
 請輸入用戶名
 請輸入密碼
 */

使用guard

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": "idbeny", "password": "123456"])
login(["password": "123456"])
login(["username": "idbeny"])

/*
 輸出:
 用戶名:idbeny, 密碼:123456, 登陸ing
 請輸入用戶名
 請輸入密碼
 */

分析:通過上面的ifguard案例可以看出,某些場景下guard更簡潔。

擴展:字典取值如果key存在返回可選類型的value,不存在就返回nil;數組取值如果下標存在返回對應的值(不是可選類型),否則直接報錯(越界)。

六、隱式解包

在某些情況下,可選項一旦被設定值之后,就會一直擁有值。在這種情況下,可以去掉檢查,也不必每次訪問的時候都進行解包,因為他能確定每次訪問的時候都有值。

可以在類型后面加個感嘆號!定義一個隱式解包的可選項。

let num1: Int! = 10
let num2: Int = num1
if num1 != nil {
    print(num1)
}

if let num3 = num1 {
    print(num3)
}

/*
 輸出:
 10
 10
 */

在類型后面加上!也代表是可選類型,同?一樣,只是加上感嘆號后會自動解包,不需要強制解包。

如果num1有值,就會返回10,而不是Optional(10);如果num1為空,就會報錯,因為對空的可選類型進行強制解包是會報錯的。

所以,如果能夠隱式解包的應用場景就是能夠確保可選項一定是有值的,否則就會容易出錯。同時建議少用隱式解包(既然不能非空,直接賦值就可以了,不需要包裝成可選類型)。

七、字符串插值

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

至少有3種方法消除警告(編譯器有給出相關提示):

  • 強制解包
print("age:\(age!)")
// 輸出:age:10
  • 字符串描述(不會解包)
print("age:\(String(describing: age))")
// 輸出:age:Optional(10)
  • 空合並運算符
print("age:\(age ?? 0)")
// 輸出:age:10

八、多重可選項

格式:類型后面多個?
案例一:

var num1: Int? = 10
var num2: Int?? = num1
var num3: Int?? = 10

print(num2 == num3) // true

/*
 num1結構:
 —— Int?
     —— Int 10

 num2結構:
 —— Int??
     —— Int?
         —— Int 10

 num3結構:
 —— Int??
     —— Int?
         —— Int 10

 num2和num3是等效的
 */

案例二:

var num1: Int? = nil
var num2: Int?? = num1
var num3: Int?? = nil

print(num2 == num3) // false

/*
 num1結構:
 —— Int?

 num2結構:
 —— Int??
     —— Int?

 num3結構:
 —— Int??
 */

可以使用lldb指令查看上面案例的區別:frame variable -Rfr v -R

查看案例一:

查看案例二:

如果是none,就代表是一個空盒子,后面的內容就不需要關心了。
如果是some,代表裝有值的盒子。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM