Swift底層探索:Optional

Optional.Swift源碼中,Optional定義如下。

image.png

  • Optional是通過(guò)enum實(shí)現(xiàn)的,Optional本質(zhì)是枚舉。
  • 有兩個(gè)casenilsome。
  • 關(guān)聯(lián)值就是傳進(jìn)來(lái)的值。

本質(zhì)上?是語(yǔ)法糖,下面的寫(xiě)法等價(jià):

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

強(qiáng)制解包

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

如果age沒(méi)有值!會(huì)直接crash。Fatal error: Unexpectedly found nil while unwrapping an Optional value:

if let/guard let 可選綁定

if let

var age: Int? = nil
//相當(dāng)于把a(bǔ)ge變量的值拿出來(lái)給到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同級(jí),可以在guard外部訪問(wèn)到temp。
init

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

.some(some)給了self。

Equatable

Optional 遵循了Equatable 協(xié)議,重寫(xiě)了==方法

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能夠比較就是因?yàn)?code>Optional遵循了Equatable協(xié)議重載了==運(yùn)算符。

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

print(age == age1)

如果我們自定義數(shù)據(jù)類(lèi)型要進(jìn)行==需要遵循Equatable協(xié)議:

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中我們并沒(méi)有實(shí)現(xiàn)==方法,編譯器幫我們默認(rèn)實(shí)現(xiàn)了,看下SIL實(shí)現(xiàn):

image.png

__derived_struct_equals實(shí)現(xiàn):

// 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):
  //兩個(gè)變量分別給到a和b(HotpotCat結(jié)構(gòu)體)
  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
  //結(jié)構(gòu)體中取出age(Int)
  %6 = struct_extract %0 : $HotpotCat, #HotpotCat.age // user: %8
  %7 = struct_extract %1 : $HotpotCat, #HotpotCat.age // user: %9
  //Int結(jié)構(gòu)體中取出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結(jié)構(gòu)體中取出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
  //構(gòu)造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
  //構(gòu)造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
   //構(gòu)造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
  //返回上面構(gòu)造的bool值
  return %32 : $Bool                              // id: %33
} // end sil function 'static main.HotpotCat.__derived_struct_equals(main.HotpotCat, main.HotpotCat) -> Swift.Bool'
  • 1.取兩個(gè)結(jié)構(gòu)體ab
  • 2.比較a.ageb.age
  • 3.取a.nameb.name,并且調(diào)用String自己的判等方法
  • 4.構(gòu)造Int1類(lèi)型的數(shù)據(jù),相等-1,不相等0。

底層通過(guò)Int1類(lèi)型的數(shù)據(jù)生成Bool類(lèi)型:

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

所以也可以簡(jiǎn)單說(shuō)Swift中的Bool-1(true)0(false)
Bool.Swift源碼:

image.png

上面我們分析的是結(jié)構(gòu)體,那么如果HotpotCatclass呢?

image.png

可以看到這個(gè)時(shí)候需要我們自己實(shí)現(xiàn)==方法了。

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自動(dòng)實(shí)現(xiàn)了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)

在這里結(jié)構(gòu)體必須實(shí)現(xiàn)<,編譯器會(huì)通過(guò)<自動(dòng)實(shí)現(xiàn)其它運(yùn)算符。當(dāng)然實(shí)現(xiàn)>,不實(shí)現(xiàn)<不行,由于源碼中其它運(yùn)算法是通過(guò)<來(lái)實(shí)現(xiàn)的:

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

??

??運(yùn)算符在optional源碼中有兩個(gè):

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

一個(gè)返回T,一個(gè)返回T?,這里為什么有兩個(gè)?看個(gè)例子就明白了

var age: Int? = nil
var age2: Int? = 10
var temp = age ?? age2

print(temp)

可以看到??的返回值是和age2相關(guān)的。age2是什么類(lèi)型就返回什么類(lèi)型,age2是可選類(lèi)型就返回T?,否則返回T。

可選鏈

可選鏈?zhǔn)且粋€(gè)調(diào)用和查詢可選屬性、方法和下標(biāo)的過(guò)程,它可能為 nil 。如果可選項(xiàng)包含值,屬性、方法或者下標(biāo)的調(diào)用成功;如果可選項(xiàng)是 nil ,屬性、方法或者下標(biāo)的調(diào)用會(huì)返回 nil 。多個(gè)查詢可以鏈接在一起,如果鏈中任何一個(gè)節(jié)點(diǎn)是 nil ,那么整個(gè)鏈就會(huì)得體地失敗。

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源碼中還有一個(gè)unsafelyUnwrapped,實(shí)現(xiàn)如下:

 /// 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!強(qiáng)制解包一樣。

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

