可空类型(optional)
是Swift的独特特性,用来指定某个实例可能没有值。看到可空类型时,你会知道该实例一定:要么有值并且已经可用,要么没有值。如果一个实例没有值,就称其为nil。
任何类型都可以用可空类型来说明一个实例可能是nil。这个特性将Swift和Objective-C区分开来,后者只允许对象是nil。
可空类型
声明一个可空类型
import Cocoa
var errorCodeString: String?
errorCodeString = "404"
首先声明一个名为errorCodeString的变量,以字符串的形式来持有错误码信息。接着,显式声明errorCodeString的类型是String;跟之前的形式略有不同,这次在String后面加上了?。?使得errorCodeString成为了可空的String类型。
打印到控制台
print(errorCodeString)
查看控制台,你会看到打印的值是nil。
import Cocoa
var errorCodeString: String?
if errorCodeString != nil {
let theError = errorCodeString!
print(theError)
}
这段代码增加了一个条件语句,如果errorCodeString不是nil就会执行相应的代码(回忆一下,!=就是“不等于”的意思)。
可空实例绑定
可空实例绑定(optional binding)是一种固定模式,对于判断可空实例是否有值很有用。如果有值,就将其赋给一个临时常量或变量,并且使这个常量或变量在条件语句的第一个分支代码中可用。这样可以让代码更简洁,同时保持表达力。下面是基本的语法:
if let temporaryConstant = anOptional {
// 用temporaryConstant做一些事情
} else {
// anOptional没有值,也就是说anOptional为nil
}
如果anOptional
不是nil
,那么就把anOptional
的值赋值给temporaryConstant
然后执行第一个分支。否则执行第二个分支。
还可以嵌套
import Cocoa
var errorCodeString: String?
errorCodeString = "404"
if let theError = errorCodeString {
if let errorCodeInteger = Int(theError) {
print("\(theError): \(errorCodeInteger)")
}
}
展开多个可空实例
import Cocoa
var errorCodeString: String?
errorCodeString = "404"
if let theError = errorCodeString, let errorCodeInteger = Int(theError) {
print("\(theError): \(errorCodeInteger)")
}
可空实例绑定和额外的检查
import Cocoa
var errorCodeString: String?
errorCodeString = "404"
if let theError = errorCodeString, let errorCodeInteger = Int(theError) ,
errorCodeInteger == 404 {
print("\(theError): \(errorCodeInteger)")
}
(不要漏掉这段代码所添加的逗号。)
隐式展开可空类型
现在值得讲一下隐式展开可空类型(implicitly unwrapped optional)
,不过在后面讨论类和类初始化之后才会正式用到它。隐式展开可空类型与普通可空类型类似,只是有一个重要的区别:它们不需要展开。
import Cocoa
var errorCodeString: String!
errorCodeString = "404"
print(errorCodeString)
这里的可空类型是用!
声明的,表示这是一个隐式展开可空类型。条件语句被删除了,因为
使用隐式展开可空类型意味着我们对于其有值要比使用普通可空类型更有信心。确实,隐式展开
可空类型的强大和灵活性跟不必展开就能访问其值有关。
不过要注意,这种强大和灵活性也伴随着一定的危险性:如果隐式展开可空实例没有值的话,访问其值会导致运行时错误。为此,我们建议只要某个实例有可能是nil,就别用隐式展开可空类型。实际上,因为隐式展开可空类型安全性较差,所以如果你不是明确指出想要使用隐式展开可空类型,Swift就会给你普通的可空类型。
可空链式调用
与可空实例绑定类似,可空链式调用(optional chaining)
提供了一种对可空实例进行查询以判断其是否包含值的机制。两者的一个重要区别是,可空链式调用允许程序员把多个查询串联为一个可空实例的值。如果链式调用中的每个可空实例都包含值,那么每个调用都会成功,整个查询链会返回期望类型的可空实例。如果查询链中的任意可空实例是nil,那么整个链式调用会返回nil。
import Cocoa
var errorCodeString: String?
errorCodeString = "404"
var errorDescription: String?
if let theError = errorCodeString, let errorCodeInteger = Int(theError),
errorCodeInteger == 404 {
errorDescription = "\(errorCodeInteger + 200): resource was not found."
}
var upCaseErrorDescription = errorDescription?.uppercased()
errorDescription
原地修改可空实例
可空实例可以被“原地”修改,从而免去创建新变量或常量的麻烦。upCaseErrorDescription增加一个append(_:)调用
upCaseErrorDescription?.append(" PLEASE TRY AGAIN.")
upCaseErrorDescription
nil 合并运算符
要么获取其值(如果可空实例有值),要么使用某个默认值(如果可空实例是nil)。
let description: String
if let errorDescription = errorDescription {
description = errorDescription
} else {
description = "No error"
}
这样写有个问题,那就是需要写很多代码来完成一个本应该很简单的操作:从可空实例中获取值或者使用"No error"(如果可空实例为nil的话)。可以用nil合并运算符(nil coalescing operator)``??
来达到这个目的。
let description = errorDescription ?? "No error"
??
的左边必须为可空实例;在本例中是errorDescription
,一个可空的String。右边必须是非可空的同类型实例;在本例中是"No error",就是String类型。如果左边的可空实例是nil
,那么??
会返回右边的值。如果左边的可空实例不是nil
,那么??
会返回可空实例中包含的值。