Swift学习(六)Mirror原理探索

3,932 阅读6分钟

在日常开发中,我们在模型转换成字典的时候需要借助Mirror来实现。对于熟悉Object-C的开发者来说Mirror是非常陌生的。本文通过一些例子和Swift源码的分析,来记录探索Mirror的原理。

Mirror的使用

模型转化成字典。

//模型数据类型
class OSDeviceWaterCleaner :JSONMap {
    var deviceId: String?
    var deviceStatus: Int = 0
}

//转换协议
protocol JSONMap {
    func jsonMap() -> Any
}

//Mirror的使用。
extension JSONMap {
    func jsonMap() -> Any {
        let mirror = Mirror(reflecting: self)
        guard !mirror.children.isEmpty else {
            return self;
        }
        
        var result: [String: Any] = [:]
        for property in mirror.children {
            if let value = property.value as? JSONMap {
                if let key = property.label {
                    result[key] = value.jsonMap()
                }
            }
        }
        return result
    }
}

extension String: JSONMap {
    func jsonMap() -> Any {
        return self
    }
}

extension Int: JSONMap {
    func jsonMap() -> Any {
        return self
    }
}
func func1() {
         //新建对象
        let waterCleaner = OSDeviceWaterCleaner()
        waterCleaner.deviceId = "001"
        //model to dictory
        let json = waterCleaner.jsonMap()
        print(json)
    }

通过以上代码,我们就可以成功的把实例对象waterCleaner转换成字典类型,以供我们开发使用。在JSONMap方法中,有对Mirror的初始化和使用。我们在源码中找找Mirror的初始化方法的相关内容

源码分析

在Mirror.swift 中找到一下内容:

image.png 看到是有一个判断条件。CustomReflectable如何理解呢?其实是一个协议,我们通过一个例子来了解CustomReflectable

class OSWaterPuriferDevice: CustomReflectable {
        var customMirror: Mirror {
            let info = KeyValuePairs<String, Any>.init(dictionaryLiteral: ("deviceId",deviceId), ("deviceName", deviceName))
            let mirror = Mirror.init(self, unlabeledChildren: info, displayStyle: .class, ancestorRepresentation: .generated)
            return mirror
        }
        
        var deviceId : String
        var deviceName : String
        
        init(_ deviceId: String, _ deviceName: String) {
            self.deviceId = deviceId
            self.deviceName = deviceName
        }
    }
func func2() {
    let device = OSWaterPuriferDevice("001", "好喝不上头")
    print(device)
}

print处加断点,使用lldb打印device,可得到结果:

image.png 通过这个上面的例子,再回过头看源码,其实是判断是否遵循来CustomReflectable协议,如果遵循就使用customMirror方法去取自定义的Mirror实例,如果没有就新建一个。而新建的过程是我们本次探究的重点,继续探索新建源码。

全局索索可检索到一下内容:

image.png

let subjectType = subjectType ?? _getNormalizedType(subject, type: type(of: subject))

这句代码其实是在获取传入对象的真实类型。我看先看源码中的定义。

image.png @_silgen_name这种定义的方式在混编的时候比较常见,经过查阅相关资料,它的意思就是如果在swift中调用_getNormalizedType,编译器就会去C或者C++中去找swift_reflectionMirror_normalizedType函数,简单点理解可以把它看成是一个桥接的方式。 自己也可以去模仿实现一下。

int _log_from_c(int num) {
    printf("我在使用C语言%d", num);
    return num;
}
@_silgen_name("log_from_c")
func logFromC(num: Int) -> Int

我们继续看_getNormalizedType方法的实现,果然全局搜索后,在一个cpp文件中找到了函数的定义:

image.png 在这里面是调用了call函数。

image.png

image.png 可以看到,先去获取实例的类型(第一个图),然后再根据俄不同类型,去初始化一个impl,再传入call函数中(第二个图)。

每个类型都有对应的Impl结构体,我们以EnumImpl为例。

image.png 该结构体继承自ReflectionMirrorImpl,这是一个抽象类如下图:

image.png 回过头,继续看EnumImpl中,我们看是如何找到对应的Field的。

image.png

image.png 在之前的文章讲到的,每一个类的Metadata中都有一个Descriptor,这里是取MetadatetypeDescriptor属性。然后再去获取FiedDescuriptor,然后再获取其中的key(name)。

代码还原

  • 通过源码查找EnumMetaData信息 在源码中打开MetaData.h文件,搜索TargetEnumMetadata。注意MetaData.h的文件目录include>swift>ABI>MetaData.h

image.png 注意:c++中结构体是可以继承的,所有我们想知道它的类型需要去它的父结构体中继续寻找:

image.png 找到了descripton。再去其非结构体TaregetMatedate中继续寻找:

image.png又找到一个kind。 根据以上信息可以得到TargetEnumMetadata的数据类型如下:

struct OSTargetEnumMetadata{
    var kind: Int
    var typeDescriptor: UnsafeMutablePointer
}

用同样的方法。还原下TargetEnumDescriptor的数据类型。

image.png 这里寻找过程只放部分,需要更详细的可以在源码中慢慢找。 得到数据结构:

struct TargetEnumDescriptor{
    var flags: Int32
    var parent: TargetRelativeDirectPointer<UnsafeRawPointer>
    var name: TargetRelativeDirectPointer<CChar>
    var accessFunctionPointer: TargetRelativeDirectPointer<UnsafeRawPointer>
    var fieldDescriptor: TargetRelativeDirectPointer<UnsafeRawPointer>
    var NumPayloadCasesAndPayloadSizeOffset: UInt32
    var NumEmptyCases: UInt32
}
  • 相对寻址、直接寻址