區(qū)別在于:

image.png

對(duì)于第2點(diǎn),在release -Onone模式下驗(yàn)證下:
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

訪問(wèn)控制

OC中很少用到訪問(wèn)控制,Swift中主要針對(duì)其它源文件和模塊對(duì)代碼的訪問(wèn)控制。

Swift為代碼的實(shí)體提供個(gè)五個(gè)不同的訪問(wèn)級(jí)別。這些訪問(wèn)級(jí)別和定義實(shí)體的源文件相關(guān),并且也和源文件所屬的模塊相關(guān)。如果不指明訪問(wèn)級(jí)別的話,代碼中的所有實(shí)體都會(huì)默認(rèn)為 internal 級(jí)別。大多數(shù)情況下不需要明確指定訪問(wèn)級(jí)別。訪問(wèn)控制權(quán)限

private

將實(shí)體的使用限制于封閉聲明中。當(dāng)一些細(xì)節(jié)僅在單獨(dú)的聲明中使用時(shí),使用 private 訪問(wèn)隱藏特定功能的實(shí)現(xiàn)細(xì)節(jié)。

僅在聲明的作用域有效


image.png

fileprivate

將實(shí)體的使用限制于當(dāng)前定義源文件中。當(dāng)一些細(xì)節(jié)在整個(gè)文件中使用時(shí),使用 file-private 訪問(wèn)隱藏特定功能的實(shí)現(xiàn)細(xì)節(jié)。

僅限制在當(dāng)前定義的源文件中
這個(gè)時(shí)候我們?cè)诹硗庖粋€(gè)文件新建一個(gè)SubHotpotCat:

class SubHotpotCat: HotpotCat {
    fileprivate var name:String = "subHotpotCat"
}

fileprivate func test() {
    let subHotpot = SubHotpotCat()
    print(subHotpot.name)
}

HotpotCat文件中訪問(wèn)subHotpot.name:

image.png

internal

允許實(shí)體被定義模塊中的任意源文件訪問(wèn),但不能被該模塊之外的任何源文件訪問(wèn)。通常在定義應(yīng)用程序或是框架的內(nèi)部結(jié)構(gòu)時(shí)使用。

默認(rèn)的訪問(wèn)級(jí)別,允許定義模塊中的任意源文件訪問(wèn),但不能被該模塊之外的任何源文件訪問(wèn)。
這里的模塊是指:一個(gè)框架或者應(yīng)用程序。主要指import關(guān)鍵字導(dǎo)入的模塊

import Foundation

上面的Foundation就是模塊。三方庫(kù)也是模塊。

public

允許實(shí)體被定義模塊中的任意源文件訪問(wèn),同樣可以被另一模塊的源文件通過(guò)導(dǎo)入該定義模塊來(lái)訪問(wèn)。在指定框架的公共接口時(shí),通常使用 open 或 public 訪問(wèn)。

開(kāi)放式訪問(wèn),能夠在其定義模塊的任何源文件中使用代碼,并且可以從另外一個(gè)源文件訪問(wèn)源文件。
只能在定義的模塊中繼承和子類(lèi)重寫(xiě)。

open

最不受限制的訪問(wèn)級(jí)別

open 訪問(wèn)僅適用于類(lèi)和類(lèi)成員,它與 public 訪問(wèn)區(qū)別如下:

  • public 訪問(wèn),或任何更嚴(yán)格的訪問(wèn)級(jí)別的類(lèi),只能在其定義模塊中被繼承。
  • public 訪問(wèn),或任何更嚴(yán)格訪問(wèn)級(jí)別的類(lèi)成員,只能被其定義模塊的子類(lèi)重寫(xiě)。
  • open 類(lèi)可以在其定義模塊中被繼承,也可在任何導(dǎo)入定義模塊的其他模塊中被繼承。
  • open 類(lèi)成員可以被其定義模塊的子類(lèi)重寫(xiě),也可以被導(dǎo)入其定義模塊的任何模塊重寫(xiě)。

顯式地標(biāo)記類(lèi)為 open 意味著你考慮過(guò)其他模塊使用該類(lèi)作為父類(lèi)對(duì)代碼的影響,并且相應(yīng)地設(shè)計(jì)了類(lèi)的代碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容