Optional分析
Swift中的可选类型(Optional),用于处理值缺失的情况,有以下两种情况
有值,且等于x- 没有值
这点可以通过Swift源码->Optional.swift源码(CMD+P,搜索Optional)源码来印证
@frozen
public enum Optional<Wrapped>: ExpressibleByNilLiteral {
......
//为nil
case none
......
//有值
case some(Wrapped)
......
}
- 通过源码可知,
Optional的本质是enum,当前枚举接收一个泛型参数Wrapped,当前Some的关联值就是当前的Wrapper,下面两种写法完全等价
var age: Int? = 10
等价于
var age1: Optional<Int> = Optional(10)
Optional使用模式匹配
-
【Optional使用模式匹配】:既然
Optional的本质是枚举,那么也可以使用模式匹配来匹配对应的值,如下所示
//1、声明一个可选类型的变量
var age: Int? = nil
//2、通过模式匹配来匹配对应的值
switch age {
case nil:
print("age 是个空值")
case .some(let val):
print("age的值是\(val)")
}
Optional解包
【Optional解包】:因为是Optional类型,当我们需要从其中拿到我们想要的值时,需要对其进行解包,因为当前的可选项是对值做了一层包装的,有以下两种方式:
- 1、
强制解包:好处是省事,坏处是一旦解包的值是nil,那么程序就会崩溃

- 2、通过
可选项绑定:判断当前的可选项是否有值-
if let:如果有值,则会进入if流程 -
guard let:如果为nil,则会进入else流程
-
//可选项解包
var age: Int? = 10
//强制解包
print(age!)
//if let 可选绑定
if let value = age {
print(value)
}
//guard let 可选绑定
guard let tmp = age else {
print("age值为nil")
return
}
print(tmp)
可选项绑定总结
- 1、使用
if let创建的内容当中value仅仅只能在当前if分支的大括号内访问 - 2、使用
guard let定义的tmp在当前大括号外部也是能访问的
unsafelyUnwrapped
这个和强制解包的内容是一致的,如下所示
//unsafelyUnwrapped 和强制解包内容是一致的
var age: Int? = 30
print(age!)
print(age.unsafelyUnwrapped)
//如果age1是nil
var age1: Int? = nil
//print(age1!)
print(age1.unsafelyUnwrapped)
age1是nil的结果和强制解包一致,程序会崩溃

- 官方对其的描述如下

这里的-O,是指target -> Build Setting -> Optimization Level设置成-O时,如果使用的是age1.unsafelyUnwrapped,则不检查这个变量是否为nil,
- 1、设置
Optimization Level为Fastest, Smallest[-Os] - 2、
edit Scheme -> Run -> Info -> Build Configuration改为release模式,然后再次运行发现,没有崩溃,与官方所说是一致的

Equatable协议
在下面的例子中,可以通过==判断两个可选项是否相等,原因是因为Optinal在底层遵循了Equatable协议
//Equatable协议
var age: Int? = 10
var age1: Optional<Int> = Optional(10)
age == age1
- 继续回到
Optional源码中,可以看到Optional遵循了Equatable协议
extension Optional: Equatable where Wrapped: Equatable {
......
@inlinable
public static func ==(lhs: Wrapped?, rhs: Wrapped?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l == r
case (nil, nil):
return true
default:
return false
}
}
}
Swift标准库中的类型
在Swift中的类型,可以通过遵循Equatable协议来使用相等运算符(==)和不等运算符(!=)来比较两个值相等还是不相等,Swift标准库中绝大多数类型都默认实现了Equatable协议
例如下面的例子,对于Int类型来说,系统默认实现了 ==

结构体类型
对于自定义的类型,如果想实现 ==,应该怎么办呢?
- 如果像下面这样写,是会直接报错的

- 可以通过
遵循Equatable协议实现,如下所示

为什么呢?其根本原因是因为遵守了Equatable协议,系统默认帮我们实现了==方法
- 查看
SIL文件

- 查看
__derived_struct_equals方法的实现


Class类型
如果像Struct那么写,会报错,提示需要自己实现Equatable协议的方法

