swift底層探索 08-根據Mirror原理還原TargetStructMetadata結構


在swift源碼中有這樣一段代碼旱爆,可以看到所有的類型都是有元類型的,枚舉冲泥、結構體都被已經被定義了,下面就通過mirror來還原TargetStructMetadata結構驹碍。

1壁涎、Mirror

struct Teacher{
    var age = 18
    var name = "henry"
    var height = 1.8
    var hobby = "woman"
}

let t = Teacher()
let mirror = Mirror(reflecting: t)

for pro in mirror.children{
   print("\(pro.label):\(pro.value)")
}

為什么Mirror可以獲取到結構體的屬性名稱?

1.1 Mirror源碼

public struct Mirror {
   //初始化方法
  public init(reflecting subject: Any) {
    if case let customized as CustomReflectable = subject {
      self = customized.customMirror
    } else {
      self = Mirror(internalReflecting: subject)
    }
  }
}

1.2 Mirror初始化

extension Mirror {
  internal init(internalReflecting subject: Any,
              subjectType: Any.Type? = nil,
              customAncestor: Mirror? = nil)
{
// type(of:)獲取到subject的真實類型
    let subjectType = subjectType ?? _getNormalizedType(subject, type: type(of: subject))
// 獲取屬性大小
    let childCount = _getChildCount(subject, type: subjectType)
// 遍歷志秃,將屬性存儲到字典中
    let children = (0 ..< childCount).lazy.map({
      getChild(of: subject, type: subjectType, index: $0)
    })
    self.children = Children(children)
// 設置父類反射
    self._makeSuperclassMirror = {
      guard let subjectClass = subjectType as? AnyClass,
            let superclass = _getSuperclass(subjectClass) else {
        return nil
      }
      if let customAncestor = customAncestor {
        if superclass == customAncestor.subjectType {
          return customAncestor
        }
        if customAncestor._defaultDescendantRepresentation == .suppressed {
          return customAncestor
        }
      }
      return Mirror(internalReflecting: subject,
                    subjectType: superclass,
                    customAncestor: customAncestor)
    }
 // 獲取并解析顯示的樣式怔球,并設置Mirror的其他屬性
    let rawDisplayStyle = _getDisplayStyle(subject)
    switch UnicodeScalar(Int(rawDisplayStyle)) {
    case "c": self.displayStyle = .class
    case "e": self.displayStyle = .enum
    case "s": self.displayStyle = .struct
    case "t": self.displayStyle = .tuple
    case "\0": self.displayStyle = nil
    default: preconditionFailure("Unknown raw display style '\(rawDisplayStyle)'")
    }

    self.subjectType = subjectType
    self._defaultDescendantRepresentation = .generated
}
  • type(of:) 獲取到subject的真實類型

2、TargetStructMetadata結構

源碼中繼承關系比較復雜浮还,因為設計到繼承和一些模板類竟坛,所以只放出一張結構圖:


轉換為代碼形式:

struct StructMetaData{
    var kind : Int32
    var description : UnsafePointer<StructDescriptor>
}

struct StructDescriptor {
    let flags: Int32
    let parent: Int32
    var name: RelativePointer<CChar>
    var AccessFunctionPtr: RelativePointer<UnsafeRawPointer>
    var Fields: RelativePointer<FieldDescriptor>
    var NumFields: Int32
    var FieldOffsetVectorOffset: Int32
}

struct FieldDescriptor {
    var MangledTypeName: RelativePointer<CChar>
    var Superclass: RelativePointer<CChar>
    var kind: UInt16
    var fieldRecordSize: Int16
    var numFields: Int32
    var fields: FieldRecord //連續(xù)的存儲空間
}

struct FieldRecord {
    var Flags: Int32
    var MangledTypeName: RelativePointer<CChar>
    var FieldName: RelativePointer<CChar>
}


struct RelativePointer<T> {
    var offset:Int32
    
