在Swift開發中關於Optionals你須要知道的5件事(譯)

Optionals是Swift的核心,而且在第一個版本中就已經存在;optional修飾的值容許咱們在關注可能爲nil值的時候書寫整潔的代碼。git

若是你剛開始接觸Swift,那麼你可能須要熟悉在屬性中添加?的語法;只要你熟悉了這個語法你就能夠從中受益,好比extensions。github

在Swift中什麼是可選值?

在咱們深刻了解optionals以前須要瞭解基本知識。編程

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

下面的示例代碼展現了自定義可選的String而且print字符數的可選連接。app

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

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

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

可是在咱們深刻了解強制解包以前,讓咱們先來看看在沒有強制的狀況下解包可選值有哪些可能性。post

如何解包一個可選值

在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:fetch

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

使用!強制解包可選值

能夠在可選值後面使用!進行強制解包。

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

當上述示例中的name變量設置爲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的時候,在編程錯誤時使用強制解包也很好;所以在早期的時候你能夠經過調試強制解包幫你本身發現一個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"
複製代碼

看看它的文檔你會發現一個可選值附帶了一些很是方便的方法;一個不錯的示例就是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,查看個人博客:CompactMap vs flatMap: The differences explained

擴展中的可選

你如今知道一個可選被定義爲一個枚舉,你能猜到你能夠爲它寫擴展

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

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.可選的協議方法

若是你有過編寫Objective-C的經驗,你可能會錯過可選的協議方法。儘管用Swift編寫可選的協議方法有更好的方式,可是標準庫中最多見的方式以下:

@objc protocol UITableViewDataSource : NSObjectProtocol {

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

    // ...
}
複製代碼

下面容許你使用?調用方法:

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

你能夠看到它基本上只須要使用一個額外的!或者是?。

總結

當你在Swift中使用optionals的時候咱們涵蓋了不少你須要知道的知識;解包可選值從最基本的使用感嘆號!!到擴展可選枚舉的高級實現。

本文翻譯自原文:www.avanderlee.com/swift/optio…

相關文章
相關標籤/搜索