Swift--反射机制--Handyjson源码分析--还原StructMetadata

所谓反射就是可以动态获取类型、成员信息,在运行时可以调用方法、属性等行为的特性。 在OC中很少强调反射概念,因为OC的Runtime要比其他语言中的反射强大很多。但是Swift是一门类型安全的语言,不支持OC那样直接操作,他的标准库仍然提供了反射机制来让我们访问成员信息。

Swift的反射机制是基于一个叫Mirror的机构体来实现的。你为具体的实例创建一个Mirror对象,然后就可以通过它查询这个实例了。

Mirror用法

  • 创建Mirror类的实例mirror,参数reflecting是要反射的实例。是Any类型。

let mirror = Mirror(reflecting: LGTeacher.self)

  • mirror可以查询里面的属性信息

看下面代码

for pro in mirror.children {
    // label value
    print("(pro.label):(pro.value)")
}
复制代码

image.png 上图寻找源码,mirror.children是一个元组的集合。

用Mirror递归实现jsonMap

看下面代码,我们通过Mirror一级一级从里到外回调来获取当前类LGTeacher的所有属性信息。

struct Student {
    let name:String
}

enum SexType {
    case Man, Woman
}

class LGTeacher {
    var age: Int = 18
    var student: [Student] = [Student(name:"乔布斯"),Student(name:"kuke")]
    let sex = SexType.Man
    
//    func teach(){
//        print("teach")
//    }
}

func test(_ mirrorObj: Any) -> Any{
    let mirror = Mirror(reflecting: mirrorObj)

    guard !mirror.children.isEmpty else{ return mirrorObj }

    var result: [String: Any] = [:]

    for child in mirror.children{
        if let key = child.label{
            result[key] = test(child.value)
        }else{
            print("No Keys")
        }
    }

    return result
}

var reslut = test(LGTeacher())
print(reslut)
复制代码

添加Error

//未完待续。。。

Mirror源码分析

//未完待续。。。

Handyjson源码分析

//未完待续。。。

还原StructMetadata

本节,我们需要还原StructMetadata,打印出Struct实例的属性类型和属性值,来了解反射原理。

通过Swfit源码还原结构

为了还原StructMetadata的结构,我们首先得从源码入手,找到Metadata.h,搜索TargetStructMetadata

struct TargetStructMetadata : public TargetValueMetadata<Runtime>
复制代码

发现TargetStructMetadata里面有TargetStructDescriptor但不是直接拥有,所以继续往下看,然后继续寻找 TargetValueMetadata

struct TargetValueMetadata : public TargetMetadata<Runtime> {
  using StoredPointer = typename Runtime::StoredPointer;
  TargetValueMetadata(MetadataKind Kind,
                      const TargetTypeContextDescriptor<Runtime> *description)
      : TargetMetadata<Runtime>(Kind), Description(description) {}

  /// An out-of-line description of the type.
  TargetSignedPointer<Runtime, const TargetValueTypeDescriptor<Runtime> * __ptrauth_swift_type_descriptor> Description;

  static bool classof(const TargetMetadata<Runtime> *metadata) {
    return metadata->getKind() == MetadataKind::Struct
      || metadata->getKind() == MetadataKind::Enum
      || metadata->getKind() == MetadataKind::Optional;
  }

  ConstTargetMetadataPointer<Runtime, TargetValueTypeDescriptor>
  getDescription() const {
    return Description;
  }

  typename Runtime::StoredSignedPointer
  getDescriptionAsSignedPointer() const {
    return Description;
  }
};
复制代码

这个里面我们发现了一个成员 Description, 再看 TargetMetadata

struct TargetMetadata {
  using StoredPointer = typename Runtime::StoredPointer;

  /// The basic header type.
  typedef TargetTypeMetadataHeader<Runtime> HeaderType;

