美文网首页
Swift底层探索:Optional

Swift底层探索:Optional

作者: HotPotCat | 来源:发表于2021-01-09 22:14 被阅读0次

    Optional.Swift源码中,Optional定义如下。

    image.png
    • Optional是通过enum实现的,Optional本质是枚举。
    • 有两个casenilsome
    • 关联值就是传进来的值。

    本质上?是语法糖,下面的写法等价:

    var age: Int? = 10
    var age1:Optional<Int> = Optional(10)
    
    switch age {
        case .none:
            print("nil")
        case .some(10):
            print("\(10)")
        default:
            print("unKnown")
    }
    
    print(age == age1)
    
    10
    true
    

    强制解包

    var age: Int? = nil
    print(age!)
    

    如果age没有值!会直接crashFatal error: Unexpectedly found nil while unwrapping an Optional value:

    if let/guard let 可选绑定

    if let

    var age: Int? = nil
    //相当于把age变量的值拿出来给到temp
    if let temp = age {
        print("\(temp)")
    } else {
        print("nil")
    }
    

    temp作用域在if中。
    guard let

    var age: Int? = 10
    
    func test() -> Any {
        guard let temp = age else {
            return "error"
        }
        print(temp)
        return temp
    }
    
    test()
    
    10
    

    temp作用和guard同级,可以在guard外部访问到temp
    init

      @_transparent
      public init(_ some: Wrapped) { self = .some(some) }
     @_transparent
      public init(nilLiteral: ()) {
        self = .none
      }
    

    .some(some)给了self

    Equatable

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

    前面ageage1能够比较就是因为Optional遵循了Equatable协议重载了==运算符。

    var age: Int? = 10
    var age1:Optional<Int> = Optional(10)
    
    print(age == age1)
    

    如果我们自定义数据类型要进行==需要遵循Equatable协议:

    struct HotpotCat {
        var age: Int
        var name: String
    }
    
    extension HotpotCat: Equatable {}
    
    var hp = HotpotCat(age: 18, name: "hotpot")
    var hp1 = HotpotCat(age: 18, name: "hotpot")
    print(hp == hp1)
    
    true
    

    HotpotCat中我们并没有实现==方法,编译器帮我们默认实现了,看下SIL实现:

    image.png
    __derived_struct_equals实现:
    // static HotpotCat.__derived_struct_equals(_:_:)
    sil hidden @static main.HotpotCat.__derived_struct_equals(main.HotpotCat, main.HotpotCat) -> Swift.Bool : $@convention(method) (@guaranteed HotpotCat, @guaranteed HotpotCat, @thin HotpotCat.Type) -> Bool {
    // %0 "a"                                         // users: %13, %6, %3
    // %1 "b"                                         // users: %15, %7, %4
    // %2 "self"                                      // user: %5
    bb0(%0 : $HotpotCat, %1 : $HotpotCat, %2 : $@thin HotpotCat.Type):
      //两个变量分别给到a和b(HotpotCat结构体)
      debug_value %0 : $HotpotCat, let, name "a", argno 1 // id: %3
      debug_value %1 : $HotpotCat, let, name "b", argno 2 // id: %4
      debug_value %2 : $@thin HotpotCat.Type, let, name "self", argno 3 // id: %5
      //结构体中取出age(Int)
      %6 = struct_extract %0 : $HotpotCat, #HotpotCat.age // user: %8
      %7 = struct_extract %1 : $HotpotCat, #HotpotCat.age // user: %9
      //Int结构体中取出value
      %8 = struct_extract %6 : $Int, #Int._value      // user: %10
      %9 = struct_extract %7 : $Int, #Int._value      // user: %10
      //比较
      %10 = builtin "cmp_eq_Int64"(%8 : $Builtin.Int64, %9 : $Builtin.Int64) : $Builtin.Int1 // user: %11
      //相等bb1,否则bb4
      cond_br %10, bb1, bb4                           // id: %11
    
    bb1:                                              // Preds: bb0
      %12 = metatype $@thin String.Type               // user: %18
      //HotpotCat结构体中取出name
      %13 = struct_extract %0 : $HotpotCat, #HotpotCat.name // users: %20, %18, %14
      retain_value %13 : $String                      // id: %14
      %15 = struct_extract %1 : $HotpotCat, #HotpotCat.name // users: %19, %18, %16
      retain_value %15 : $String                      // id: %16
      // function_ref static String.== infix(_:_:)
      //String自己的==方法
      %17 = function_ref @static Swift.String.== infix(Swift.String, Swift.String) -> Swift.Bool : $@convention(method) (@guaranteed String, @guaranteed String, @thin String.Type) -> Bool // user: %18
      %18 = apply %17(%13, %15, %12) : $@convention(method) (@guaranteed String, @guaranteed String, @thin String.Type) -> Bool // user: %21
      release_value %15 : $String                     // id: %19
      release_value %13 : $String                     // id: %20
      //比较是否相同
      %21 = struct_extract %18 : $Bool, #Bool._value  // user: %22
      cond_br %21, bb2, bb3                           // id: %22
    
    bb2:                                              // Preds: bb1
      //构造bool值-1
      %23 = integer_literal $Builtin.Int1, -1         // user: %24
      %24 = struct $Bool (%23 : $Builtin.Int1)        // user: %25
      br bb5(%24 : $Bool)                             // id: %25
    
    bb3:                                              // Preds: bb1
      //构造bool值0
      %26 = integer_literal $Builtin.Int1, 0          // user: %27
      %27 = struct $Bool (%26 : $Builtin.Int1)        // user: %28
      br bb5(%27 : $Bool)                             // id: %28
    
    bb4:                                              // Preds: bb0
       //构造bool值0
      %29 = integer_literal $Builtin.Int1, 0          // user: %30
      %30 = struct $Bool (%29 : $Builtin.Int1)        // user: %31
      br bb5(%30 : $Bool)                             // id: %31
    
    // %32                                            // user: %33
    bb5(%32 : $Bool):                                 // Preds: bb2 bb3 bb4
      //返回上面构造的bool值
      return %32 : $Bool                              // id: %33
    } // end sil function 'static main.HotpotCat.__derived_struct_equals(main.HotpotCat, main.HotpotCat) -> Swift.Bool'
    
    • 1.取两个结构体ab
    • 2.比较a.ageb.age
    • 3.取a.nameb.name,并且调用String自己的判等方法
    • 4.构造Int1类型的数据,相等-1,不相等0

    底层通过Int1类型的数据生成Bool类型:

    %29 = integer_literal $Builtin.Int1, 0          // user: %30
    %30 = struct $Bool (%29 : $Builtin.Int1)        // user: %31
    

    所以也可以简单说Swift中的Bool-1(true)0(false)
    Bool.Swift源码:

    image.png

    上面我们分析的是结构体,那么如果HotpotCatclass呢?

    image.png
    可以看到这个时候需要我们自己实现==方法了。
    class HotpotCat {
        var age: Int
        var name: String
        init(age: Int, name: String) {
            self.age = age
            self.name = name
        }
    }
    
    extension HotpotCat: Equatable {
        static func == (lhs: HotpotCat, rhs: HotpotCat) -> Bool {
            return lhs.age == rhs.age && lhs.name == rhs.name
        }
    }
    
    var hp = HotpotCat.init(age: 18, name: "hotpot")
    var hp1 = HotpotCat.init(age: 18, name: "hotpot")
    var hp2 = hp
    //比较值相等
    print(hp == hp1)
    //比较地址
    print(hp === hp1)
    print(hp === hp2)
    
    true
    false
    true
    

    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
    }
    

    Comparable自动实现了Equatable

    struct HotpotCat {
        var age: Int
        var name: String
    }
    
    extension HotpotCat: Comparable {
        static func < (lhs: HotpotCat, rhs: HotpotCat) -> Bool {
            return lhs.age < rhs.age && lhs.name < lhs.name
        }
    }
    
    var hp = HotpotCat(age: 21, name: "hotpot1")
    var hp1 = HotpotCat(age: 20, name: "hotpot")
    print(hp > hp1)
    

    在这里结构体必须实现<,编译器会通过<自动实现其它运算符。当然实现>,不实现<不行,由于源码中其它运算法是通过<来实现的:

    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 {
    
      @inlinable
      public static func > (lhs: Self, rhs: Self) -> Bool {
        return rhs < lhs
      }
      
      @inlinable
      public static func <= (lhs: Self, rhs: Self) -> Bool {
        return !(rhs < lhs)
      }
    
      @inlinable
      public static func >= (lhs: Self, rhs: Self) -> Bool {
        return !(lhs < rhs)
      }
    }
    

    ??

    ??运算符在optional源码中有两个:

    @_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()
      }
    }
    
    @_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?,这里为什么有两个?看个例子就明白了

    var age: Int? = nil
    var age2: Int? = 10
    var temp = age ?? age2
    
    print(temp)
    

    可以看到??的返回值是和age2相关的。age2是什么类型就返回什么类型,age2是可选类型就返回T?,否则返回T

    可选链

    可选链是一个调用和查询可选属性、方法和下标的过程,它可能为 nil 。如果可选项包含值,属性、方法或者下标的调用成功;如果可选项是 nil ,属性、方法或者下标的调用会返回 nil 。多个查询可以链接在一起,如果链中任何一个节点是 nil ,那么整个链就会得体地失败。

    class Hotpot {
        var name: String?
        var cat: Cat?
        func test() {
            print("test")
        }
    }
    
    class Cat {
        var catName: String?
        func ctTest() {
            print("ctTest")
        }
    }
    
    var cat: Cat? = nil
    var hp: Hotpot? = Hotpot()
    let temp = hp?.cat?.catName
    hp?.test()
    hp?.cat?.ctTest()
    
    test
    

    unsafelyUnwrapped

    optional源码中还有一个unsafelyUnwrapped,实现如下:

     /// The wrapped value of this instance, unwrapped without checking whether
      /// the instance is `nil`.
      ///
      /// The `unsafelyUnwrapped` property provides the same value as the forced
      /// unwrap operator (postfix `!`). However, in optimized builds (`-O`), no
      /// check is performed to ensure that the current instance actually has a
      /// value. Accessing this property in the case of a `nil` value is a serious
      /// programming error and could lead to undefined behavior or a runtime
      /// error.
      ///
      /// In debug builds (`-Onone`), the `unsafelyUnwrapped` property has the same
      /// behavior as using the postfix `!` operator and triggers a runtime error
      /// if the instance is `nil`.
      ///
      /// The `unsafelyUnwrapped` property is recommended over calling the
      /// `unsafeBitCast(_:)` function because the property is more restrictive
      /// and because accessing the property still performs checking in debug
      /// builds.
      ///
      /// - Warning: This property trades safety for performance.  Use
      ///   `unsafelyUnwrapped` only when you are confident that this instance
      ///   will never be equal to `nil` and only after you've tried using the
      ///   postfix `!` operator.
      @inlinable
      public var unsafelyUnwrapped: Wrapped {
        @inline(__always)
        get {
          if let x = self {
            return x
          }
          _debugPreconditionFailure("unsafelyUnwrapped of nil optional")
        }
      }
    

    unsafelyUnwrapped!强制解包一样。

    var age: Int? = 20
    print(age.unsafelyUnwrapped)
    

    区别在于:

    image.png
    对于第2点,在release -Onone模式下验证下:
    image.png
    可以看到age.unsafelyUnwrapped0

    as as? as!

    var age: Int = 10
    
    var age1 = age as Any
    print(age1)
    
    var age2 = age as? Double
    print(age2)
    
    var age3 = age as! Double
    print(age3)
    
    10
    nil
    SwiftClouse was compiled with optimization - stepping may behave oddly; variables may not be available.
    
    image.png

    访问控制

    OC中很少用到访问控制,Swift中主要针对其它源文件和模块对代码的访问控制。

    Swift为代码的实体提供个五个不同的访问级别。这些访问级别和定义实体的源文件相关,并且也和源文件所属的模块相关。如果不指明访问级别的话,代码中的所有实体都会默认为 internal 级别。大多数情况下不需要明确指定访问级别。访问控制权限

    private

    将实体的使用限制于封闭声明中。当一些细节仅在单独的声明中使用时,使用 private 访问隐藏特定功能的实现细节。

    仅在声明的作用域有效


    image.png

    fileprivate

    将实体的使用限制于当前定义源文件中。当一些细节在整个文件中使用时,使用 file-private 访问隐藏特定功能的实现细节。

    仅限制在当前定义的源文件中
    这个时候我们在另外一个文件新建一个SubHotpotCat:

    class SubHotpotCat: HotpotCat {
        fileprivate var name:String = "subHotpotCat"
    }
    
    fileprivate func test() {
        let subHotpot = SubHotpotCat()
        print(subHotpot.name)
    }
    

    HotpotCat文件中访问subHotpot.name:

    image.png

    internal

    允许实体被定义模块中的任意源文件访问,但不能被该模块之外的任何源文件访问。通常在定义应用程序或是框架的内部结构时使用。

    默认的访问级别,允许定义模块中的任意源文件访问,但不能被该模块之外的任何源文件访问。
    这里的模块是指:一个框架或者应用程序。主要指import关键字导入的模块

    import Foundation
    

    上面的Foundation就是模块。三方库也是模块。

    public

    允许实体被定义模块中的任意源文件访问,同样可以被另一模块的源文件通过导入该定义模块来访问。在指定框架的公共接口时,通常使用 open 或 public 访问。

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

    open

    最不受限制的访问级别

    open 访问仅适用于类和类成员,它与 public 访问区别如下:

    • public 访问,或任何更严格的访问级别的类,只能在其定义模块中被继承。
    • public 访问,或任何更严格访问级别的类成员,只能被其定义模块的子类重写。
    • open 类可以在其定义模块中被继承,也可在任何导入定义模块的其他模块中被继承。
    • open 类成员可以被其定义模块的子类重写,也可以被导入其定义模块的任何模块重写。

    显式地标记类为 open 意味着你考虑过其他模块使用该类作为父类对代码的影响,并且相应地设计了类的代码。

    相关文章

      网友评论

          本文标题:Swift底层探索:Optional

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