关闭

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

标签: swiftios开发移动平台
53人阅读 评论(0) 收藏 举报
分类:

原文地址: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

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:113次
    • 积分:40
    • 等级:
    • 排名:千里之外
    • 原创:0篇
    • 转载:0篇
    • 译文:2篇
    • 评论:0条
    文章分类
    文章存档