Optionals是Swift的核心,而且在第一個版本中就已經存在;optional修飾的值容許咱們在關注可能爲nil值的時候書寫整潔的代碼。git
若是你剛開始接觸Swift,那麼你可能須要熟悉在屬性中添加?的語法;只要你熟悉了這個語法你就能夠從中受益,好比extensions。github
在咱們深刻了解optionals以前須要瞭解基本知識。編程
屬性、方法、下標可以返回一個可選值,這個值可能不爲nil可能爲nil。多個引用能夠連接在一塊兒,這稱爲可選連接。這是強制解包的另外一種方式,稍後將對其進行更詳細的講解。swift
下面的示例代碼展現了自定義可選的String而且print字符數的可選連接。app
let name: String? = "Antoine van der Lee"
print(name?.count ?? 0)
複製代碼
強制解包可選值可能返回一個存在的值或者是一個致使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)
複製代碼
使用可選值最好的方式在默認的狀況下最好不要使用!;一些人甚至建議啓用 force unwrapping SwiftLint rule;這將防止你引用更多不指望的閃退。
可是有時候若是值爲nil的時候,在編程錯誤時使用強制解包也很好;所以在早期的時候你能夠經過調試強制解包幫你本身發現一個bug。
很高興知道可選整體來講是枚舉中的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: ""
複製代碼
當你在寫測試的時候,有一個很好的方法能夠在不強制解包的狀況下使用可選值;若是要使用強制解包,你可能會致使致命錯誤,從而阻斷全部測試的成功。
若是可選值不包含值你可使用XCTUnwrap,它將會拋出異常:
func testBlogPostTitle() throws {
let blogPost: BlogPost? = fetchSampleBlogPost()
let unwrappedTitle = try XCTUnwrap(blogPost?.title, "Title should be set")
XCTAssertEqual(unwrappedTitle, "Learning everything about optionals")
}
複製代碼
若是你有過編寫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)
複製代碼
雖然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…