美文网首页
swift--Optional

swift--Optional

作者: Mjs | 来源:发表于2020-12-26 17:14 被阅读0次

    Optioanl 是通过 enum 实现的⼀个⾮常好的代表,这⾥我们可以通过阅读 Optional 的源码来看⼀下:

    @frozen
    public enum Optional<Wrapped>: ExpressibleByNilLiteral {
      // The compiler has special knowledge of Optional<Wrapped>, including the fact
      // that it is an `enum` with cases named `none` and `some`.
    
      /// The absence of a value.
      ///
      /// In code, the absence of a value is typically written using the `nil`
      /// literal rather than the explicit `.none` enumeration case.
      case none
    
      /// The presence of a value, stored as `Wrapped`.
      case some(Wrapped)
    ...
    }
    

    可以看到这⾥ Optional 的本质是⼀个 enum 。当前枚举接收⼀个泛型参数,⽽当前 some 的关联值 是当前的 Wrapped ,也就是说下⾯两种写法是完全等价的:

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

    同样的,既然是枚举,那么我们是可以通过模式匹配来匹配对应的值

    switch age{
    case nil:
        print("nil")
    case .some(10):
        print("10")
    case .some(_):
        print("other")
    }
    

    涉及到 Optional 我们就不得不⾯对⼀个问题:解包。因为当前的可选项其实是对我们的值做了包装, 当前不为 nil 的时候我们就需要从其中拿到我们要的值。
    ⽐较粗暴的⽅式是:强制解包,⽐如下⾯这段代码

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

    这样做的好处是省事,坏处是当前⼀旦 age 是nil ,那么应⽤程序就会崩溃
    这⾥⽐较好的做法就是使⽤ if let 或者 guard let .这⾥我们是通过可选项绑定的⽅式来判断当前 的可选项是否有值.

    if let tmp = age{
        print("\(tmp)")
    }else{
        print("nil")
    }
    
    guard let tmp = age else {
        print("age 为空")
        return
    }
    print(tmp)
    

    可以看到 gurad 后⾯的判断条件为 false 的时候会执⾏当前⼤括号⾥⾯的内容,反之执⾏后⾯的代码。gurad 在我们当前这句代码⾥⾯扮演的⻆⾊就是如果当前为空,我们就退出我们当前的递归调⽤。 其次,⼤家来看⼀个点:我们在使⽤if let 创建的内容当中 tmp 仅仅只能在当前 if 分⽀的⼤括号内访问到,⽽我们当前的guard 定义的 tmp 在当前⼤括号外部也是能访 问到的。

    Equatable

    Swift中的类型,可以通过遵循 Equatable 协议来使⽤相等运算符(==)和不等运算符(!=)来⽐较两 个值相等还是不相等。Swift 标准库中绝⼤多数的类型都默认实现了 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
        }
      }
    }
    

    如果我们对于⾃定的类型实现 ==,我们需要实现Equatable协议

    struct Teacher :Equatable{
        var age: Int
        var name: String
    }
    var t1 = Teacher(age: 18, name: "a")
    var t2 = Teacher(age: 18, name: "a")
    print(t1 == t2)
    

    我们实现Equatable协议就会自动帮我们实现==方法,我们可以通过sil文件查看

    struct Teacher : Equatable {
      @_hasStorage var age: Int { get set }
      @_hasStorage var name: String { get set }
      init(age: Int, name: String)
      @_implements(Equatable, ==(_:_:)) static func __derived_struct_equals(_ a: Teacher, _ b: Teacher) -> Bool
    }
    
    
    // static Teacher.__derived_struct_equals(_:_:)
    sil hidden @$s4main7TeacherV23__derived_struct_equalsySbAC_ACtFZ : $@convention(method) (@guaranteed Teacher, @guaranteed Teacher, @thin Teacher.Type) -> Bool {
    // %0                                             // users: %13, %6, %3
    // %1                                             // users: %15, %7, %4
    // %2                                             // user: %5
    bb0(%0 : $Teacher, %1 : $Teacher, %2 : $@thin Teacher.Type):
      debug_value %0 : $Teacher, let, name "a", argno 1 // id: %3
      debug_value %1 : $Teacher, let, name "b", argno 2 // id: %4
      debug_value %2 : $@thin Teacher.Type, let, name "self", argno 3 // id: %5
      %6 = struct_extract %0 : $Teacher, #Teacher.age // user: %8
      %7 = struct_extract %1 : $Teacher, #Teacher.age // user: %9
      %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
      cond_br %10, bb1, bb4                           // id: %11
    ///如果当前 age 的值⽐较成功了,就跳转 bb1 ,否则跳转 bb4 ,我们先来看 bb1 的代码分⽀:
    bb1:                                              // Preds: bb0
      %12 = metatype $@thin String.Type               // user: %18
      %13 = struct_extract %0 : $Teacher, #Teacher.name // users: %20, %18, %14
      retain_value %13 : $String                      // id: %14
      %15 = struct_extract %1 : $Teacher, #Teacher.name // users: %19, %18, %16
      retain_value %15 : $String                      // id: %16
      // function_ref static String.== infix(_:_:)
      %17 = function_ref @$sSS2eeoiySbSS_SStFZ : $@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
      %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
      %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
      %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
      return %32 : $Bool                              // id: %33
    } // end sil function '$s4main7TeacherV23__derived_struct_equalsySbAC_ACtFZ'
    

    然而如果是我们的类中就需要手动实现了

    static func == (lhs: Teacher, rhs: Teacher) -> Bool { 
     return lhs.age == rhs.age && lhs.name == rhs.name 
     }
    

    === 是⽤来检验两个对象是否是同⼀个实例对象是否是同⼀个

    空运算符(??)

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

    通过源码可以看到空运算符有两种

    
    @_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()
      }
    }
    

    根据空运算符的返回值来确定

    var age:Int? = nil
    var age1:Int? = 20
    var t = age ?? age1
    

    这个时候t的类型是不是可选类型由age1决定

    可选链

    class Teacher {
        var age: Int?
        var subject: Subject?
    }
    class Subject{
        var subName :String?
        func test(){}
    }
    var  t = Teacher()
    if let tmp = t.subject?.subName{
        
    }
    
    t.subject?.test()
    

    如果发现可选值为空的时候就不会往下执行了

    unsafelyUnwrapped

    unsafelyUnwrapped和强制解包效果一样

    t.subject!
    t.subject.unsafelyUnwrapped
    

    都不会检测是否为空

    类型转换(as)

    var age:Int = 10
    var age1 = age as AnyObject
    

    as可以转换为另一个类型
    as?:转换失败返回nil
    as!:转换失败直接奔溃

    相关文章

      网友评论

          本文标题:swift--Optional

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