Optional是什么?
A type that represents either a wrapped value or
nil
, the absence of a value.
我们从官方文档关于Optional
介绍可以看出,可选类型Optional
可以是包装值,也可以是nil
,也就是缺失值。有且仅有这两种情况。
You use the
Optional
type whenever you use optional values, even if you never type the wordOptional
. Swift’s type system usually shows the wrapped type’s name with a trailing question mark (?
) instead of showing the full type name. For example, if a variable has the typeInt?
, that’s just another way of writingOptional<Int>
. The shortened form is preferred for ease of reading and writing code.
在平时的代码编程过程中,我们似乎没有见过Optional
这个单词。以Int
为例,Optional<Int>
是它的一种写法,还有一种是Int?
。
// shortForm和longForm是等同的。
let shortForm: Int? = Int("42")
let longForm: Optional<Int> = Int("42")
Optional<Int>
和Int?
是等同的。所以Optional
可以理解为是?
。
The
Optional
type is an enumeration with two cases.Optional.none
is equivalent to thenil
literal.Optional.some(Wrapped)
stores a wrapped value.
Optional
是一个枚举类型,它包含两个case
,none
和Optional.some(Wrapped)
。
接下来,我们看看Optional
的代码。
@frozen public enum Optional<Wrapped> : ExpressibleByNilLiteral {
/// The absence of a value.
///
/// In code, the absence of a value is typically written using the `nil`
/// literal rather than the explicit `.none` enumeration case.
case none
/// The presence of a value, stored as `Wrapped`.
case some(Wrapped)
// For example:
let number: Int? = Optional.some(42)
let noNumber: Int? = Optional.none
print(noNumber == nil)// Prints "true"
从官方代码看到,Optional是一个enum
枚举,里面有两个case
,none
和some(Wrapped)
,Optional.none
就是nil
,Optional.some(Wrapped)
就是一个包装值,Wrapped
就是存储的值。
安全解包
You must unwrap the value of an
Optional
instance before you can use it in many contexts. Because Swift provides several ways to safely unwrap optional values, you can choose the one that helps you write clear, concise code.
如果值被Optional包装,取值时需要打开包装,官方提供了几种安全解包的方法。
我们以下面的字典为例,来介绍安全解包的几种方式:
let imagePaths = ["star": "/glyphs/star.png",
"portrait": "/images/content/portrait.jpg",
"spacer": "/images/shared/spacer.gif"]
通过字典的key值获取value值,这个返回的value值就是一个Optional
值,所以imagePaths["star"]
是Optional<String>
类型,我们也可以写成String?
。
1. 可选绑定(Optional Binding)
To conditionally bind the wrapped value of an
Optional
instance to a new variable, use one of the optional binding control structures, including
if let
,guard let
, andswitch
.
// if let, 打印 [if let] The star image is at '/glyphs/star.png'
if let starPathIf = imagePaths["star"] {
print("[if let] The star image is at '\(starPathIf)'")
}
// guard let, 打印 [guard let] The star image is at '/glyphs/star.png'
func guardTest() {
guard let starPathGuard = imagePaths["star"] else {
return
}
print("[guard let] The star image is at '\(starPathGuard)'")
}
guardTest()
// switch, 打印 [switch] The star image is at '/glyphs/star.png'
switch imagePaths["star"] {
case .none:
print("nil")
case .some(let starSwitch):
print("[switch] The star image is at '\(starSwitch)'")
}
2. 可选链(Optional Chaining)
To safely access the properties and methods of a wrapped instance, use the postfix optional chaining operator (postfix
?
).
if imagePaths["star"]?.hasSuffix(".png") == true {
print("The star image is in PNG format")
}
// Prints "The star image is in PNG format"
imagePaths["star"]
取出来的值为Optional
值。为了安全访问imagePaths["star"]
包装值的属性或方法,我们需要在imagePaths["star"]
后面添加?
,即imagePaths["star"]?
。
当值为nil
的时候,不满足继续执行的条件,所以hasSuffix(".png")
就不会被调用。
当值为String
的时候,继续执行,所以hasSuffix(".png")
就会被调用。
3. 空合运算符(Nil-Coalescing Operator)
Use the nil-coalescing operator (
??
) to supply a default value in case theOptional
instance isnil
. Here a default path is supplied for an image that is missing fromimagePaths
.
当可选实例的值为nil时,给可选实例一个默认值。
let defaultImagePath = "/images/default.png"
let heartPath = imagePaths["heart"] ?? defaultImagePath
print(heartPath)
// Prints "/images/default.png"
The
??
operator also works with anotherOptional
instance on the right-hand side. As a result, you can chain multiple??
operators together.
默认值也可以是可选实例。
let shapePath = imagePaths["cir"] ?? imagePaths["squ"] ?? defaultImagePath
print(shapePath)
// Prints "/images/default.png"
4. 无条件解包(Unconditional Unwrapping)
When you’re certain that an instance of
Optional
contains a value, you can unconditionally unwrap the value by using the forced unwrap operator (postfix!
).
强制取出包装内的值。
let number = Int("42")!
print(number)
// Prints "42"
You can also perform unconditional optional chaining by using the postfix
!
operator.
无条件解包也可以用于可选链。
let isPNG = imagePaths["star"]!.hasSuffix(".png")
print(isPNG)
// Prints "true"
Unconditionally unwrapping a
nil
instance with!
triggers a runtime error.
无条件解包之前得确定解包的可选实例值不能为nil
,否则会触发运行时报错。