Swift Optionals: When to use if let, when ? and !, when as? and as

翻译 2015年07月08日 16:38:42

原文地址:http://www.touch-code-magazine.com/swift-optionals-use-let/

1. 可选类型是什么?

回忆你知道的普通的数据类型,就像你在的Objective-C(或PHP,JavaScript等)等代码中曾经看到的。比方说,你有两个变量叫“age”和”height”。他们是简单的Int类型的,可以容纳任何整数。让我们来看一个简单的代码示例:

var age: Int = 35
let height: Int = 180

通过使用关键字“var”声明变量“age”并且赋值为35。你可能会改变你的想法后来并指定另一个值,例如36,你可以改变很多次。相反的,“height”是一个常数 —一旦你赋值100,那它的值永远不变!

在这两种情况下,你都知道Int包含一个整数值。你不能做的是给变量age赋值为空值(nil)。

例如,如果这些数据块代表员工的资料在公司数据库,对一些记录,你可能不知道员工的身高(height) — 在这种情况下,你不应该将它设置值为0,你应该将这个值设置为空或缺失。

遇到上面的情况你应该使用可选的数据类型。

很多人不知道,可选(Optional)实际上是在Swift的标准库中定义,并加入一些语法糖的枚举类型。它看起来像这样(简略的):

enum Optional : NilLiteralConvertible {
  case None
  case Some(T)
}

因此,可选(Optional)可以包含空 - 既然是遵循NilLiteralConvertible协议,可以猜测,那就是当你要使用nil时可以使用可选(Optional)值。Optional定义的后一种情况表明,它可容纳一种数据 —可以是任何类型。

对于一个这样的定义 Optional< Int >— 它可以包含nil或Int值。非常简单和容易。那么,让我们用Swift的语法糖吧?并声明”age”和”height”变量,可以保留和整数或nil。

var age: Int? = nil
var height: Int? = 180

通过在数据类型之后加入?相当于你马上告诉编译器这个变量可能包含数字或者是nil。请注意,定义可选常量并没有意义的—常量是一个确定的不可变的值,但是可选说明它的值是可以是nil或者其他的值。

为了弄清楚到底是怎么回事,当你使用Optionals让我们来看看这是怎么回事幕后在上面的例子上。这一行:

var height: Int? = 180

等价于:

var height: Optional<Int> = Optional<Int>(180)

看来,一旦你知道发生了什么事情就很简单了,对不对?
“?”符号添加到数据类型来声明Optinal只是语法糖,实际上是在翻译上面的代码。

好吧,现在,我希望你能认识到什么是可选的(Optinal)数据类型,让我们一起来看看如何在代码中使用这些类型。

2. When to use ? and when !

让我们想象一下,你有一个简单的iPhone应用程序 — 一个基于的UIKit应用。你的视图控制器有一些代码,你想在屏幕的最上层显示一个新的视图控制器。你决定通过导航控制器Push一个新的。

希望你知道每一个的ViewController实例都有一个属性navigationController。如果你正在开发一个基于导航控制器的应用程序,你的应用程序的主视图控制器的这个属性被自动设置,你可以用它来push或pop其他的视图控制器。如果你使用的是单一的应用程序项目模板 — 将不会有自动为您创建一个导航控制器(navigationController),所以你的应用程序的默认视图控制器将不会有任何值存储在navigationController属性中。

我敢肯定你已经猜到了,这正是一个可选的数据类型的情况。如果你检查的UIViewController,你会看到属性定义为:

var navigationController: UINavigationController? { get }

因此,让我们回到我们的用例。如果你明确地知道一个事实,你的视图控制器永远有一个导航控制器,你可以继续前进,并且强制解析:

controller.navigationController!.pushViewController(myViewController, animated: true)

当你把一个!放在可选的属性名的后面,你告诉编译器,我不关心这个属性是不是可选的(optional),我知道,当这个代码执行时,这个可选值里会有一个确定的值,就像一个正常的数据类型。这样其实不是很好?如果一个视图控制器没有导航控制器?如果你以为navigationController始终有值这个假设是错误的?那么您的应用程序会崩溃—简单而丑陋的。

