首頁 > 軟體

Swift中的可選項Optional解包方式實現原理

2023-03-27 06:00:59

為什麼需要Optional

Swift中引入了可選項(Optional)的概念是為了解決在程式碼中對於某些變數或常數可能為nil的情況進行處理,從而減少了程式中的不確定性,使得程式更加穩定和安全。

什麼是Optional

在Swift中,可選項的型別是使用?來表示的,例如String?即為一個可選的字串型別,表示這個變數或常數可能為nil。而對於不可選項,則直接使用相應型別的名稱,例如String表示一個非可選的字串型別。

var str: String = nil
var str1: String? = nil 
複製程式碼

Optional實現原理

Optional實際上是Swift語言中的一種列舉型別。在Swift中宣告Optional型別時,編譯器會自動將其轉換成對應的列舉型別,例如:

var optionalValue: Int? = 10
// 等價於:
enum Optional<Int> {
    case none
    case some(Int)
}
var optionalValue: Optional<Int> = .some(10)
複製程式碼

在上面的程式碼中,我們宣告了一個Optional型別的變數optionalValue,並將其初始化為10。實際上,編譯器會自動將其轉換為對應的列舉型別,即Optional列舉型別的.some(Int),其中的Int就是我們所宣告的可選型別的關聯值。

當我們在使用Optional型別的變數時,可以通過判斷其列舉值是.none還是.some來確定它是否為nil。如果是.none,表示該Optional值為空;如果是.some,就可以通過存取其關聯值獲取具體的數值。

Optional的原始碼實現為:

@frozen public enum Optional<Wrapped> : ExpressibleByNilLiteral {
	case none
	case some(Wrapped)															 
}
複製程式碼
  • Optioanl其實是標準庫裡的一個enum型別
  • 用標準庫實現語言特性的典型
  • Optional.none 就是nil
  • Optional.some 就是包裝了實際的值
  • 泛型屬性 unsafelyUnwrapped
  • 理論上我們可以直接呼叫unsafelyUnwrapped獲取可選項的值

Optional的解包方式

1. 可選項繫結(Optional Binding)

使用 if let 或者 guard let 語句來判斷 Optional 變數是否有值,如果有值則解包,並將其賦值給一個非可選型別的變數。

var optionalValue: Int? = 10
// 可選項繫結
if let value = optionalValue {
    print("Optional value is (value)")
} else {
    print("Optional value is nil")
}
複製程式碼

可選項繫結語句有兩個分支:if分支和else分支。如果 optionalValue 有值,if 分支就會被執行,unwrappedValue 就會被賦值為 optionalValue 的值。否則,執行 else 分支。

2. 強制解包(Forced Unwrapping)

使用!來獲取一個不存在的可選值會導致執行錯誤,在使用!強制展開之前必須保證可選項中包含一個非nil的值

var optionalValue: Int? = 10
let nonOptionalValue = optionalValue!  // 解包optionalValue值
print(nonOptionalValue)                // 輸出:10
複製程式碼

需要注意的是,如果 Optional 型別的值為 nil,使用強制解包方式解包時,會導致執行時錯誤 (Runtime Error)。

3. 隱式解包(Implicitly Unwrapped Optionals)

在定義 Optional 型別變數時使用 ! 操作符,標明該變數可以被隱式解包。用於在一些情況下,我們可以確定該 Optional 變數繫結後不會為 nil,可以快捷的解包而不用每次都使用 ! 或者 if let 進行解包。

var optionalValue: Int! = 10
let nonOptionalValue = optionalValue // 隱式解包
print(nonOptionalValue) // 輸出:10
複製程式碼

需要注意的是,隱式解包的 Optional 如果 nil 的話,會導致 runtime error,所以使用隱式解包 Optional 需要確保其一直有值,否則還是需要檢查其非 nil 後再操作。

總的來說,我們應該儘量避免使用強制解包,而是通過可選項繫結來處理 Optional 型別的值,在需要使用隱式解包的情況下,也要確保其可靠性和穩定性,儘量減少出現執行時錯誤的概率。

可選鏈(Optional Chaining)

是一種在 Optional 型別值上進行操作的方式,可以將多個 Optional 值的處理放在一起,並在任何一個 Optional 值為 nil 的時刻停止處理。

