美文网首页swift进阶
swift进阶十:Mirror源码解析

swift进阶十:Mirror源码解析

作者: markhetao | 来源:发表于2020-12-20 21:40 被阅读0次

swift进阶 学习大纲

上一节,我们简单体验Mirror反射机制。

本节,通过源码,深入探索Mirror反射 💪

  1. 初始化
  2. 属性类型
  3. 属性个数
  4. 属性值

  • Mirror(反射):
    可以动态获取类型成员变量,在运行时可以调用方法属性等行为的特性

回顾Mirror简单使用

class HTPerson {
   var name = "ht"
   var age = 18
}

let p = HTPerson()
let mirror = Mirror(reflecting: p.self)

for pro in mirror.children {
   print("\(pro.label ?? ""):\(pro.value)")
}
  • 打印结果:
image.png

1. 初始化

我们知道,OC中通过Runtime能轻松获取属性方法协议等,但swiftMirror是如何获取到属性类型个数的呢?

  • 带着这些疑问,我们在swift源码中搜索Mirror.swift

    image.png
  • 我们搜索找到初始化方法public init(reflecting subject: Any),从这里开始我们的探索:

    image.png
  • 首先,我们分析一下如何通过subject获取类型

2. 类型获取

  • 搜索_getNormalizedType:
    image.png

注意:

  • 源码分析时,首先分析结构,结构是由继承链上的所有属性共同决定的。

  • 函数只是所有属性服务的。所以我们的重心,就是先找到结构,尝试重写结构,能使用自己的结构完整读取到相应的内容证明源码分析的结果是正确的。

  • 在这个过程,顺道看一眼函数,在属性操作时,回头分析哪些函数操作了它,是如何实现的。
    这样才能完整梳理清楚。(当然,得花大量时间反复研究才行)

  • 根据源码探索,以struct类型为例,可知structMetaData结构为:
image.png
  • 按照structMetadata格式,尝试读取struct的类型:
    (get:仿照源码中RelativeDirectPointer (相对位置指针)进行偏移,获取真实内存值)
struct StructMetadata {
    var kind:        Int
    var description: UnsafeMutablePointer<StructMetadataDesc>
}

struct StructMetadataDesc {
    var flags:              UInt32
    var parent:             UInt32  // 展示用Uint32代替,实际是相同大小的结构体,
    var name:               RelativeDirectPointer<CChar>
    // . . .  (当前研究获取属性类型,后面的属性先不管)
}

struct RelativeDirectPointer<T>{
    var offset: Int32
    
    // 偏移offset位置,获取内容指针
    mutating func get() -> UnsafeMutablePointer<T> {
        let offset = self.offset
        // withUnsafePointer获取指针
        return withUnsafePointer(to: &self) { p in
            // UnsafeMutablePointer 返回T类型对象的指针
            // UnsafeRawPointer将p指针转换为未知类型
            // numericCast将offset转换为偏移单位数
            // advanced进行内存偏移
            // assumingMemoryBound绑定指针为T类型
            return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: T.self))
        }
        
    }
}

struct HTStruct {
    var age = 18
}

// 读取将HTStuct指针内容,赋值给StructMetadata  (unsafeBitCast: 通过字节读取)
let p = unsafeBitCast(HTStruct.self as Any.Type, to: UnsafeMutablePointer<StructMetadata>.self)

// 读取当前name的内容指针
let namePtr = p.pointee.description.pointee.name.get()

// name是CChar类型,转为字符串输出
print(String(cString: namePtr))
  • 成功获取HTStruct类型:
    image.png

3. 属性个数

  • 回到初始化方法,可以看到是通过_getChildCount获取属性个数:

    image.png
  • 仿写代码,

struct StructMetadata {
    var kind:        Int
    var description: UnsafeMutablePointer<StructMetadataDesc>
}

struct StructMetadataDesc {
    var flags:                   UInt32
    var parent:                  UInt32  // 展示用Uint32代替,实际是相同大小的结构体,
    var name:                    RelativeDirectPointer<CChar>  // 不在乎具体类型,就先用UnsafeRawPointer
    var accessFunctionPtr:       RelativeDirectPointer<UnsafeRawPointer>  // 不在乎具体类型,就先用UnsafeRawPointer
    var fields:                  RelativeDirectPointer<UnsafeRawPointer>  // 不在乎具体类型,就先用UnsafeRawPointer
    var numFields:               UInt32  // 属性个数
    var fieldOffsetVectorOffset: UInt32
}

struct RelativeDirectPointer<T>{
    var offset: Int32

    // 偏移offset位置,获取内容指针
    mutating func get() -> UnsafeMutablePointer<T> {
        let offset = self.offset
        // withUnsafePointer获取指针
        return withUnsafePointer(to: &self) { p in
            // UnsafeMutablePointer 返回T类型对象的指针
            // UnsafeRawPointer将p指针转换为未知类型
            // numericCast将offset转换为偏移单位数
            // advanced进行内存偏移
            // assumingMemoryBound绑定指针为T类型
            return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: T.self))
        }

    }
}

