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/