swift进阶

swift进阶十:Mirror源码解析

2020-12-20  本文已影响0人  markhetao

swift进阶 学习大纲

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

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

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

回顾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是如何获取到属性类型个数的呢?

2. 类型获取

注意:

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

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

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

image.png
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))

3. 属性个数

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

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

4. 属性值

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()) // 相似的实现方式
上一篇 下一篇

猜你喜欢

热点阅读