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