国外优秀教程精译 | Swift 可选值详解(下)

如果看完 Swift 可选值详解(上)后,你对可选值还是有些迷惑,甚至一头雾水,那么我们来换一种方式来解释。
看下面的方法:

func yearAlbumReleased(name: String) -> Int {
    if name == "Taylor Swift" { return 2006 }
    if name == "Fearless" { return 2008 }
    if name == "Speak Now" { return 2010 }
    if name == "Red" { return 2012 }
    if name == "1989" { return 2014 }

    return 0
}

该方法要求传入一个 Taylor Swift 专辑的名称,返回对应的发行年份。 但是如果我们传入了专辑名称“Lantern”,因为我们将 Taylor Swift 与 Hudson Mohawke 混为一谈(这是一个容易犯的错误,对吧?),该方法就返回 0,因为它不是 Taylor 的专辑之一。
但 0 在这里有意义吗? 当然,如果专辑是在公元 0 年发布的,当时凯撒奥古斯是罗马的皇帝,0 可能有意义,但在这里只会让人感到困惑 —— 人们需要提前知道 0 的含义是“不被认可”。(译者注:这里说明的是,0 代表的含义需要提前定义,否者很容易被当作年份处理)
最好是改写为返回 int (有对应年份时)或 nil (没有对应年份),可选值使这一切变得容易。下面是改写后的方法:

func yearAlbumReleased(name: String) -> Int? {
    if name == "Taylor Swift" { return 2006 }
    if name == "Fearless" { return 2008 }
    if name == "Speak Now" { return 2010 }
    if name == "Red" { return 2012 }
    if name == "1989" { return 2014 }

    return nil
}

由于用了可选值,我们需要用 if let 解包,因为需要检查值是否存在。
再来看另一种情形:

var items = ["James", "John", "Sally"]

现在我们想输出其中一个名字对应的索引值,我们可能这样写:

func position(of string: String, in array: [String]) -> Int {
    for i in 0 ..< array.count {
        if array[i] == string {
            return i
        }
    }

    return 0
}

其中循环检查了数组成员,返回了对应名称的索引值,如果没找到,返回 0 。
现在试着运行下面 4 行代码:

let jamesPosition = position(of: "James", in: items)
let johnPosition = position(of: "John", in: items)
let sallyPosition = position(of: "Sally", in: items)
let bobPosition = position(of: "Bob", in: items)

将输出 0, 1, 2, 0 —— James 和 Bob 的位置是一样的,然而他们实际上一个存在,而另一个并不存在。这是因为我用 0 表示 “不存在” 造成的。图省事的话,可以用 -1 来表示不存在,但这样一来,又必须时刻记得有这个特殊值意味着“不存在”。(译者注:-1 引发的崩溃相信大家都有经历过,在 C 语言中,有符号 -1 转换为无符号时,为无符号的最大值,从而导致很多混乱)
解决办法还是可选值:用 nil 表示没有找到对应名称。这也是数组内置查找方法 someArray.firstIndex(of: someValue)的实际做法。
当你要对付“可能有可能没有”的值,Swfit 强制要求在使用前解包,以此明确这里可能没有值。if let就是用来做这个的:如果有值就解包后使用,否者完全不使用它。Swift 根本不让你凭空地使用一个可能为空的值。

强制解包

Swift 允许使用感叹号 !来屏蔽它的安全性。当你能确认可选值有值时,可以将感叹号放在这个值后面来强制解包它。

请小心:如果你强制解包一个没有值的可选值时,会引发代码崩溃。

下面是一些协同工作的代码:

func yearAlbumReleased(name: String) -> Int? {
    if name == "Taylor Swift" { return 2006 }
    if name == "Fearless" { return 2008 }
    if name == "Speak Now" { return 2010 }
    if name == "Red" { return 2012 }
    if name == "1989" { return 2014 }

    return nil
}

var year = yearAlbumReleased(name: "Red")

