原文出处: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. HEAP CORRUPTION DETECTED :after Normal block 错误

    http://blog.csdn.net/zhccl/article/details/7889590

  2. Service and controller in angularJs

    Separation of concern is at the heart while designing an AngularJS application. Your controller must ...

  3. Daily Scrum Meeting ——ThirdDay(Beta)12.11

    一.Daily Scrum Meeting照片 二.Burndown Chart 三.项目进展(check-in) 1.欢迎界面的优化,从模糊到清楚 2.新增主界面背景 3.新增注册背景 4.参与者侧 ...

  4. 【NEUQACM OJ】1017: 平面切割(特别版)

    1017: 平面切割(特别版) 题目描述 我们要求的是n条闪电型折线分割平面的最大数目.比如,一条闪电型折线可以将平面分成两部分,两条最多可以将平面分成12部分,三条最多可将平面分成31部分,四条最多 ...

  5. [转] nodemon 基本配置与使用

    在开发环境下,往往需要一个工具来自动重启项目工程,之前接触过 python 的 supervisor,现在写 node 的时候发现 supervisior 在很多地方都有他的身影,node 也有一个 ...

  6. Windows中使用TortoiseGit提交项目到GitLab配置

    下文来给各位介绍Windows中使用TortoiseGit提交项目到GitLab配置过程,下在全部图片希望对各位带来方便面. Gitlab默认的配置推荐使用shell命令行与server端进行交互,作 ...

  7. Shell 快捷键

    输入bind -P可以查看所有的键盘绑定 Ctrl + Shift + '-' 缩小shell框Ctrl + Shift + ‘+' 放大shell框 CTRL相关的快捷键Ctrl-A 相当于HOME ...

  8. linux安装oracle11g

    准备oracle安装文件 Oracle11gR2包含两个文件linux_11gR2_database_1of2.zip和linux_11gR2_database_2of2.zip,将这两个文件通过SS ...

  9. Nodejs 创建web服务

    // 加载http服务包 var http = require("http"); // 创建服务 var server = http.createServer(requestLis ...

  10. sql server的优缺点

    sql server的优点众多,让其在数据库领域独占鳌头,成为最受欢迎的数据库系统,其优缺点也自然是喜爱者们所关注的,首先了解一下它的历史: sql server是一个关系型数据库管理系统,最初是由M ...