[翻译]理解Swift中的Optional

原文出处:Understanding Optionals in Swifthtml

苹果新的Swift编程语言带来了一些新的技巧,能使软件开发比以往更方便、更安全。然而,一个颇有力的特性Optional,在你第一次使用时可能会感到困惑。Optionals将会在编译阶段检查哪些值为nil。经过这种方式,你能够更好的保证应用程序交付在用户手里是可运行的。在Swift中,Optionals也提供了一些接口用来和遗留的Objective-C代码之间交互。react

初试Optional编程

让咱们在XCode中新建一个叫作swift-optionals的playground文件。你能够添加下面的代码来看看Optionals是什么样的。swift

import Foundation

var rightURL = NSURL(string: "http://www.reactive.io") // => {Some http://www.reactive.io}
var wrongURL = NSURL(string: "this is not a real url") // => nil

在这种场景下,咱们试着经过字符串来建立NSURL。对于rightURL,咱们获得了一个Some的结果,里面存放着一个NSURL对象,对于wrongURL,咱们将获得nil。在两种状况下都没有直接获得NSURL,这是一件好事,由于Some结果须要显示的解包,这将迫使咱们检查nil值,让咱们看看这是如何工做的。安全

一切都是Someapp

若是咱们在XCode中看下NSURL的构造函数,咱们将会看到以下的代码:编程语言

convenience init?(string URLString: String)

那个在init以后的问号标记告诉咱们当构造函数执行完毕,NSURL将会返回一个Optional。在Swift中,Optional是一个实际的类型,更加明确的说是一个泛型的enum,这种类型或者持有另外一个对象,或者是nil。让咱们看看Optional长什么样?函数

enum Optional<T> : Reflectable, NilLiteralConvertible {
    case None
    case Some(T)

    /// Construct a `nil` instance.
    init()