  constexpr TargetMetadata()
    : Kind(static_cast<StoredPointer>(MetadataKind::Class)) {}
  constexpr TargetMetadata(MetadataKind Kind)
    : Kind(static_cast<StoredPointer>(Kind)) {}

#if SWIFT_OBJC_INTEROP
protected:
  constexpr TargetMetadata(TargetAnyClassMetadata<Runtime> *isa)
    : Kind(reinterpret_cast<StoredPointer>(isa)) {}
#endif

private:
  /// The kind. Only valid for non-class metadata; getKind() must be used to get
  /// the kind value.
  StoredPointer Kind;
复制代码

这里代码太长,我们只找到StoredPointer Kind;就行。 所以我们初步还原:TargetStructMetadata如下

struct TargetStructDescriptor {
    //未完
}
struct TargetStructMetadata {
    var kind: Int
    var typeDescriptor: UnsafeMutablePointer<TargetStructDescriptor>
}
复制代码

我们继续探索 TargetStructDescriptor 的结构

class TargetStructDescriptor final
: public TargetValueTypeDescriptor<Runtime>,
复制代码

image.png 一层层剥离我们最终还原出:TargetStructMetadata和TargetStructDescriptor

struct TargetStructDescriptor {
    var flags: Int32
    var parent: TargetRelativeDirectPointer<UnsafeRawPointer>
    var name: TargetRelativeDirectPointer<CChar>
    var accessFunctionPointer: TargetRelativeDirectPointer<UnsafeRawPointer>
    var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor>
    var NumFields: UInt32
    var FieldOffsetVectorOffset: UInt32
}

struct TargetStructMetadata {
    var kind: Int
    var typeDescriptor: UnsafeMutablePointer<TargetStructDescriptor>
}
复制代码

验证还原结构

接下来,我们验证一下我们的结构是否正确: 看下面的代码:

struct WYWPerson {
    var age:Int = 37
    let name:String = "wuyanwei"
    let sex = false
    var address = "wujiazui117-601"
    var birthday = ("2019", "10", "05")
}

var person = WYWPerson()

//个人认为可能WYWPerson的metadata还有其他东西,所以不强转成Any.Type。与 TargetStructMetadata的内存大小稍有区别
let ptr = unsafeBitCast(WYWPerson.self as Any.Type, to: UnsafeMutablePointer<TargetStructMetadata>.self)

let namePtr = ptr.pointee.typeDescriptor.pointee.name.getmeasureRelativeOffset()
print("current class name: \(String(cString: namePtr))")

let numFileds = ptr.pointee.typeDescriptor.pointee.numFields
print("当前结构体的属性的个数: \(numFileds)")

///
**current class name: WYWPerson**

**当前结构体的属性的个数: 5**

**======= start fetch filed ======**

**--- filed: age info begin ----**

**--- filed: name info begin ----**

**--- filed: sex info begin ----**

**--- filed: address info begin ----**

**--- filed: birthday info begin ----**
复制代码

结果打印和我们的结构是一致的,所以我们还原的结构没有问题

获取属性信息

可以发现,我们还原的属性fieldDescriptor他存储了结构体的属性信息,这里我们很容易打印出属性的名字和数据类型。 注意这里typeManglename存放的字符串是混写过后的字符串. 接着上面继续

let offsets = ptr.pointee.typeDescriptor.pointee.getFieldOffsets(UnsafeRawPointer(ptr).assumingMemoryBound(to: Double.self))
    for i in 0..<numFileds{
        let fieldDespritor = ptr.pointee.typeDescriptor.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.fieldName.getmeasureRelativeOffset()
        print("--- filed: \(String(cString: fieldDespritor)) info begin ----")
        
        //    let fieldOffset = offsets[Int(i)]
        //Int ,String
        let typeMangleName = ptr.pointee.typeDescriptor.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.mangledTypeName.getmeasureRelativeOffset()
        print("typeManglename:\(typeMangleName)")
        
        let genericVector = UnsafeRawPointer(ptr).advanced(by: ptr.pointee.typeDescriptor.pointee.genericArgumentOffset * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self)
        
        //HandyJSON
        let fieldType = _swift_getTypeByMangledNameInContext(typeMangleName, 256, genericContext: UnsafeRawPointer(ptr.pointee.typeDescriptor), genericArguments: UnsafeRawPointer(genericVector)?.assumingMemoryBound(to: Optional<UnsafeRawPointer>.self))
        
        print(fieldType as Any)
    }
///
current class name: WYWPerson
当前结构体的属性的个数: 5
======= start fetch filed ======
--- filed: age info begin ----
typeManglename:0x000000010000acb0
Optional(Swift.Int)
--- filed: name info begin ----
typeManglename:0x000000010000ad2a
Optional(Swift.String)
--- filed: sex info begin ----
typeManglename:0x000000010000ad34
Optional(Swift.Bool)
--- filed: address info begin ----
typeManglename:0x000000010000ad2a
Optional(Swift.String)
--- filed: birthday info begin ----
typeManglename:0x000000010000ad38
Optional((Swift.String, Swift.String, Swift.String))
复制代码

注意:这里我们调用了一个swift标准库的函数来获取属性的Type类型,_swift_getTypeByMangledNameInContext是swift的一个调用别名,通过下面的方式申明的

@_silgen_name("swift_getTypeByMangledNameInContext")
public func _swift_getTypeByMangledNameInContext(
    _ name: UnsafeMutablePointer<CChar>,
    _ nameLength: Int,
    genericContext: UnsafeRawPointer?,
    genericArguments:UnsafeRawPointer?) -> Any.Type?
复制代码

获取属性值

最后我们需要获取属性值了,这里有一个比较绕的方式,也还是借助上面一节HandyJSON的动态赋值思路:

