Swift90Days - 可选类型
(对于可选类型的理解可以参见猫神的这篇行走于 Swift 的世界中,在此整理一些关键的部分。)
Optinal Value
Swift 中的 Optinal Value 就像是一个盒子,可能装着值,也可能什么都没装。
我们可以用 ?
定义一个可选类型的整数 :
var num: Int?
num = nil //nil
num = 3 //{Some 3}
可选类型的真实身份其实是 Optional
类型,常用的 ?
是 Apple 提供的语法糖。使用 Optional
的写法是这样的:
var num: Optional<Int>
num = Optional<Int>() // nil
num = Optional<Int>(3) // {Some 3}
点进去看下 Optional
的定义:
enum Optional<T> : Reflectable, NilLiteralConvertible {
case None
case Some(T)
/// Construct a `nil` instance.
init()
/// Construct a non-\ `nil` instance that stores `some`.
init(_ some: T)
/// If `self == nil`, returns `nil`. Otherwise, returns `f(self!)`.
func map<U>(f: (T) -> U) -> U?
/// Returns a mirror that reflects `self`.
func getMirror() -> MirrorType
/// Create an instance initialized with `nil`.
init(nilLiteral: ())
}
这样一来,var num: Int? = 3
其实就是 Optional.Some(3)
。
在这里,?
声明了一个 Optional<T>
类型的变量,然后做了判断:
- 如果等号右边是 nil ,则赋值为
Optional.None
- 如果等号右边不是 nil ,则通过
init(_ some: T)
初始化并返回Optional.Some(T)
。
Force Unwraps
在遭遇可选类型的时候,我们可以在 Optional 变量后面加上 !
进行强制解包。这样虽然减少了代码量,但是容易带来隐患,使用的时候务必要慎重。就像是过马路一样,一定要仔细看好两边车辆,否则悲剧随时可能发生。
而实际上,很多时候可以避免使用 !
,用其他方法取而代之。
来看这样一个例子:
let ages = [
"Tim": 53, "Angela": 54, "Craig": 44,
"Jony": 47, "Chris": 37, "Michael": 34,
]
let people = sorted(ages.keys, <).filter { ages[$0]! < 50 }
println(people) // "[Chris, Craig, Jony, Michael]"
在这里先对 ages
进行了排序,然后筛选出年纪小于 50 的人。因为是对字典取值,会出现 nil
,所以 ages[$0]
是 optional 的,需要进行解包。
当然也可以用 if let
:
let people = sorted(ages.keys, <).filter {
if let age = ages[$0] {
return age < 50
}
return false;
}
但是本来一行就能搞定的问题却拖沓到了五行才解决。实际上,换个思路,我们完全不需要遭遇可选类型:
let people = filter(ages) { $0.1 < 50 }.map { $0.0 }.sorted(<)
filter
中 $0 表示传入的 key-value ,$0.1 表示 value,map
中 $0 表示传入的 key-value ,$0.0 表示 key 。或许这样可读性比较差,可以通过 tuple 包装一下入参:
let people = filter(ages) { (k,v) in v < 50 }.map { (k,v) in k }.sorted(<)
在平时的开发中,我们可以用 if let
或者 ??
替换掉 !
。如果确实确实肯定不会出问题再去用 !
。
Implicitly Unwrapped Optionals
同样是 !
符号,如果放在类型后面,则表示隐式解析可选类型 (Implicitly Unwrapped Optionals):
var num: Int!
num = 1 // 1
num = nil // nil
通过 !
定义的变量,实质上只是对 Optional 的变量多了一层封装,帮我们完成了原本需要手动解包的过程。
在什么场合下可以使用隐式解析可选类型呢?
场景1:无法在初始化方法中定义的常量
有些尴尬的情况,我们想定义常量属性,无奈它的初始化依赖于其他属性值。如果你用可选类型,实在是麻烦,因为你确信无疑它肯定会在调用之前就完成初始化,不可能是 nil
,这个时候你可以用 !
进行定义。
举个例子:
class MyView : UIView {
@IBOutlet var button : UIButton
var buttonOriginalWidth : CGFloat!
override func viewDidLoad() {
self.buttonOriginalWidth = self.button.frame.size.width
}
}
这部分内容在 Day11 中有涉及,其实更好的方法是用 lazy 延时加载,不再赘述。
场景2:与 objc 进行交互
隐式解析可选类型似乎是为了照顾 objc 这个历史包袱而存在。比如 UITableView
中:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell? { return nil }
我们很清楚的知道,在调用的时候 tableView
和 indexPath
不可能为 nil
,如果还要 if let
这样解包实在是浪费时间。如果是纯粹的 Swift 语言写的,绝对不会定义成 optional 类型。
什么时候不该用
除了上述的情况2 ,其他情况都不该用 (包括情况1)。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。