    mutating public func get() -> UnsafePointer<T> {
        let offSet = self.offset
        
        return withUnsafePointer(to: &self){ ptr in
            UnsafeRawPointer(ptr).advanced(by: numericCast(offSet)).assumingMemoryBound(to: T.self)
        }
    }
}

3、還原結構

根據源碼要先獲取元類指針,2種方式均可钧舌。swift指針使用起來有點難度(攤牌了我是個菜雞流码。。延刘。)swift底層探索 06 - 指針簡單使用

  1. withUnsafeMutablePointer
var type = Teacher.self
//指向元類指針的指針
let a = withUnsafeMutablePointer(to: &type) { ptr in
    UnsafeRawPointer(ptr).bindMemory(to: UnsafeMutablePointer<StructMetaData>.self, capacity: 1)
}
// 獲取元類指針
let metaPtr = a.pointee
  • 注意:a是 : 指向元類指針的指針
  1. unsafeBitCast按位強轉
let metaPtr = unsafeBitCast(Teacher.self as Any.Type, to: UnsafePointer<StructMetaData>.self)
  • unsafeBitCast需要兩個參數的內存大小相同漫试。必須使用:Teacher.self as Any.Type進行轉換,因為根據測試發(fā)現Teacher.self獲取內存大小為0(這個地方是真的坑)碘赖,如下圖:
Teacher.self as Any.Type 動作


根據sil查看發(fā)現 Teacher.self as Any.Type在底層的操作驾荣;

3.1 獲取結構體名

let structName = metaPtr.pointee.description.pointee.name.get()
let structNameStr = String(cString: structName)
print("獲取結構體名:\(structNameStr)")

輸出:


3.2 獲取內部屬性個數

let cnt = metaPtr.pointee.description.pointee.NumFields
print("獲取結構體屬性個數:\(cnt)")

輸出:


3.3 獲取所有屬性

let fieldsPtr = b.pointee.description.pointee.Fields.get()
for i in 0..<cnt {
    let record = withUnsafePointer(to: &fieldsPtr.pointee.fields) {
        UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: FieldRecord.self).advanced(by: numericCast(i)))
    }
    let recordNameStr = String(cString: record.pointee.FieldName.get())
    let manNameStr = String(cString: record.pointee.MangledTypeName.get())
    print("類型名:\(recordNameStr) ----  類型類型:\(manNameStr)")
}

輸出:


  • si : 其實就是Int
  • ss : 其實就是String
  • sd : 其實就是Double

demo下載-github

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末播掷,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子撼班,更是在濱河造成了極大的恐慌歧匈,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件砰嘁,死亡現場離奇詭異件炉,居然都是意外死亡,警方通過查閱死者的電腦和手機矮湘,發(fā)現死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門斟冕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缅阳,你說我怎么就攤上這事磕蛇。” “怎么了十办?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵秀撇,是天一觀的道長。 經常有香客問我向族,道長呵燕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任炸枣,我火速辦了婚禮虏等,結果婚禮上弄唧,老公的妹妹穿的比我還像新娘适肠。我一直安慰自己霍衫,他們只是感情好,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布侯养。 她就那樣靜靜地躺著敦跌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪逛揩。 梳的紋絲不亂的頭發(fā)上柠傍,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機與錄音辩稽,去河邊找鬼惧笛。 笑死,一個胖子當著我的面吹牛逞泄,可吹牛的內容都是我干的患整。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼喷众,長吁一口氣:“原來是場噩夢啊……” “哼各谚!你這毒婦竟也來了?” 一聲冷哼從身側響起到千,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤昌渤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后憔四,有當地人在樹林里發(fā)現了一具尸體膀息,經...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年了赵,在試婚紗的時候發(fā)現自己被綠了履婉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡斟览,死狀恐怖毁腿,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情苛茂,我是刑警寧澤已烤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站妓羊,受9級特大地震影響胯究,放射性物質發(fā)生泄漏。R本人自食惡果不足惜躁绸,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一裕循、第九天 我趴在偏房一處隱蔽的房頂上張望臣嚣。 院中可真熱鬧,春花似錦剥哑、人聲如沸硅则。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怎虫。三九已至,卻和暖如春困介,著一層夾襖步出監(jiān)牢的瞬間大审,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工座哩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留徒扶,地道東北人。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓根穷,卻偏偏與公主長得像姜骡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子缠诅,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內容