if year == nil {
    print("There was an error")
} else {
    print("It was released in \(year)")
}

代码获得了专辑的发行年份。如果没找到专辑,year被置空,将打印出错信息。否则正确的发行年份会被打印。
是吗?好吧,因为 yearAlbumReleased()返回的是可选值,这里由没有使用 if let来解包。结果,实际输出的是“It was released in Optional(2012)”——可能并不是我们想要的。
关键是,我们已经检查了可选值的有效性,再用 if let进行所谓的安全解包显得有点无意义。因此,Swift 提供了解决办法——把上面代码中第二个print() 改为这样:

print("It was released in \(year!)")

注意这个感叹号:它的意思是“我保证这里有值,强制解包吧”。

隐形解包

你还能用感叹号来创建隐形解包可选值,这是很多人真正感到困惑的地方。那么就好好读读下面的介绍。

  • 静态变量必须含有值。比如:String 必须含有 string ,哪怕是空字符串"",不能为 nil 。
  • 可选值可以有值也可以没有。使用前必须解包。比如 String? 可能为 string ,或者是 nil ,确定的办法就是解包。
  • 隐形解包可选值可以有值也可以没有。但使用前不需要解包。Swift 不再为你检查,所以你使用时要额外小心。比如 String! 可能为 string ,或者是 nil ——取决于你适当的使用它。它类似静态变量,Swift 允许你直接访问它的值,不需要安全地解包。如果你要这么做,意味着你知道一定有值,否者程序会崩。

使用隐形解包可选值的主要情形是在使用 UIKit 的界面元素时(macOS 里对应 AppKit ),这些需要事先声明,但是在创建之前你不能使用它们 —— 而 Apple 喜欢在最后一刻创建用户界面元素以避免任何不必要的工作。 必须不断地打开你肯定知道的值,这会使人厌烦,所以这些是隐式解开的。
不要担心,如果您发现隐式解包的选项有点难以理解 - 当您使用该语言时,它将变得清晰。

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,154评论 2 9
  • 英文原文 Swift 是一门非常安全的语言,这里指的是 Swift 在防止代码意外崩溃方面做了很多努力。代码崩溃的...
    溪石iOS阅读 360评论 1 4
  • Swift 介绍 简介 Swift 语言由苹果公司在 2014 年推出,用来撰写 OS X 和 iOS 应用程序 ...
    大L君阅读 2,294评论 3 25
  • 基础部分(The Basics) 当推断浮点数的类型时,Swift 总是会选择Double而不是Float。 结合...
    gamper阅读 811评论 0 7
  • 使用可选值有时会让人感到有点笨拙,所有的解包和检查会变得如此繁重,以至于会让你想要丢几个感叹号上去强制解包,好让你...
    溪石iOS阅读 699评论 1 9
  • 前天读完余华的小说《活着》,在这篇用抽离平静的笔墨,用极其直白的语言,描写了绝望的生活和艰难的处境,却在穿越所有...
    塵光阅读 67评论 0 1
  • 今晚读到苏东坡有关瑜伽与炼丹这个部分,还是很开心的。特别是他有关瑜伽的诠释,暂且抛开瑜伽八部功法前面的持戒精进外,...
    转转小仙女阅读 133评论 0 1
  • 本节主要内容 理解用户和组的概念 用户管理 组管理 权限分配 1. 理解用户和组的概念 在第一讲中我们提到,lin...
    piziyang12138阅读 111评论 0 0
  • 当代著名散文家,诗人余光中自命江南人,并曾谓大陆是母亲,台湾是妻子,香港是情人,欧洲是外遇。他是浪漫豁达不...
    1703刘晨阅读 334评论 0 1
  • 听樊登老师解读《中国哲学简史》 (1)老子:圣人常无心 老子所谓的圣人与孔子所说的圣人是不一样的。老子说的圣人是得...
    六爸啦啦啦阅读 134评论 0 0