Swift-根据Mirror原理还原StructMetadata

697 阅读5分钟

1、Mirror-反射机制

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

class XLMirror {    
    var age = 20    
    var name = "放得开"
}
var t = XLMirror()
let mirror = Mirror.init(reflecting: t)
for pro in mirror.children { 
   print("\(String(describing: pro.label)):\(pro.value)")
}
//代码的输出结果是:
//Optional("age"):20
//Optional("name"):放得开

Mirror在Swift源码中的本质是一个结构体:

  • subjectType: 表示类型,被反射主体的类型
  • children: 子元素集合
  • displayStyle:显示类型,基本类型为nil 枚举值: struct, class, enum, tuple, optional, collection, dictionary, set

这是Mirror最常用的初始化方法,接受一个Any类型的参数,if case的写法是用来判断当前的subject是否遵循了CustomReflectable协议,如果遵循了可以直接调用

public init(reflecting subject: Any) { 
    if case let customized as CustomReflectable = subject {     
         self = customized.customMirror    
    } else {      
        self = Mirror(internalReflecting: subject)    
    } 
}

CustomReflectable协议的具体用法:

class XLMirror:CustomReflectable {
    //遵循CustomReflectable协议    
    var age = 20    
    var name = "放得开"
    
    //实现CustomReflectable协议的方法    
    var customMirror: Mirror {        
        let info = KeyValuePairs<String, Any>.init(dictionaryLiteral: ("age",age), ("name",name))        
        let mirror = Mirror.init(self, children: info, displayStyle: .class, ancestorRepresentation: .generated)        
        return mirror    
    }
}
var t = XLMirror()

最直观的区别就在于 LLDB输出的时候:

2、TargetStructMetadata的数据结构

2.1、还原StructMetadata数据结构

想要还原TargetStructMetadata,首先要了解TargetStructMetadata在源码中的数据结构,下面来源码中看一下:打开源码搜索TargetStructMetadata 可以定位到 Metadata.h 这个文件可以看到 TargetStructMetadata

是一个继承于TargetValueMetadatastruct,而且TargetValueMetadata这个结构体提供了一个TargetValueTypeDescriptor类型的Description

继续向上寻找 可以看到 TargetValueMetadata 继承于 TargetMetadata,而 TargetMetadata提供了一个 StoredPointer类型的Kind

那么StructMetadataswift中的代码就是这样的

struct TargetStructMetadata {    
    var kind: Int32    
    var Description:UnsafeRawPointer
}

2.2、还原TargetStructDescriptor数据结构

接下来看一下TargetValueTypeDescriptor这条线 TargetValueTypeDescriptor这个结构体继承自TargetValueTypeDescriptor而且本身提供了两个值 NumFieldsFieldOffsetVectorOffset

继续向上寻找可以看到TargetValueTypeDescriptor继承自TargetValueTypeDescriptor,再找一层就能看到TargetValueTypeDescriptor继承自TargetTypeContextDescriptorTargetTypeContextDescriptor这个类有TargetRelativeDirectPointer这个类型的三个值:NameAccessFunctionPtrFields,其中Fields里存储的FieldDescriptor信息我们也需要还原一下

继续向上寻找会发现TargetTypeContextDescriptor继承自 TargetContextDescriptor的结构体,这个结构体提供了FlagsParent这两个值

据此可以得到TargetStructDescriptor的数据结构

struct TargetStructDescriptor{    
    var Flags: Int32    
    var Parent: Int32    
    var Name: TargetRelativeDirectPointer<CChar>    
    var AccessFunctionPtr:TargetRelativeDirectPointer<UnsafeRawPointer>    
    var Fields: TargetRelativeDirectPointer<FieldDescriptor>    
    var NumFields: UInt32    
    var FieldOffsetVectorOffset: UInt32
}

2.3、还原FieldDescriptor数据结构

进入到FieldDescriptor可以看到有MangledTypeNameSuperclassKindFieldRecordSizeNumFieldsFieldRecord这个类型的数据也需要还原

据此可知FieldDescriptor的数据结构为

struct FieldDescriptor{    
    var MangledTypeName: Int32    
    var Superclass: UnsafeRawPointer    
    var Kind: UInt16    
    var FieldRecordSize:UInt16        var NumFields: UnsafeRawPointer    
    var FieldRecord: Int32
}

2.4、还原FieldRecord数据结构

进入到FieldRecord类可以看到FlagsMangledTypeNameFieldName

据此可知FieldRecord数据结构

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

