Swift中的可选项Optional解包方式实现原理

为什么需要Optional

Swift中引入了可选项(Optional)的概念是为了解决在代码中对于某些变量或常量可能为nil的情况进行处理,从而减少了程序中的不确定性,使得程序更加稳定和安全。

什么是Optional

在Swift中,可选项的类型是使用?来表示的,例如String?即为一个可选的字符串类型,表示这个变量或常量可能为nil。而对于不可选项,则直接使用相应类型的名称,例如String表示一个非可选的字符串类型。

var str: String = nil
var str1: String? = nil

Optional实现原理

Optional实际上是Swift语言中的一种枚举类型。在Swift中声明Optional类型时,编译器会自动将其转换成对应的枚举类型,例如:

var optionalValue: Int? = 10
// 等价于:
enum Optional<Int> {
 case none
 case some(Int)
}
var optionalValue: Optional<Int> = .some(10)

在上面的代码中,我们声明了一个Optional类型的变量optionalValue,并将其初始化为10。实际上,编译器会自动将其转换为对应的枚举类型,即Optional枚举类型的.some(Int),其中的Int就是我们所声明的可选类型的关联值。

当我们在使用Optional类型的变量时,可以通过判断其枚举值是.none还是.some来确定它是否为nil。如果是.none,表示该Optional值为空;如果是.some,就可以通过访问其关联值获取具体的数值。

Optional的源码实现为:

@frozen public enum Optional<Wrapped> : ExpressibleByNilLiteral {
	case none
	case some(Wrapped)	 
}
  • Optioanl其实是标准库里的一个enum类型
  • 用标准库实现语言特性的典型
  • Optional.none 就是nil
  • Optional.some 就是包装了实际的值
  • 泛型属性 unsafelyUnwrapped
  • 理论上我们可以直接调用unsafelyUnwrapped获取可选项的值

Optional的解包方式

1. 可选项绑定(Optional Binding)

使用 if let 或者 guard let 语句来判断 Optional 变量是否有值,如果有值则解包,并将其赋值给一个非可选类型的变量。

var optionalValue: Int? = 10
// 可选项绑定
if let value = optionalValue {
 print("Optional value is \(value)")
} else {
 print("Optional value is nil")
}

可选项绑定语句有两个分支:if分支和else分支。如果 optionalValue 有值,if 分支就会被执行,unwrappedValue 就会被赋值为 optionalValue 的值。否则,执行 else 分支。

2. 强制解包(Forced Unwrapping)

使用!来获取一个不存在的可选值会导致运行错误,在使用!强制展开之前必须保证可选项中包含一个非nil的值

var optionalValue: Int? = 10
let nonOptionalValue = optionalValue! // 解包optionalValue值
print(nonOptionalValue) // 输出:10

需要注意的是,如果 Optional 类型的值为 nil,使用强制解包方式解包时,会导致运行时错误 (Runtime Error)。

3. 隐式解包(Implicitly Unwrapped Optionals)

在定义 Optional 类型变量时使用 ! 操作符,标明该变量可以被隐式解包。用于在一些情况下,我们可以确定该 Optional 变量绑定后不会为 nil,可以快捷的解包而不用每次都使用 ! 或者 if let 进行解包。

var optionalValue: Int! = 10
let nonOptionalValue = optionalValue // 隐式解包
print(nonOptionalValue) // 输出:10

需要注意的是,隐式解包的 Optional 如果 nil 的话,会导致 runtime error,所以使用隐式解包 Optional 需要确保其一直有值,否则还是需要检查其非 nil 后再操作。

总的来说,我们应该尽量避免使用强制解包,而是通过可选项绑定来处理 Optional 类型的值,在需要使用隐式解包的情况下,也要确保其可靠性和稳定性,尽量减少出现运行时错误的概率。

可选链(Optional Chaining)

是一种在 Optional 类型值上进行操作的方式,可以将多个 Optional 值的处理放在一起,并在任何一个 Optional 值为 nil 的时刻停止处理。

