国外优秀教程精译 | 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 喜欢在最后一刻创建用户界面元素以避免任何不必要的工作。 必须不断地打开你肯定知道的值,这会使人厌烦,所以这些是隐式解开的。
不要担心,如果您发现隐式解包的选项有点难以理解 - 当您使用该语言时,它将变得清晰。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容