原文出处: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将会使你保证你写代码更迅速,减小运行时的错误。