因此,使用!只有当你是101%肯定这是安全的。

假如你不确定是否总是存在一个导航控制器?那么你就可以使用?代替!

controller.navigationController?.pushViewController(myViewController, animated: true)

属性名后面的?告诉编译器,我不知道该属性是否有值或者是nil,因此:如果它有值就使用它,相反的,这个表达式就是nil。实际上,?确保只有当存在导航控制器的情况下,使用该属性。没有if判断或者其他任何处理。当你不在乎你是否有一个导航控制器或没有,只想当存在是做一些事情,那么这个语法是完美的。
但是,如果你想在没有navigationController的情况下做其他事情?例如,显示一个警告框来提示用户。
那么你要使用if let的表达方式。

3. When to use “if let”?

if let是Swift一个特殊的结构,使你可以检查一个可选值(Optional),如果它确实存储值,那么可以使用它解析后的数据做一些事情。让我们一起来看看:

if let nav = controller.navigationController {
    nav.pushViewController(myViewController, animated: true)
} else {
    //show an alert ot something else
}

if let 结构会解析controller.navigationController这个属性并且把解析出来的值放到常量“nav”中。你可以在if的第一个分支中直接使用它。注意,在分支内部你没有必要在使用?或者!来解析。你必须得知道“nav”的类型就是“UINavigationController”而不是可选类型(Optional type),因此你可以很直接地使用它。(你是否还记得navigationController原始的类型是UINavigationController?)

if let 结构在其他情况下也很有用。想象一下,你要多次调用navigationController这个属性而不是单一的调用一个方法,你不使用if let 结构那么你的代码看起来是这样:

controller.navigationController?.hidesBarsOnSwipe = true
controller.navigationController?.hidesBarsOnTap = true
controller.navigationController?.navigationBarHidden = false
controller.navigationController?.popToRootViewControllerAnimated(true)

这段代码的确有用但是你要重复地对这个属性做四次解析,如果用if let 结构你只需要解析一次而且更清晰,更高效:

if let nav = controller.navigationController {
  nav.hidesBarsOnSwipe = true
  nav.hidesBarsOnTap = true
  nav.navigationBarHidden = false
  nav.popToRootViewControllerAnimated(true)
}

很棒,不是吗?

最后一点 — nav在上面的例子是一个常量。你可以改变它的属性并且调用它的方法,但你不能改变nav本身的值。值得一提的是,你也可以这样使用if var nav = controller.navigationController。它的工作原理和if let相同,但你解析出来的是变量而不是常量。如果你有需要,可以改变它的值。

让我们来看看可选类型(Optional types)的最后的使用情境 — 类型转换。

4. When to use as? and when as

让我们继续想象UIKit的应用程序。让我们假设你在屏幕上显示一个新的视图控制器(例如使用这个方法presentViewController(_, animated:, completion:) )。

//your custom view controller
class MyViewController: UIViewController {
    var lastUpdated: NSDate? = nil
}

//in your master view controller
let myVC = MyViewController()
presentViewController(myVC, animated: true, completion: nil)

您定义了一个视图控制器,它有一个名为的“lastUpdated”额外的属性。让我们假设一旦这个控制器被呈现就改变这个属性。您可以通过主视图控制器上的presentedViewController属性访问当前正在屏幕上显示的控制器:

controller.presentedViewController!.title = "New Title"

presentedViewController的类型是 UIViewController?所以你可以很容易地解析并使用它。但是如果你应该如何改变“lastUpdated” 这个属性呢?这个属性是定义在“MyViewController”中而不是“UIViewController”?

你应该使用“as?”这个语法做转换,像这样:

let myVC = controller.presentedViewController as? MyViewController
myVC?.lastUpdated = NSDate()