上面的结构中,使用到了TargetRelativeDirectPointer结构体,该结构体其实是一个相对寻址的实现。

与基址变址寻址方式相类似,相对寻址以程序计数器PC的当前值(R15中的值)为基地址,指令中的地址标号作为偏移量,将两者相加后得到操作数的有效地址。(摘自百度百科)

而我们平时对象的引用方式其实是直接寻址。

先看看TargetRelativeDirectPointer的结构:

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

对应swift源码中的代码如下:

image.png

  • 其他结构补全
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 ValueWitnessFlags {
    static let alignmentMask: UInt32 = 0x0000FFFF
    static let isNonPOD: UInt32 = 0x00010000
    static let isNonInline: UInt32 = 0x00020000
    static let hasExtraInhabitants: UInt32 = 0x00040000
    static let hasSpareBits: UInt32 = 0x00080000
    static let isNonBitwiseTakable: UInt32 = 0x00100000
    static let hasEnumWitnesses: UInt32 = 0x00200000
}

struct ValueWitnessTable{
    var initializeBufferWithCopyOfBuffer: UnsafeRawPointer
    var destroy: UnsafeRawPointer
    var initializeWithCopy: UnsafeRawPointer
    var assignWithCopy: UnsafeRawPointer
    var initializeWithTake: UnsafeRawPointer
    var assignWithTake: UnsafeRawPointer
    var getEnumTagSinglePayload: UnsafeRawPointer
    var storeEnumTagSinglePayload: UnsafeRawPointer
    var size: Int
    var stride: Int
    var flags: UInt32
    var extraInhabitantCount: UInt32
}
  • 代码调用 定义枚举类型
enum TerminalChar {
  case Plain(Bool)
  case Bold
  case Empty
  case Cursor
}

使用代码获取

func func12() {
        let emunMatedate = TerminalChar.self
        //按位转换、不安全
        let ptr = unsafeBitCast(TerminalChar.self as Any.Type, to: UnsafeMutablePointer<TargetEnumMetadata>.self)
        let namePtr = ptr.pointee.typeDescriptor.pointee.name.getmeasureRelativeOffset()

        print(String(cString: namePtr))                 print(ptr.pointee.typeDescriptor.pointee.NumPayloadCasesAndPayloadSizeOffset)
        print(ptr.pointee.typeDescriptor.pointee.NumEmptyCases)
    }

对象的解析

使用同样的方法,我们对class的进行解析。同样的方法获取到TargetClassMetaData的数据结构。

struct TargetClassMetadata{
    var kind: Int
    var superClass: Any.Type
    var cacheData: (Int, Int)
    var data: Int
    var classFlags: Int32
    var instanceAddressPoint: UInt32
    var instanceSize: UInt32
    var instanceAlignmentMask: UInt16
    var reserved: UInt16
    var classSize: UInt32
    var classAddressPoint: UInt32
    var typeDescriptor: UnsafeMutablePointer<TargetClassDescriptor>
    var iVarDestroyer: UnsafeRawPointer
}

再获取TargetClassDescriptor的数据结构:

struct TargetClassDescriptor{
    var flags: Int32
    var parent: Int32
    var name: TargetRelativeDirectPointer<CChar>
    var accessFunctionPointer: TargetRelativeDirectPointer<UnsafeRawPointer>
    var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor>
    var superClassType: TargetRelativeDirectPointer<CChar>
    var metadataNegativeSizeInWords: Int32
    var metadataPositiveSizeInWords: Int32
    var numImmediateMembers: Int32
    var numFields: Int32
    var fieldOffsetVectorOffset: Int32

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

然后根据以上的数据结构和数据特性,就能获取到对象实例的key和value了:

func func13() {
        let room = OSRoom("001", "300平大hourse")
        let ptr = unsafeBitCast(OSRoom.self as Any.Type, to: UnsafeMutablePointer<TargetClassMetadata>.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)")
        let superClassType = ptr.pointee.typeDescriptor.pointee.superClassType.getmeasureRelativeOffset()
        print("current superClassType: \(String(cString: superClassType))")
        
        //存储属性的首地址,存储大小为numFileds * int?
        let offsets = ptr.pointee.typeDescriptor.pointee.getFieldOffsets(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.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)
        
            //HandJSON,还原混写
            let fieldType = swift_getTypeByMangledNameInContext(
                typeMangleName,
                256,
                UnsafeRawPointer(ptr.pointee.typeDescriptor),

                UnsafeRawPointer(genericVector)?.assumingMemoryBound(to: Optional<UnsafeRawPointer>.self))
        
            print(fieldType as Any)
       
            //比较难理解,HandJSON
            let type = unsafeBitCast(fieldType, to: Any.Type.self)
        
            print(type)
        
            let instanceAddress = Unmanaged.passUnretained(room as AnyObject).toOpaque()
        
    
            let value = customCast(type: type)

            print("fieldType:\(type) \nfieldValue: \(value.get(from: instanceAddress.advanced(by: fieldOffset))) ")
            print("--- filed: \(String(cString: fieldDespritor)) info end ---- \n")
        }
    }

其中customCast的实现如下:

struct BrigeProtocolMetadata {
    let type: Any.Type
    let witness: Int
}

func customCast(type: Any.Type) -> BrigeProtocol.Type {
    var container = BrigeProtocolMetadata(type: type, witness: 0)
    var protocolType = BrigeProtocol.Type.self
    var cast = unsafeBitCast(container, to: BrigeProtocol.Type.self)
    return cast
}

打印结果为:

image.png 到此我们已经用自己的代码,去模拟实现了Mirror 的部分功能了。