    /// Construct a non-\ `nil` instance that stores `some`.
    init(_ some: T)
...

咱们可以看到Optional枚举包括两个case,None表明nil,Some表明具体的泛型类型。在Swift中,咱们能够把任何东西装箱在Optional.Some中。工具

Optional<String>.Some("really good stuff") // => {Some "really good stuff"}

另外一方面,None等价于nilthis

Optional<String>.None == nil // => true

从这个角度来说,你能够把Optional理解为一个箱子,有可能会包含一个具体的类型,也有可能没有包含。向一个方法或者函数发送一个箱子类型而不是具体类型,编译器将会强迫你打开箱子检查箱子里面的类型。若是箱子是空的,你能够捕捉这个错误而且处理这种错误。

Swift将会推断出变量的类型,不过为了让事情变得更透明,咱们显示的以两种方式来使用Optional:

import Foundation

var rightURL: Optional<NSURL> = NSURL(string: "http://www.reactive.io") // => {Some http://www.reactive.io}
var wrongURL: NSURL? = NSURL(string: "this is not a real url") // => nil

对于rightURL,咱们经过一种冗长的方式来建立一个Optional类型,由于咱们会大量使用Optional,Swift给了咱们一种简写方式,经过在变量后面追加一个问号‘?',就如同咱们以前看到的NSURL构造器那样。可是若是咱们在NSURL变量后面去掉问号会怎么样?

import Foundation

var cheatURL: NSURL = NSURL(string: "http://www.reactive.io")

咱们将会获得一个编译错误”Value of optional type ‘NSURL?’not unwrapped; did you mean to use ‘!’or ‘?’”。咱们已经知道'?'是干吗的了,那么感叹号标记'!'是干吗的呢?

隐式和显示Optional

在一个类型后面使用'?'用来显示代表这是一个Optional,让咱们使用'!'看看会发生什么:

import Foundation

var implicitURL: NSURL! = NSURL(string: "http://www.reactive.io/tips") // => http://www.reactive.io/tips

此次咱们获得了一个没有被Some包装的NSURL。使用'!'使得implicitURL看起来跟rightURL这个optional差很少,用'!'标记的类型实为ImplicitlyUnwrappedOptional,当你使用值的时候Swift编译器将会自动为你展开里面的值。使用ImplicitlyUnwrappedOptional类型会带来危险,由于编译器不会迫使咱们处理值为nil的状况。可是初始化为nil会帮咱们跟遗留的Objective-C代码之间搭起桥梁。

下面是4中不一样的Optional使用方式:

import Foundation

var rightURL: Optional<NSURL> = NSURL(string: "http://www.reactive.io") // => {Some http://www.reactive.io}
var wrongURL: NSURL? = NSURL(string: "this is not a real url") // => nil

var implicitURL: NSURL! = NSURL(string: "http://www.reactive.io") // => http://www.reactive.io/tips
var explicitURL: ImplicitlyUnwrappedOptional<NSURL> = NSURL(string: "this is another bad url") // => nil

如何使用Optional

第一种方法是显示检查Optional是不是nil:

if rightURL != nil {
    println("got a URL: \(rightURL)") // => "got a URL: Optional(http://www.reactive.io)"
}
else {
    println("no URL, sorry :(")
}

在上面获得了Optional(http://www.reactive.io),这并非咱们想要的,咱们想获得的是Optional里面包含的内容。如何作到呢?靠'!'符号,在变量或常量后面追加'!'符号将会展开Optional里面的值,若是是nil值将会抛出异常,让咱们试试:

if rightURL != nil {
    println("got a URL: \(rightURL!)") // => "got a URL: http://www.reactive.io"
}
else {
    println("no URL, sorry :(")
}

咱们能够经过另外一种方式if let块来实现:

if let url = rightURL {
    println("got a URL: \(url)") // => "got a URL: http://www.reactive.io"
}
else {
    println("no URL, sorry :(")
}

你也能够经过switch语句来实现:

switch rightURL {
case nil:
    println("no URL, sorry :(")
default:
    println("got a URL: \(rightURL!)")
}

你还可使用??操做符进行链式调用获得不为nil的值:

var myURL = wrongURL ?? explicitURL ?? rightURL // {Some http://www.reactive.io}
println("got a URL: \(myURL!)") // => "got a URL: http://www.reactive.io"

编写一个类

是时候来看看如何在面向对象的代码中使用Optional类型了,复制下面的代码到playground中:

import Foundation

class Person {
    var name: String
    var address: String

    init(name: String, address: String) {
        self.name = name
        self.address = address
    }

    func description() -> String {
        return "\(name) @ \(address)"
    }
}

class Box {
    var contents: String
    var sender: Person!
    var recipient: Person?

    init(contents: String) {
        self.contents = contents
    }
}

var alice = Person(name: "Alice", address: "New York City")
var book  = Box(contents: "A Good Book")

book.sender = alice // => {name "Alice" address "New York City"}

注意在Box类中,sender属性是一个ImplicitlyUnwrappedOptional类型,recipient属性是Optional类型。不过如果将这两个类型换为普通的Person类型,Swift编译器将会报出一个错误。由于这两个属性并无在构造函数中赋值,因此这两个属性在构造函数调用的时候没有被初始化。在上面的例子中,book被初始化后,sender和recipient都默认为nil,可是咱们肯定book必定有一个sender,因此sender为ImplicitlyUnwrappedOptional类型,在例子中,sender为alice.可是不必定有recipient,因此别人在使用book对象的时候须要检查recipient是否有值。

方法调用

若是咱们想要获得sender或者recipient的description,咱们也许会获得一些麻烦。这是由于咱们不能在nil值上调用description方法,另外使用!强制展开nil值Optional还会抛出异常。使用if else条件表达式调用方法会显得很繁琐。Swift提供了另外一个工具,经过使用?操做符来进行链式调用。在调用方法的时候先检查值是不是nil:

book.sender?.description() // => {Some "Alice @ New York City"}
book.recipient?.description() // => nil

咱们使用了一种真确的方法调用了description方法,更进一步咱们获得了一个Optional类型:

book.recipient = Person(name: "Bob", address: "San Francisco")

if let note = book.recipient?.description() {
    println("Hey \(note), enjoy the Book!") // => "Hey Bob @ San Francisco, enjoy the Book!"
}

总结

本文说了一些关于Swift中Optionals的事情,这将帮助你在写代码的时候更好的用上它,而且在使用类库的时候使用它们。熟练的使用不一样方式的Optional将会使你保证你写代码更迅速,减小运行时的错误。

相关文章
相关标签/搜索