Swift进阶10:可选类型Optional & Equatable+Comparable协议

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,那么程序就会崩溃
image
  • 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)

age1nil的结果和强制解包一致,程序会崩溃

image

  • 官方对其的描述如下
image

这里的-O,是指target -> Build Setting -> Optimization Level设置成-O时,如果使用的是age1.unsafelyUnwrapped,则不检查这个变量是否为nil,

  • 1、设置Optimization LevelFastest, Smallest[-Os]
  • 2、edit Scheme -> Run -> Info -> Build Configuration改为release模式,然后再次运行发现,没有崩溃,与官方所说是一致的
image

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类型来说,系统默认实现了 ==

image

结构体类型

对于自定义的类型,如果想实现 ==,应该怎么办呢?

  • 如果像下面这样写,是会直接报错的
image
  • 可以通过遵循Equatable协议实现,如下所示
image

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

  • 查看SIL文件
image
  • 查看__derived_struct_equals方法的实现
image

image

Class类型

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

image

  • 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协议,重写 < 运算符
image

?? 空运算符

如果当前的变量为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?
image
  • 如果??20呢? 则 t的类型是Int
image
  • 如果??String呢? -- 会报错,??要求类型一致(跟是否是可选类型无关)
image

可选链

可选链 则意味着 允许在一个链上来访问当前的属性/方法,如下所示

//可选链
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,所以属性和方法都不会往下执行

image

总结

  • Optional的本质是enum,所以可以使用模式匹配来匹配Optional的值
  • Optional的解包方式有两种:
    • 1、强制解包:一旦为nil,程序会崩溃
    • 2、可选值绑定if let (只能在if流程的作用域内访问)、guard let
  • 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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容