美文网首页Swift
Swift Optional & Equatable+Compa

Swift Optional & Equatable+Compa

作者: 深圳_你要的昵称 | 来源:发表于2021-02-07 17:18 被阅读0次

    前言

    本篇文章主要讲解一下,之前用到过但是没有仔细分析的几个知识点 👉 Optional可选类型、Equatable协议和Comparable协议,然后会补充关于解包的相关场景,最后大致说明一下Swift中的访问控制权限

    一、Optional

    首先来看看Optional可选类型,这个关键字在Swift中主要是用来处理值缺失的情况,有以下2种情况👇

    1. 有值,且等于x
    2. 没有值

    1.1 源码分析

    先来看看关于Optional的底层源码👇

    上图可见,Optional本质是enum,当前枚举接收一个泛型参数Wrapped,当前Some的关联值就是当前的Wrapper,所以下面两种写法完全等价👇

    var age: Int? = 10
    // 等价于
    var age1: Optional<Int> = Optional(5)
    

    1.2 匹配模式

    跟我们之前讲解Swift 枚举一样,接着我们来看看Optinal的匹配模式,示例👇

    //1、声明一个可选类型的变量
    var age: Int? = 10
    //2、通过模式匹配来匹配对应的值
    switch age {
        case nil:
            print("age 是个空值")
        case .some(let val):
            print("age的值是\(val)")
    }
    
    // 或者这样写
    switch age {
        case nil:
            print("age 是个空值")
        case .some(10):
            print("age的值是10")
        default:
            print("unKnow")
    }
    

    1.3 Optional解包

    解包的意思就是获取值,因为Optional可选项是对做了一层“包装”,我们得拆开这个“包装”才能拿到解包有2种方式👇

    1. 强制解包 👉 好处是省事,坏处是解包的值是nil会导致程序crash崩溃
    var age: Int? = nil
    print(age!)
    

    运行崩溃👇

    1. 通过可选项绑定:判断当前的可选项是否有值
      • if - let 👉 如果有值,则会进入if流程
      • guard - let 👉 如果为nil,则会进入else流程
    var age: Int? = nil
    // 方式一
    if let age = age{
        //如果age不为nil,则打印
        print(age)
    }
    
    // 方式二
    func test() {
        guard let tmp = age else {
            print("age为nil")
            return
        }
        print(tmp)
    }
    

    if-let 可以配合else使用,也可以直接在if中添加return,表示后续代码都是else场景下,节省了else的{},提高代码可读性👇

    var age: Int? = nil
    
    // 可以配合return使用, 省了else的{}
    if let value = age {
        print("value \(value)")
        return
    }
    
    if let value1 = age {
        print("value1 \(value1)")
    } else {
        print("age 为 nil")
    }
    

    注意:

    1. 使用return关键字,必须将该代码块放入函数体
    2. guard-let是具备守护功能,它必须搭配return使用,排除异常情况,守护guard(作用域)之后的代码。
    if-letguard-let的使用建议

    实际开发过程中,我们希望代码从上至下清晰表达主线流程

    • guard-let一般用于处理非主线异常情况,直接return出去,守护主线代码。
    • if-let一般用于处理主线重要场景
    • if-let创建的内容,仅在if 作用域内可访问;guard-let创建的内容,是供guard 作用域外访问。

    二、Equatable协议

    Swift中的类型,可以通过遵循Equatable协议来使用相等运算符(==)不等运算符(!=)

    绝大多数类型默认实现Equatable协议`👇

    上图可见,Int String Double Float均遵循了。

    2.1 Optional中的Equatable协议

    继续回到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
        }
      }
    }
    

    2.2 自定义类型实现Equatable协议

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

    2.2.1自定义struct类型

    首先我们看看结构体类型👇

    struct LGTeacher {
        var age: Int
        var name: String
    }
    
    var t = LGTeacher(age: 17, name: "Kody")
    var t1 = LGTeacher(age: 18, name: "Cooci")
    
    print(t == t1)
    

    然后遵循Equatable协议实现👇

    上图可知,结构体直接实现Equatable协议,即可比较,为什么呢?我们去看SIL代码👇

    系统默认实现了==方法,然后调用的是__derived_struct_equals去做的比较👇

    2.2.2自定义Class类型

    接下来我们看看类classEquatable协议实现,将LGPerson改为class👇

    报错提示缺少初始化方法,那么添加👇

    接着报错,未实现Equatable协议,那么实现👇

    属性是Optional类型

    我们查看SIL代码👇

    比较调用的是Optinal的==方法👇

    2.2.3 结构体嵌套类

    上图可知,结构体嵌套类,即值类型中包含引用类型的实例,必须实现引用类型Equatable (==)函数,查看SIL👇

    2.2.4 =====

    在我们平常的开发中,经常会用到==,但Swift中还有个===,它俩之间有什么区别呢?👇

    1. == 用来检验值是否相等,需要遵循Equatable协议
    2. ===是用来检验两个对象是否是同一个实例对象 👉内存地址是否相等,不支持值类型,仅支持类实例(AnyObject?)使用。
    @inlinable public func === (lhs: AnyObject?, rhs: AnyObject?) -> Bool
    @inlinable public func !== (lhs: AnyObject?, rhs: AnyObject?) -> Bool
    

    三、Comparable协议

    Comparable协议继承Equatable协议,并多支持了其他比较方式 👉 其中的运算符有:< 、<=、>=、> 、...、..<

    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>
        ......
    }
    

    3.1 结构体重写 < 运算符

    修改上面的例子👇

    3.2 ??运算符

    ??运算符也是经常被使用的,表示 👉 如果当前的变量值为nil时,可以在??运算符后面再给个默认值,例如👇

    var age: Int? = nil
    //?? 等价于 if let / guard let
    print(age ?? 20)
    

    我们先看看??函数的声明👇

    接着我们去Swift源码中搜索?? <T>(optional:,找到实现👇

    上图可知 👉 ??只有两种类型,一种是T,一种是T?,主要是与 ?? 后面的返回值有关,简单来说 👉 ??后是什么类型,??返回的就是什么类型👇

    ??后面是age1,而age1的类型是Int?,所以t的类型是 Int?

    如果??后面是18,Int类型 👉 t1的类型就是Int类型

    如果??后面是String呢? 👉 会报错,??要求类型一致,跟是否Optional类型无关。

    补充:可选链

    可选链 👉 允许在一个链上来访问当前的属性/方法,示例👇

    class LGSubject {
        var subjectName: String?
        func test(){print("test")}
    }
    
    class LGTeacher{
        var name: String?
        var subject: LGSubject?
        
    }
    
    var s = LGSubject()
    var t = LGTeacher()
    
    //可选链访问属性
    if let tmp = t.subject?.subjectName{
        print("tmp: \(tmp)")
    }else{
        print("tmp为nil")
    }
    //可选链访问方法
    t.subject?.test()
    

    修改赋值👇

    四、解包

    接着我们来讲解一下关于解包的几个关键字。

    4.1 unsafelyUnwrapped

    unsafelyUnwrappedOptional.swift中的对可选型变量的一个解包,它解包的结果强制解包的结果一样的,例如👇

    // age有值
    var age: Int? = 30
    print(age!)
    print(age.unsafelyUnwrapped)
    
    
    // age1是nil
    var age1: Int? = nil
    print(age1!)
    print(age1.unsafelyUnwrapped)
    

    age是nil时的结果和强制解包一致 👉 程序会崩溃。接下来我们看看unsafelyUnwrapped的声明👇

    这里的-O,是指Build Setting中的配置选项Optimization Level设置成-O,如果是Fastest, Smallest[-Os],即我们使用release模式👇

    再次运行👇

    4.2 as、 as? 和 as!

    1. as 将类型转换为其他类型
    var age: Int = 10
    
    var age1 = age as Any
    print(age1)
    
    var age2 = age as AnyObject
    print(age2)
    
    // 打印结果
    10
    10
    
    1. as? 将类型转换为 其他可选类型
    var age: Int = 10
    //as? 不确定类型是Double,试着转换下,如果转换失败,则返回nil
    var age3 = age as? Double
    print(age3)
    
    // 打印结果
    nil
    

    注意: 此时的age3的类型是Double? 👇

    1. as! 👉 强制转换其他类型
    var age: Int = 10
    //as! 强制转换为其他类型
    var age4 = age as! Double
    print(age4)
    
    使用建议
    • 如果能确定类型,使用as!即可。例如👇
    // 常规使用
    var age: Any = 10
    func test(_ age: Any) -> Int{
        return (age as! Int) + 1
    }
    print(test(age))
    
    // 打印结果
    11
    
    • 如果不能确定的,使用as?即可

    五、访问控制权限

    最后,我们再来了解一下Swift中的与访问控制权限相关的几个关键字。

    5.1 private

    访问级别仅在当前定义的作用域内有效(单例中使用过👇)。

    class LGTeacher{
        static let shareInstance = LGTeacher()
        private init(){}
    }
    var t = LGTeacher.shareInstance
    

    5.2 filePrivate

    访问限制仅限制在当前定义的源文件中

    fileprivate class LGPartTimeTeacher: LGTeacher{
        var partTime: Double?
        init(_ partTime: Double) {
            super.init()
            self.partTime = partTime
        }
    }
    

    如果上述代码是在Teacher.swift中,那么在main.swift无法访问LGPartTimeTeacher
    再看下面这个示例👇

    上图报错的原因 👉 let pt 默认的权限是 Internal的(接下来会讲),而LGPartTimeTeacher的访问权限是fileprivate的,Internal权限大于fileprivate,系统不允许这样,所以提示错误。

    修改
    • 方式一 👉 需要使用private / fileprivate修饰pt👇
    private let pt = LGPartTimeTeacher(10.0)
    //或者
    fileprivate let pt = LGPartTimeTeacher(10.0)
    
    • 方式二 👉 如果直接定义在方法中的,可以不用访问权限修饰符
    func test(){
        let pt = LGPartTimeTeacher(10.0)
    }
    

    5.3 Internal

    默认访问级别,允许定义模块中的任意源文件访问,但不能被该模块之外的任何源文件访问(例如 import Foundation,其中Foundation就是一个模块)。

    // main.swift中
    import Foundation
    class LGTeacher{
        init(){}
    }
    let t = LGTeacher()
    
    // 另一个custom.swift中
    import AppKit
    //访问main.swift中t,报错:Expressions are not allowed at the top level
    print(t)
    

    上图报错原因 👉 t在main.swift文件中的默认权限是Internal,只能在同一个模块内使用,其属于Foundation模块,而custom.swift文件中不能调用t,是因为其属于AppKit模块,与Foundation不是同一个模块,所以不能访问。

    5.4 public

    开放式访问,使我们能够在其定义模块任何源文件中使用代码,并且可以从另一个源文件访问源文件。但是只能在定义的模块中继承和子类重写

    5.5 open

    最不受限制的访问级别,可以在任意地方任意模块间继承、定义、重写

    public & open区别

    这也是我们经常容易混淆的点,区别主要是👇

    public不可继承,open可继承!

    总结

    本篇文章相对于前面所讲解的Mirror枚举闭包知识点而言,很简单。主要介绍了Optional可选类型的概念,底层的实现和使用场景,然后顺着Optional的匹配模式,讲解了Equatable+Comparable协议,顺便总结了解包的几种方式,最后讲解了Swift不同于OC的知识点 👉 访问控制权限,都是些大家在平常开发中碰到的知识点,希望大家掌握。

    相关文章

      网友评论

        本文标题:Swift Optional & Equatable+Compa

        本文链接:https://www.haomeiwen.com/subject/secgtltx.html