- class中手动实现
Equatable协议的方法
//class实现Equatable协议
class HTTeacher: Equatable {
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
static func == (lhs: HTTeacher, rhs: HTTeacher) -> Bool {
return lhs.age == rhs.age && lhs.name == rhs.name
}
}
var t1 = HTTeacher.init(age: 18, name: "name")
var t2 = HTTeacher.init(age: 19, name: "name")
print(t1 == t2)
区分 == vs ===
-
==相当于 equal to,用于判断两个值是否相等 -
===是用来判断两个对象是否是同一个实例对象(即内存地址指向是否一致)
Comparable协议
除了Equatable,还有Comparable协议,其中的运算符有:< 、<=、>=、> 、...、..<等
public protocol Comparable : Equatable {
static func < (lhs: Self, rhs: Self) -> Bool
static func <= (lhs: Self, rhs: Self) -> Bool
static func >= (lhs: Self, rhs: Self) -> Bool
static func > (lhs: Self, rhs: Self) -> Bool
}
extension Comparable {
public static func ... (minimum: Self, maximum: Self) -> ClosedRange<Self>
......
}
Struct重载 < 运算符
- 以
struct为例,遵循Comparable协议,重写 < 运算符

?? 空运算符
如果当前的变量为nil,可以在??返回一个nil时的默认值
- 下面例子的打印结果是什么?
//?? 空运算符
var age: Int? = nil
//?? 等价于 if let / guard let
print(age ?? 20)
//---打印结果
//20
- 进入
Optional源码,查看??实现
<!--返回T-->
@_transparent//空运算符
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T)
rethrows -> T {
switch optional {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
<!--返回T?-->
@_transparent
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?)
rethrows -> T? {
switch optional {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
从源码中分析,??只有两种类型,一种是T,一种是T?,主要是与 ?? 后面的返回值有关(即简单来说,就是??后是什么类型,??返回的就是什么类型),如下所示
- ??后面是
age1,而age1的类型是Int?,所以t的类型是Int?

- 如果
??是20呢? 则t的类型是Int

- 如果
??是String呢? -- 会报错,??要求类型一致(跟是否是可选类型无关)

可选链
可选链 则意味着 允许在一个链上来访问当前的属性/方法,如下所示
//可选链
class HTTeacher{
var name: String?
var subject: HTSubject?
}
class HTSubject {
var subjectName: String?
func test() { print("test") }
}
var s = HTSubject()
var t = HTTeacher()
//可选链访问属性
if let tmp = t.subject?.subjectName {
print("tmp不为nil")
} else {
print("tmp为nil")
}
//可选链访问方法
t.subject?.test()
运行结果如下,因为t.subject为nil,所以属性和方法都不会往下执行

总结
-
Optional的本质是enum,所以可以使用模式匹配来匹配Optional的值 - Optional的
解包方式有两种:- 1、
强制解包:一旦为nil,程序会崩溃 - 2、
可选值绑定:if let(只能在if流程的作用域内访问)、guard let
- 1、
-
Equatable协议:- 对于
swift标准库中的绝大部分类型都默认实现了Equatable协议 - 对于自定义
Struct类型,仅需要遵守Equatable协议 - 对于自定义
class类型,除了需要遵守Equatable协议,还需要自己实现Equatable协议的方法
- 对于
- 区分 == vs ===
-
==相当于 equal to,用于判断两个值是否相等 -
===是用来判断两个对象是否是同一个实例对象(即内存地址指向是否一致)
-
-
Comparable协议:- 对于自定义类型,需要遵循
Comparable协议,并重写运算符
- 对于自定义类型,需要遵循
-
??空运算符- ??只有两种类型,一种是
T,一种是T?,主要是与 ?? 后面的返回值有关(即简单来说,就是??后是什么类型,??返回的就是什么类型)
- ??只有两种类型,一种是
- 可选链:允许在一个链上来访问当前的属性/方法,如果为
nil,则不会执行?后的属性/方法 -
unsafelyUnwrapped:与强制解包类似,但是如果项目中设置target -> Build Setting -> Optimization Level设置成-O时,如果使用的是age.unsafelyUnwrapped,则不检查这个变量是否为nil