通过在 Optional 类型值后面跟上问号 ?,我们就可以使用可选链来访问该 Optional 对象的属性和方法。

class Person {
 var name: String
 var father: Person?
 init(name: String, father: Person?) {
 self.name = name
 self.father = father
 }
}
let father = Person(name: "Father", father: nil)
let son = Person(name: "Son", father: father)
// 可选链调用属性
if let fatherName = son.father?.name {
 print("Father's name is \(fatherName)") // 输出:Father's name is Father
} else {
 print("Son without father")
}
// 可选链调用方法
if let count = son.father?.name.count {
 print("Father's name has \(count) characters") // 输出:Father's name has 6 characters
} else {
 print("Son without father")
}

在上面的代码中,我们定义了一个 Person 类,并初始化了一个包含父亲(father)的儿子(son)对象。其中,父亲对象的father属性为nil。我们使用问号 ? 来标记 father 对象为 Optional 类型,以避免访问 nil 对象时的运行时错误。

需要注意的是,如果一个 Optional 类型的属性通过可选链调用后,返回值不是 Optional 类型,那么在可选链调用后,就不再需要加问号 ? 标记其为 Optional 类型了。

class Person {
 var name: String
 var age: Int?
 init(name: String, age: Int?) {
 self.name = name
 self.age = age
 }
 func printInfo() {
 print("\(name), \(age ?? 0) years old")
 }
}
let person = Person(name: "Tom", age: nil)
// 可选链调用方法后,返回值不再是 Optional 类型
let succeed = person.printInfo() // 输出:Tom, 0 years old

在上面的代码中,我们定义了一个 Person 类,并初始化了一个包含年龄(age)的人(person)对象。在可选链调用对象的方法——printInfo() 方法后,因为该方法返回值不是 Optional 类型,所以 returnedValue 就不再需要加问号 ? 标记其为 Optional 类型了。

Optional 的嵌套

将一个 Optional 类型的值作为另一个 Optional 类型的值的成员,形成嵌套的 Optional 类型。

var optionalValue: Int? = 10
var nestedOptionalValue: Int?? = optionalValue

在上面的代码中,我们定义了一个 Optional 类型的变量 optionalValue,并将其赋值为整型变量 10。然后,我们将 optionalValue 赋值给了另一个 Optional 类型的变量 nestedOptionalValue,形成了一个嵌套的 Optional 类型。

在处理嵌套的 Optional 类型时,我们需要特别小心,因为它们的使用很容易造成逻辑上的混淆和错误。为了解决这个问题,我们可以使用 Optional Binding 或者 ?? 操作符(空合并运算符)来降低 Optional 嵌套的复杂度。

var optionalValue: Int? = 10
var nestedOptionalValue: Int?? = optionalValue
// 双重可选项绑定
if let nestedValue = nestedOptionalValue, let value = nestedValue {
 print(value) // 输出:10
} else {
 print("Optional is nil")
}
// 空合并运算符
let nonOptionalValue = nestedOptionalValue ?? 0
print(nonOptionalValue) // 输出:Optional(10)

在上面的代码中,我们使用了双重可选项绑定来判断 nestedOptionalValue 是否可绑定,以及其嵌套的 Optional 值是否可绑定,并将该值赋值给变量 value,以避免 Optional 值的嵌套。另外,我们还可以使用 ?? 操作符(空合并运算符)来对嵌套的 Optional 值进行默认取值的操作。

需要注意的是,虽然我们可以使用 ?? 操作符来降低 Optional 值的嵌套,但在具体的实际应用中,我们应该在设计时尽量避免 Optional 值的嵌套,以便代码的可读性和维护性。如果对于某个变量来说,它的值可能为空,我们可以考虑使用默认值或者定义一个默认值的 Optional 值来代替嵌套的 Optional 类型。

学习 Swift,勿忘初心,方得始终。但要陷入困境时,也不要忘了最初的梦想和时代所需要的技能。

%s 个评论

要回复文章请先登录注册