通過在 Optional 型別值後面跟上問號 ?,我們就可以使用可選鏈來存取該 Optional 物件的屬性和方法。

class Person {
    var name: String
    var father: Person?
    init(name: String, father: Person?) {
        self.name = name
        self.father = father
    }
}
let father = Person(name: "Father", father: nil)
let son = Person(name: "Son", father: father)

// 可選鏈呼叫屬性
if let fatherName = son.father?.name {
    print("Father's name is (fatherName)") // 輸出:Father's name is Father
} else {
    print("Son without father")
}

// 可選鏈呼叫方法
if let count = son.father?.name.count {
    print("Father's name has (count) characters") // 輸出:Father's name has 6 characters
} else {
    print("Son without father")
}
複製程式碼

在上面的程式碼中,我們定義了一個 Person 類,並初始化了一個包含父親(father)的兒子(son)物件。其中,父親物件的father屬性為nil。我們使用問號 ? 來標記 father 物件為 Optional 型別,以避免存取 nil 物件時的執行時錯誤。

需要注意的是,如果一個 Optional 型別的屬性通過可選鏈呼叫後,返回值不是 Optional 型別,那麼在可選鏈呼叫後,就不再需要加問號 ? 標記其為 Optional 型別了。

class Person {
    var name: String
    var age: Int?
    init(name: String, age: Int?) {
        self.name = name
        self.age = age
    }
    func printInfo() {
        print("(name), (age ?? 0) years old")
    }
}
let person = Person(name: "Tom", age: nil)

// 可選鏈呼叫方法後,返回值不再是 Optional 型別
let succeed = person.printInfo() // 輸出:Tom, 0 years old
複製程式碼

在上面的程式碼中,我們定義了一個 Person 類,並初始化了一個包含年齡(age)的人(person)物件。在可選鏈呼叫物件的方法——printInfo() 方法後,因為該方法返回值不是 Optional 型別,所以 returnedValue 就不再需要加問號 ? 標記其為 Optional 型別了。

Optional 的巢狀

將一個 Optional 型別的值作為另一個 Optional 型別的值的成員,形成巢狀的 Optional 型別。

var optionalValue: Int? = 10
var nestedOptionalValue: Int?? = optionalValue
複製程式碼

在上面的程式碼中,我們定義了一個 Optional 型別的變數 optionalValue,並將其賦值為整型變數 10。然後,我們將 optionalValue 賦值給了另一個 Optional 型別的變數 nestedOptionalValue,形成了一個巢狀的 Optional 型別。

在處理巢狀的 Optional 型別時,我們需要特別小心,因為它們的使用很容易造成邏輯上的混淆和錯誤。為了解決這個問題,我們可以使用 Optional Binding 或者 ?? 操作符(空合併運運算元)來降低 Optional 巢狀的複雜度。

var optionalValue: Int? = 10
var nestedOptionalValue: Int?? = optionalValue

// 雙重可選項繫結
if let nestedValue = nestedOptionalValue, let value = nestedValue {
    print(value) // 輸出:10
} else {
    print("Optional is nil")
}
// 空合併運運算元
let nonOptionalValue = nestedOptionalValue ?? 0
print(nonOptionalValue) // 輸出:Optional(10)
複製程式碼

在上面的程式碼中,我們使用了雙重可選項繫結來判斷 nestedOptionalValue 是否可繫結,以及其巢狀的 Optional 值是否可繫結,並將該值賦值給變數 value,以避免 Optional 值的巢狀。另外,我們還可以使用 ?? 操作符(空合併運運算元)來對巢狀的 Optional 值進行預設取值的操作。

需要注意的是,雖然我們可以使用 ?? 操作符來降低 Optional 值的巢狀,但在具體的實際應用中,我們應該在設計時儘量避免 Optional 值的巢狀,以便程式碼的可讀性和維護性。如果對於某個變數來說,它的值可能為空,我們可以考慮使用預設值或者定義一個預設值的 Optional 值來代替巢狀的 Optional 型別。

學習 Swift,勿忘初心,方得始終。但要陷入困境時,也不要忘了最初的夢想和時代所需要的技能。

以上就是Swift中的可選項Optional解包方式實現原理的詳細內容,更多關於Swift可選項Optional的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com