Optional(可选类型)
Optional
是Swift中引入的新类型. 表示可以有值也可以没值. 当它没值时, 就是nil
. Optional类型其实是枚举:
enum Optional<T> : Reflectable, NilLiteralConvertible {
case None
case Some(T)
init()
init(_ some: T)
/// Haskell's fmap, which was mis-named
func map<U>(f: (T) -> U) -> U?
func getMirror() -> MirrorType
static func convertFromNilLiteral() -> T?
}
当Optional
没有值时,返回的nil
其实就是Optional.None
,即没有值。除了None
以外,还有一个Some
,当有值时就是被Some<T>
包装的真正的值,所以我们拆包的动作其实就是将Some
里面的值取出来。
Swift和OC中nil的区别
- OC中,
nil
是一个指向不存在对象的指针; - Swift中,
nil
不是指针,它是一个确定的值,用于表示值缺失。任何类型的可选状态都可以设置为nil
,不只是对象类型(当基础类型(整形、浮点、布尔等)没有值时,也是nil
);
定义
定义一个Optional的值只需要在类型后面加上问号(?),如:
var str: String?
另外:在上面可以看到,Optional其实就是一个枚举,然后给它指定一个类型就行了,所以下面这两种方法都能声明一个Optional值:
var str: String! = "Hello World!"
var str2: Optional<String>
一个Optional值和非Optional值的区别就在于:Optional值未经初始化虽然为nil,但普通变量连nil都没有:
//未被初始化,但是是一个Optional类型,为nil
var str: String?
str //输出nil
//未被初始化,也不是Optional类型
var str2: String
str2 //使用时出错
使用
关于! 和 ? 使用场景
?的使用场景:
1)声明Optional值变量
2)在对Optional值操作中,用来判断是否能响应后面的操作
!的使用场景:
1)强制对Optional值进行拆包
2)声明隐式拆包变量,一般用于类中的属性
**对于可选类型, 使用之前需要拆包才不会报错. 拆包有两种方式. **
显式拆包
- Optional Binding
正常情况下,
let pet = jackon.pet
的返回值不是Bool
,而是“有值”或者“没有值”,不能直接用于条件表达式。但是if let
的搭配是一种仅仅针对Optional
类型的特殊的情况,苹果有意在编译器做了这种处理。
if let str = strValue {
let hashValue = str.hashValue
}
-
通过! str!
对比拆包前后,对str的输出:var str: String? = "Hello World!"
str //{Some "Hello World!"}
str! //Hello World!
隐式拆包
通过在声明时的数据类型后面加一个感叹号(!)来实现:
var str: String! = "Hello World!"
str //Hello World!
可以看到没有使用(?)进行显式的折包也得到了Some
中的值,这个语法相当于告诉编译器:在我们使用Optional
值前,这个Optional
值就会被初始化,并且总是会有值,所以当我们使用时,编译器就帮我做了一次拆包。如果你确信你的变量能保证被正确初始化,那就可以这么做,否则还是不要尝试为好。
Optional Chaining
当一个Optional
值调用它的另一个Optional
值的时候,Optional Chaining
就形成了,基本上,Optional Chaining
就是总是返回一个Optional
的值,只要这个Chaining
中有一个值为nil
,整条Chaining
就为nil
,和Objective-C
的向nil
发消息类似。
//不使用Optional Chaining需要判断两次
if let pet = jackon.pet {
if let toy = pet.favoriteToy {
toy.name
}
}
//使用Optional Chaining只需要判断一次
if let toy = jackon.pet?.favoriteToy {
toy.name
}
Optional Chaining
除了能将属性返回的类型变为Optional
外,连方法的返回值都能强制变为Optional
,哪怕这个方法没有返回值,但是别忘了,Void
也算是一个类型:
typealias Void = ()
如果Pet类有一个玩玩具的play方法的话,就可以这样来判断是否会调用成功:
if let p: Void = jackon.pet?.play() {
"play is called"
}