在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…

相关文章
相关标签/搜索