as? 会根据给定的类型做转换,如果失败了会返回nil。这就是为什么一些函数的返回类型总是可选类型。当你不能保证类型转换一定成功时你应该用as?—例如,当你尝试将“AnyObject “或者”Any”转换成一个具体的类型时。
长话短说,当你不确定转换是否成功时用 as? 。

如果你101%肯定,转换会成功,在这种情况下可以省略?而只使用as.

swift 1.2以上版本对这一部分做了修改

对AnyObject直接使用as做类型转换会报错

1

为避免误解,同时学习最新的swift语言 原文后半部分略去,
有兴趣的可以参看这个
http://iphonedev.tv/blog/2015/2/9/swift-12-fixes-and-breaks-a-few-things-you-should-be-excited
end

解决ios - use of @import when modules are disabled问题

第一步: 点击项目->targets->build settings 搜索module将下图两个设置成YES 编译运行,如果问题依然存在那么进入第二布。 第二步:查看项目中是否存在.mm文件,如果存...
  • kun_LY
  • kun_LY
  • 2017年04月13日 11:22
  • 2566

iOS之use of '@import' when modules are disabled

在导入百度地图SDK的Framework时遇到“use of '@import' when modules are disabled” 错误,寻找网上的解决方案一一试过均无效,偶然突发奇想,把SDK文...
  • hilaryms
  • hilaryms
  • 2016年10月11日 15:22
  • 1214

原创:PHP内核研究 常量

声明:本文为斯人原创,全部为作者一一分析得之,有不对的地方望赐教。 博客地址:PHP技术博客 在CSDN也会同步更新的哦. 欢迎转载,转载请注明出处  常量 什么是常量. ...
  • siren0203
  • siren0203
  • 2012年03月10日 19:39
  • 2343

Cannot use Jedis when in Multi. Please use Transation or reset jedis state.

使用jedis的transaction时,执行如下代码会报异常: Jedis conn = new Jedis("localhost");conn.select(0);Transaction...
  • yuxxz
  • yuxxz
  • 2016年08月30日 23:27
  • 2650

使用TinyXML 出现 skipped when looking for precompiled header use 问题

使用TinyXML 出现 skipped when looking for precompiled header use 问题 参考了http://www.programmingforums.org/...
  • gengxt2003
  • gengxt2003
  • 2010年10月28日 18:39
  • 4315

iOS LBS地图服务之高德地图几种定位说明

iOS 11出现了四种定位隐私设置 iOS 11不能定位问题 iOS 11定位隐私选择提示框说明定位原因否则被拒 选择使用应用期间定位屏幕顶部讨厌的蓝色闪烁提示框 如何不出现蓝色定位闪烁提示框 高德地...
  • kuangdacaikuang
  • kuangdacaikuang
  • 2018年01月30日 23:30
  • 63

libpcap6410交叉编译

1.首先按照上一篇文章中的步骤建立交叉编译环境。 2.下载libpcap-1.0.0.tar.gz源码。 3.解压libpcap-1.0.0.tar.gz,cd进入目录,修改confiugre文件...
  • jingzhesiye
  • jingzhesiye
  • 2012年06月11日 10:45
  • 3059

Swift Compiler Error Integer literal overflows when stored into 'UInt8'

Swift Compiler Error Integer literal overflows when stored into 'UInt8'
  • soindy
  • soindy
  • 2015年07月06日 11:56
  • 1434

When to use assert() and when to use try catch?

Try... catch - for exceptional conditions, i.e. conditions which aren't caused by malformed code, bu...
  • deyili
  • deyili
  • 2013年08月27日 15:47
  • 1401

浅谈Sybase的备份与恢复及常见问题解决

      上一篇写了Sybase的安装,下面就讲一下备份与恢复.这两步也是必须会的.必须掌握的.    1.1 备份    首先说一下备份.    备份的命令是     dump database ...
  • cuiran
  • cuiran
  • 2010年08月03日 15:18
  • 4854
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Swift Optionals: When to use if let, when ? and !, when as? and as
举报原因:
原因补充:

(最多只允许输入30个字)