  1. 首先通过内存相对偏移找到FieldOffsets的地址。这是一个buffer的首地址,里面依次存放着每个属性的当前值的地址(相对实例首地址的偏移地址)。
  2. 根据值的地址获取内存中的value。
  3. 协议中定义方法,将value转化为我们的Self类型,也就是属性类型,然后输出打印。

看下面代码

func getFieldOffsets(_ metadata: UnsafeRawPointer) -> UnsafePointer<Int32> {
        return UnsafeRawPointer(metadata.assumingMemoryBound(to: Int.self).advanced(by: numericCast(self.fieldOffsetVectorOffset))).assumingMemoryBound(to: Int32.self)
    }
复制代码
//HandJSON
    let fieldType = _swift_getTypeByMangledNameInContext(typeMangleName, 256, genericContext: UnsafeRawPointer(ptr.pointee.typeDescriptor), genericArguments: UnsafeRawPointer(genericVector)?.assumingMemoryBound(to: Optional<UnsafeRawPointer>.self))

    print(fieldType as Any)

    //比较难理解,HandJSON
    let type = unsafeBitCast(fieldType, to: Any.Type.self)
    let value = customCast(type: type)

    let instanceAddress = UnsafeRawPointer(withUnsafeMutablePointer(to: &person){$0})
    let fieldOffset = offsets[Int(i)]

    print("fieldType:\(type) \nfieldValue: \(value.get(from: instanceAddress.advanced(by: Int(fieldOffset)))) ")

    print("--- filed: \(String(cString: fieldDespritor)) info end ---- \n")
复制代码

实现总结

以上就实现了我们打印struct实例属性和属性信息。 我们的基本思路总结如下,

通过源码还原 TargetClassMetadata 的结构。 通过源码还原 TargetClassDescriptor 的结构。 通过内存映射 unsafeBitCast,将 WYWPerson 的实例的 metadata也就是WYWPerson.self格式化为我们还原的 StructMetadata 结构的指针。 从 StructMetadata 中读取到结构体名字, 从 fieldDescriptorfields 中找到每一项的属性名字,和属性类型(类型的string是混写的) 通过Handyjson中桥接的方式,获取到属性的值并打印。

全部代码

相关操作代码

struct TargetRelativeDirectPointer<Pointee>{
    var offset: Int32
    
    mutating func getmeasureRelativeOffset() -> UnsafeMutablePointer<Pointee>{
        let offset = self.offset
        
        return withUnsafePointer(to: &self) { p in
           return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: Pointee.self))
        }
    }
}

struct FieldDescriptor {
    var MangledTypeName: TargetRelativeDirectPointer<CChar>
    var Superclass: TargetRelativeDirectPointer<CChar>
    var kind: UInt16
    var fieldRecordSize: Int16
    var numFields: Int32
    var fields: FiledRecordBuffer<FieldRecord>
}

struct FieldRecord {
    var fieldRecordFlags: Int32
    var mangledTypeName: TargetRelativeDirectPointer<CChar>
    var fieldName: TargetRelativeDirectPointer<UInt8>
}

struct FiledRecordBuffer<Element>{
    var element: Element
    
    mutating func buffer(n: Int) -> UnsafeBufferPointer<Element> {
        return withUnsafePointer(to: &self) {
            let ptr = $0.withMemoryRebound(to: Element.self, capacity: 1) { start in
                return start
            }
            return UnsafeBufferPointer(start: ptr, count: n)
        }
    }
    
    mutating func index(of i: Int) -> UnsafeMutablePointer<Element> {
        return withUnsafePointer(to: &self) {
            return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self).advanced(by: i))
        }
    }
}
复制代码

还原struct的结构

struct TargetStructDescriptor {
    var flags: Int32
    var parent: TargetRelativeDirectPointer<UnsafeRawPointer>
    var name: TargetRelativeDirectPointer<CChar>
    var accessFunctionPointer: TargetRelativeDirectPointer<UnsafeRawPointer>
    var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor>
    var numFields: UInt32
    var fieldOffsetVectorOffset: UInt32