struct HTStruct {
    var age = 18
}

// 读取将HTStuct指针内容,赋值给StructMetadata  (unsafeBitCast: 通过字节读取)
let p = unsafeBitCast(HTStruct.self as Any.Type, to: UnsafeMutablePointer<StructMetadata>.self)

// 读取当前name的内容指针
let namePtr = p.pointee.description.pointee.name.get()
let count = p.pointee.description.pointee.numFields

// name是CChar类型,转为字符串输出
print(String(cString: namePtr))
print("HTStruct属性个数:\(count)")
  • 打印结果:


    image.png
  • 修改结构体属性个数后:

    image.png

成功的重写结构,拿到属性个数,下面,我们来分析属性值的获取

4. 属性值

  • 属性值的获取,也是在children获取时得到。我们循着路径,找到对应类型的impl,读取description里面的fields,分析fields内部结构,发现每个属性是以FieldRecord格式进行存储。 存放的位置在fields的最后。有几个属性就会新增几条FieldRecord记录,可通过内存地址偏移获取所有属性名
image.png
// 结构体类型
struct StructMetadata {
    var kind:        Int
    var description: UnsafeMutablePointer<StructMetadataDesc>
}

// 结构体类型的描述
struct StructMetadataDesc {
    var flags:                   UInt32
    var parent:                  UInt32  // 展示用Uint32代替,实际是相同大小的结构体,
    var name:                    RelativeDirectPointer<CChar>  // 不在乎具体类型,就先用UnsafeRawPointer
    var accessFunctionPtr:       RelativeDirectPointer<UnsafeRawPointer>  // 不在乎具体类型,就先用UnsafeRawPointer
    var fields:                  RelativeDirectPointer<FieldDescription>  // 记录所有属性内容
    var numFields:               UInt32   // 属性个数
    var fieldOffsetVectorOffset: UInt32
}

// 记录结构体内所有属性的结构
struct FieldDescription {
    var MangledTypeName:         RelativeDirectPointer<CChar>
    var Superclass:              RelativeDirectPointer<CChar>
    var Kind:                    UInt16
    var FieldRecordSize:         UInt16
    var NumFields:               UInt32
    var fields:                  FieldRecord // 连续存储空间 (有几个数据,就会在后面添加几个记录,通过内存平移读取)
}

// 每个属性的内容
struct FieldRecord {
    var flag:                    Int32
    var mangledTypeName:         RelativeDirectPointer<CChar>
    var fieldName:               RelativeDirectPointer<CChar>   // 属性名称
}

// 相对位移指针
struct RelativeDirectPointer<T>{
    var offset: Int32

    // 偏移offset位置,获取内容指针
    mutating func get() -> UnsafeMutablePointer<T> {
        let offset = self.offset
        // withUnsafePointer获取指针
        return withUnsafePointer(to: &self) { p in
            // UnsafeMutablePointer 返回T类型对象的指针
            // UnsafeRawPointer将p指针转换为未知类型
            // numericCast将offset转换为偏移单位数
            // advanced进行内存偏移
            // assumingMemoryBound绑定指针为T类型
            return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: T.self))
        }

    }
}

struct HTStruct {
    var age = 18
    var name = "ht"
}

// 读取将HTStuct指针内容,赋值给StructMetadata  (unsafeBitCast: 通过字节读取)
let p = unsafeBitCast(HTStruct.self as Any.Type, to: UnsafeMutablePointer<StructMetadata>.self)

// 读取当前name的内容指针
let namePtr = p.pointee.description.pointee.name.get()
let count = p.pointee.description.pointee.numFields
print("类型:\(String(cString: namePtr))") // name是CChar类型,转为字符串输出
print("属性个数:\(count)")

// 单独读取第一个属性名
var fields = p.pointee.description.pointee.fields.get()
let fieldRecord1Name = fields.pointee.fields.fieldName.get()
print(String(cString: fieldRecord1Name))

// 读取所有记录
print("----读取所有属性名----")
(0..<count).forEach { index in
    let recordPtr = withUnsafePointer(to: &fields.pointee.fields) {
        return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: FieldRecord.self).advanced(by: Int(index)))
    }
    print(String(cString: recordPtr.pointee.fieldName.get()))
}

print("----dump----")
dump(HTStruct()) // 相似的实现方式
  • 本文从struct结构开始入手分析,通过源码。从初始化方法开始,找到属性类型属性个数属性值。成功还原相应数据格式。初步窥探Mirror的内部实现。

  • 源码的世界,都是大师级制作。每次拜读和分析,都能到一些思维方式。本文仅仅作为一个引路篇,更多有意思的东西,还需要我们自己慢慢探索。

相关文章

网友评论

    本文标题:swift进阶十:Mirror源码解析

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