swift--Optional

2020-12-26  本文已影响0人  Mjs

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!:转换失败直接奔溃

上一篇下一篇

猜你喜欢

热点阅读