    func getFieldOffsets(_ metadata: UnsafeRawPointer) -> UnsafePointer<Int32> {
        return UnsafeRawPointer(metadata.assumingMemoryBound(to: Int.self).advanced(by: numericCast(self.fieldOffsetVectorOffset))).assumingMemoryBound(to: Int32.self)
    }
    
    var genericArgumentOffset: Int {
        return 2
    }
}

struct TargetStructMetadata {
    var kind: Int
    var typeDescriptor: UnsafeMutablePointer<TargetStructDescriptor>
}

@_silgen_name("swift_getTypeByMangledNameInContext")
public func _swift_getTypeByMangledNameInContext(
    _ name: UnsafeMutablePointer<CChar>,
    _ nameLength: Int,
    genericContext: UnsafeRawPointer?,
    genericArguments:UnsafeRawPointer?) -> Any.Type?
复制代码
struct WYWPerson {
    var age:Int = 37
    let name:String = "wuyanwei"
    let sex = false
    var address = "wujiazui117-601"
    var birthday = ("2019", "10", "05")
}

var person = WYWPerson()

//个人认为可能WYWPerson的metadata还有其他东西,所以不强转成Any.Type。与 TargetStructMetadata的内存大小稍有区别
let ptr = unsafeBitCast(WYWPerson.self as Any.Type, to: UnsafeMutablePointer<TargetStructMetadata>.self)

let namePtr = ptr.pointee.typeDescriptor.pointee.name.getmeasureRelativeOffset()
print("current class name: \(String(cString: namePtr))")

let numFileds = ptr.pointee.typeDescriptor.pointee.numFields
print("当前结构体的属性的个数: \(numFileds)")

print("======= start fetch filed ======")

let offsets = ptr.pointee.typeDescriptor.pointee.getFieldOffsets(UnsafeRawPointer(ptr))

for i in 0..<numFileds{
    let fieldDespritor = ptr.pointee.typeDescriptor.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.fieldName.getmeasureRelativeOffset()
    print("--- filed: \(String(cString: fieldDespritor)) info begin ----")

    let typeMangleName = ptr.pointee.typeDescriptor.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.mangledTypeName.getmeasureRelativeOffset()
    print("typeManglename:\(typeMangleName)")

    let genericVector = UnsafeRawPointer(ptr).advanced(by: ptr.pointee.typeDescriptor.pointee.genericArgumentOffset * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self)

    //HandJSON
    let fieldType = _swift_getTypeByMangledNameInContext(typeMangleName, 256, genericContext: UnsafeRawPointer(ptr.pointee.typeDescriptor), genericArguments: UnsafeRawPointer(genericVector)?.assumingMemoryBound(to: Optional<UnsafeRawPointer>.self))

    print(fieldType as Any)

    //比较难理解,HandJSON
    let type = unsafeBitCast(fieldType, to: Any.Type.self)
    let value = customCast(type: type)

    let instanceAddress = UnsafeRawPointer(withUnsafeMutablePointer(to: &person){$0})
    let fieldOffset = offsets[Int(i)]

    print("fieldType:\(type) \nfieldValue: \(value.get(from: instanceAddress.advanced(by: Int(fieldOffset)))) ")

    print("--- filed: \(String(cString: fieldDespritor)) info end ---- \n")
}

print("-----end")
复制代码
///result打印结果
current class name: WYWPerson
当前结构体的属性的个数: 5
======= start fetch filed ======
--- filed: age info begin ----
typeManglename:0x000000010000ad72
Optional(Swift.Int)
fieldType:Int 
fieldValue: 37 
--- filed: age info end ---- 

--- filed: name info begin ----
typeManglename:0x000000010000adf6
Optional(Swift.String)
fieldType:String 
fieldValue: wuyanwei 
--- filed: name info end ---- 

--- filed: sex info begin ----
typeManglename:0x000000010000adfa
Optional(Swift.Bool)
fieldType:Bool 
fieldValue: false 
--- filed: sex info end ---- 

--- filed: address info begin ----
typeManglename:0x000000010000adf6
Optional(Swift.String)
fieldType:String 
fieldValue: wujiazui117-601 
--- filed: address info end ---- 

--- filed: birthday info begin ----
typeManglename:0x000000010000adfe
Optional((Swift.String, Swift.String, Swift.String))
fieldType:(String, String, String) 
fieldValue: ("2019", "10", "05") 
--- filed: birthday info end ---- 

-----end
复制代码

Guess you like

Origin juejin.im/post/7053397610303848478