在Swift開發(fā)中關(guān)于Optionals你需要知道的5件事(譯)

Optionals是Swift的核心,并且在第一個版本中就已經(jīng)存在;optional修飾的值允許我們在關(guān)注可能為nil值的時候書寫整潔的代碼。

如果你剛開始接觸Swift,那么你可能需要熟悉在屬性中添加?的語法;只要你熟悉了這個語法你就可以從中受益,比如extensions。

在Swift中什么是可選值?

在我們深入了解optionals之前需要了解基本知識。

屬性、方法、下標能夠返回一個可選值,這個值可能不為nil可能為nil。多個引用可以鏈接在一起,這稱為可選鏈接。這是強制解包的另一種方式,稍后將對其進行更詳細的講解。

下面的示例代碼展示了自定義可選的String并且print字符數(shù)的可選鏈接。

let name: String? = "Antoine van der Lee"
print(name?.count ?? 0)

1. 在Swift中強制解包可選值

強制解包可選值可能返回一個存在的值或者是一個導致runtime運行時錯誤的空值。

但是在我們深入了解強制解包之前,讓我們先來看看在沒有強制的情況下解包可選值有哪些可能性。

如何解包一個可選值

在Swift中有很多方法可以解包可選值。你可以使用guard聲明:

let name: String? = "Antoine van der Lee"
guard let unwrappedName = name else {
    return
}
print(unwrappedName.count)

或者可以使用if let聲明:

let name: String? = "Antoine van der Lee"
if let unwrappedName = name {
    print(unwrappedName.count)
}

或者你可以使用??運算符,也稱為nil合并運算符。如果它存在,這種將會返回一個可選值或者比如下面自定義的默認值0:

let name: String? = "Antoine van der Lee"
print(name?.count ?? 0)

使用!強制解包可選值

可以在可選值后面使用!進行強制解包。

var name: String? = "Antoine van der Lee"
print(name!.count)

當上述示例中的name變量設(shè)置為nil的時候它將導致致命的運行時錯誤,比如下面這種錯誤:

Fatal error: Unexpectedly found nil while unwrapping an Optional value

鏈接解包

可以用下面的方式進行可選鏈:

struct BlogPost {
    let title: String?
}

let post: BlogPost? = BlogPost(title: "Learning everything about optionals")
print(post?.title?.count ?? 0)

同時對counts使用強制解包:

let post: BlogPost? = BlogPost(title: "Learning everything about optionals")
print(post!.title!.count)

但是要注意如果你解包最后一個可選值,那么你仍然將得到一個可選值;在下面的示例中,我們僅僅解包title而不是post;這就意味著如果post為nil的時候,那么我們就不能獲取到一個title值:

let post: BlogPost? = BlogPost(title: "Learning everything about optionals")
print(post?.title!.count) // Prints: Optional(35)

強制解包捕獲erros是可選值最好的方式

使用可選值最好的方式在默認的情況下最好不要使用??;一些人甚至建議啟用 force unwrapping SwiftLint rule;這將防止你引用更多不期望的閃退。

但是有時候如果值為nil的時候,在編程錯誤時使用強制解包也很好;因此在早期的時候你可以通過調(diào)試強制解包幫你自己發(fā)現(xiàn)一個bug。

2.可選是枚舉中的2種情況

很高興知道可選總體來說是枚舉中的2種情況:

enum Optional<Wrapped> {
    /// The absence of a value.
    case none

    /// The presence of a value, stored as `Wrapped`.
    case some(Wrapped)
}

但是如果不使用.none,你將使用nil來表示缺失值。

考慮到這一點,你還可以使用枚舉將上述name變量定義為可選的:

let name = Optional.some("Antoine van der Lee")
print(name!.count)

或者你可以像切換普通枚舉一樣切換可選值:

func printName(_ name: String?) {
    switch name {
    case .some(let unwrappedValue):
        print("Name is \(unwrappedValue)")
    case .none:
        print("Name is nil")
    }
}

printName(nil) // Prints: "Name is nil"
printName("Antoine van der Lee") // Prints: "Name is Antoine van der Lee"

看看它的文檔你會發(fā)現(xiàn)一個可選值附帶了一些非常方便的方法;一個不錯的示例就是map方法:

let sideLength: Int? = Int("20")
let possibleSquare = sideLength.map { $0 * $0 }
print(possibleSquare) // Prints: "Optional(400)"

或者flatMap方法,在本例中該方法僅在得到至少5個字符的驗證時才返回name:

var name: String? = "Antoine van der Lee"
let validName = name.flatMap { name -> String? in
    guard name.count > 5 else { return nil }
    return name
}
print(validName) // Prints: "Optional("Antoine van der Lee")"

如果你想了解更多map、flatMap以及compactMap,查看我的博客:<u>CompactMap vs flatMap: The differences explained</u>

擴展中的可選

你現(xiàn)在知道一個可選被定義為一個枚舉,你能猜到你可以為它寫擴展

最常見的示例是擴展可選字符串并處理空值:

extension Optional where Wrapped == String {
    var orEmpty: String {
        return self ?? ""
    }
}

var name: String? = "Antoine van der Lee"
print(name.orEmpty) // Prints: "Antoine van der Lee"
name = nil
print(name.orEmpty) // Prints: ""

3.為可選值編寫單元測試

當你在寫測試的時候,有一個很好的方法可以在不強制解包的情況下使用可選值;如果要使用強制解包,你可能會導致致命錯誤,從而阻斷所有測試的成功。

如果可選值不包含值你可以使用XCTUnwrap,它將會拋出異常:

func testBlogPostTitle() throws {
    let blogPost: BlogPost? = fetchSampleBlogPost()
    let unwrappedTitle = try XCTUnwrap(blogPost?.title, "Title should be set")
    XCTAssertEqual(unwrappedTitle, "Learning everything about optionals")
}

4.可選的協(xié)議方法

如果你有過編寫Objective-C的經(jīng)驗,你可能會錯過可選的協(xié)議方法。盡管用Swift編寫可選的協(xié)議方法有更好的方式,但是標準庫中最常見的方式如下:

@objc protocol UITableViewDataSource : NSObjectProtocol {

    @objc optional func numberOfSections(in tableView: UITableView) -> Int

    // ...
}

下面允許你使用?調(diào)用方法:

let tableView = UITableView()
let numberOfSections = tableView.dataSource?.numberOfSections?(in: tableView)

5.嵌套可選是一個重要的功能

雖然SE-0230 – Flatten nested optionals resulting from ‘try?’是刪除嵌套可選的最常見原因之一,但它仍然是一個重要的功能!

var name: String?? = "Antoine van der Lee"
print(name!!.count)

你解包了一個可選值仍然會返回一個可選值,以前在早期的Swift版本中使用try?運算符就是這種情況。

一個常見的示例是當你使用包含可選值字典的時候:

let nameAndAges: [String:Int?] = ["Antoine van der Lee": 28]
let antoinesAge = nameAndAges["Antoine van der Lee"]
print(antoinesAge) // Prints: "Optional(Optional(28))"
print(antoinesAge!) // Prints: "Optional(28)"
print(antoinesAge!!) // Prints: "28"

你可以看到它基本上只需要使用一個額外的!或者是?。

總結(jié)

當你在Swift中使用optionals的時候我們涵蓋了很多你需要知道的知識;解包可選值從最基本的使用感嘆號!!到擴展可選枚舉的高級實現(xiàn)。

本文翻譯自原文:https://www.avanderlee.com/swift/optionals-in-swift-explained-5-things-you-should-know/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容