2.5、还原TargetRelativeDirectPointer

首先要搞清楚RelativeDirectPointer是什么,看一下源码

T是传进来的类型,Offset 就是 int32_t 的类型,在 Swift 中引⽤⼀个 Object 有两种情况: 

  • 当前 Object 的引⽤是⼀个 absolute 指针,就像我们的对象⼀样 

  • 当前指针存储的是相对引⽤,相对引⽤指的是当前引⽤的内存地址到当前对象的内存地址的距离

    以上我们根据这个思路还原数据结构

    typealias Offset = Int32 struct TargetRelativeDirectPointer{//相对地址信息 var offset: Offset

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

    }

最终的StructMetadata的数据结构应该是这样的

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 TargetStructMetadata {
    var kind: Int32
    var Description:UnsafeMutablePointer<TargetStructDescriptor>
}

struct TargetStructDescriptor{
    var Flags: Int32
    var Parent: Int32
    var Name: TargetRelativeDirectPointer<CChar>
    var AccessFunctionPtr:TargetRelativeDirectPointer<UnsafeRawPointer>
    var Fields: TargetRelativeDirectPointer<FieldDescriptor>
    var NumFields: UInt32
    var FieldOffsetVectorOffset: Int32
    
    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 FieldDescriptor{
    var MangledTypeName: TargetRelativeDirectPointer<CChar>
    var Superclass: TargetRelativeDirectPointer<CChar>
    var Kind: UInt16
    var FieldRecordSize:Int16
    var NumFields: Int32
    var FieldRecord: FiledRecordBuffer<FieldRecord>
}
struct FieldRecord {
    var Flags: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))
        }
    }
}

以上就是StructMetadataswift数据结构,接下来验证码一下:

//首先创建一个结构体
struct XLCountry {    
    var name = "中国"    
    var english = "China"    
    var people = 140000
}
//然后按位存放在 TargetStructMetadata类型的指针
let ptr = unsafeBitCast(XLCountry.self as Any.Type, to: UnsafeMutablePointer<TargetStructMetadata>.self)
//拿到XLCountry的结构体的名字
let ptrName = ptr.pointee.Description.pointee.Name.getmeasureRelativeOffset()
//输出
print(String(cString: ptrName))//结果:XLStutend
print("当前类属性的个数: \(numFields)")//输出结果:3

 再拿到XLCountry各属性的值以及属性的类型

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

for i in 0 ..< numFields {
    let structFields = ptr.pointee.Description.pointee.Fields.getmeasureRelativeOffset().pointee.FieldRecord.index(of: Int(i)).pointee.FieldName.getmeasureRelativeOffset()//获取XLCountry的属性的名字
    print("---------start filed \(String(cString: structFields))  ---------")
    let typeMangleName = ptr.pointee.Description.pointee.Fields.getmeasureRelativeOffset().pointee.FieldRecord.index(of: Int(i)).pointee.MangledTypeName.getmeasureRelativeOffset()//获取到属性的类型的名称(这个名称是混写后的)
    
    let genericVector = UnsafeRawPointer(ptr).advanced(by: ptr.pointee.Description.pointee.genericArgumentOffset * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self)
    
//    const void * _Nullable swift_getTypeByMangledNameInContext(
//                            const char * _Nullable typeNameStart,
//                            int typeNameLength,
//                            const void * _Nullable context,
//                            const void * _Nullable const * _Nullable genericArgs);
    let fieldType = swift_getTypeByMangledNameInContext(//C标准库里的方法,可以直接调用
        typeMangleName,
        256,
        UnsafeRawPointer(ptr.pointee.Description),
        UnsafeRawPointer(genericVector)?.assumingMemoryBound(to: Optional<UnsafeRawPointer>.self)
    )
    let type = unsafeBitCast(fieldType, to: Any.Type.self)
    let value = customCast(type: type)
    
    let instanceAddress = UnsafeRawPointer(withUnsafeMutablePointer(to: &country){$0})
    
    var offset = offsets[Int(i)]
    print("fieldType: \(type)")
    let val = value.get(from: instanceAddress.advanced(by: Int(offset)))
    print("fieldValue: \(val)")
    
    print("---------end filed ---------")
}

输出结果
---------start filed name  ---------
fieldType: String
fieldValue: 中国
---------end filed ---------
---------start filed english  ---------
fieldType: String
fieldValue: China
---------end filed ---------
---------start filed people  ---------
fieldType: Int
fieldValue: 140000
---------end filed ---------