原文出处:Understanding Optionals in Swift

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

初试Optional

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

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值,让我们看看这是如何工作的。

一切都是Some

如果我们在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等价于nil

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

[翻译]理解Swift中的Optional的更多相关文章

  1. swift中,Optional、?与!之间的关系

    swift中,Optional.?与!之间的关系 Optional <ClassName> 与 ClassName? 等价 对 ClassName! 强制取值会导致崩溃(如果对象为nil时 ...

  2. Swift中的Optional类型 (可选类型)与强制解包 ? !

    我们在swift的开发中会经常遇见?和! ,理解这两个符号深层次的内容对我们的开发是相当有利的: 目前网上对swift3.0的教程还相当的少,如果去搜索会发现早期的说法,在定义变量的时候,swift是 ...

  3. 理解Swift中map 和 flatMap对集合的作用

    map和flatMap是函数式编程中常见的概念,python等语言中都有.借助于 map和flapMap 函数可以非常轻易地将数组转换成另外一个新数组. map函数可以被数组调用,它接受一个闭包作为參 ...

  4. 如何理解swift中的delegate

    Delegation翻译为代理或者委托,是一种设计模式.顾名思义,使class或struct能够将某些职责移交给其他类型的实例. 该设计模式通过定义一个封装(包含)delegate的protocol( ...

  5. [翻译]理解Ruby中的blocks,Procs和lambda

    原文出处:Understanding Ruby Blocks, Procs and Lambdas blocks,Procs和lambda(在编程领域被称为闭包)是Ruby中很强大的特性,也是最容易引 ...

  6. 浅谈 Swift 中的 Optionals

    input[type="date"].form-control,.input-group-sm>input[type="date"].input-grou ...

  7. 理解、学习与使用 Java 中的 Optional

    从 Java 8 引入的一个很有趣的特性是 Optional  类.Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException) -- 每个 Java 程序员都 ...

  8. swift中的?和!理解

    本文转载至 http://www.cnblogs.com/dugulong/p/3770367.html 首先贴cocoachina上某位大大的帖子:     Swift语言使用var定义变量,但和别 ...

  9. [翻译]使用Swift在Xcode中创建自定义控件

    使用Swift在Xcode中创建自定义控件 原文 IBDesignable and IBInspectable With IBDesignable and IBInspectable, develop ...

随机推荐

  1. APP产品交互设计资源汇总(不断更新中...)

    Axure RP 7 元件库 http://www.iaxure.com/tag/axure7-0%E5%85%83%E4%BB%B6%E5%BA%93 ios8组合元件库V1.2版 http://w ...

  2. Ext.js的store里放model,还是field?

    按别人的经验, 一般来说,如果通用性强的应用,STORE里存放MODEL,便于重用代码. 如果通用性较弱的(报告,图表),则考虑使用field进行定制.

  3. 【leetcode】Pascal's Triangle II

    题目简述: Given an index k, return the kth row of the Pascal's triangle. For example, given k = 3, Retur ...

  4. Beginning Scala study note(9) Scala and Java Interoperability

    1. Translating Java Classes to Scala Classes Example 1: # a class declaration in Java public class B ...

  5. webrtc中APM(AudioProcessing module)的使用2

    这个其实就是从Audio_processing.h中拿出来的. APM should be placed in the signal chain as close to the audio hardw ...

  6. windows下mysql客户端输入中文显示??解决方法

    >>>>>>>>>>>>>>>>>>>> 1.检查并修改mysql的my.ini ...

  7. git使用

    1.权限校验 首先,您的数据保存在远端服务器一份,服务器需要对您的身份进行识别,一段RAS加密字串, 启动GUI,step1:创建秘钥,generate SSHkey. step2:添加密钥:去你的代 ...

  8. RTX二次开发集成

    1,rtx服务器端有很多端口,二次发的程序与这些打开的端口交互.打开端口的方法在rtx服务管理器中,默认http服务未启用.需要手动启用http端口如下: 如果打开rtx服务器没有启用http的801 ...

  9. SparkLauncher 1.6 版本bug

    背景 近期在研究使用java api的方式来调用Spark程序,通过句柄的方式来完成监控Job运行状态.及时杀死Job等功能.官方文档直接指出使用Java/Scala创建Job的方式——利用Spark ...

  10. [资料分享]dubbo视频教程流行版

    一.基础篇 第001节–课程介绍 第01节–使用Dubbo对传统工程进行服务化改造的思路介绍 第02节–使用Dubbo对传统工程进行服务化改造 第03节–ZooKeeper注册中心安装 第04节–使用 ...