在日常开发中,我们在模型转换成字典的时候需要借助
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 中找到一下内容:
看到是有一个判断条件。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
,可得到结果:
通过这个上面的例子,再回过头看源码,其实是判断是否遵循来CustomReflectable
协议,如果遵循就使用customMirror
方法去取自定义的Mirror
实例,如果没有就新建一个。而新建的过程是我们本次探究的重点,继续探索新建源码。
全局索索可检索到一下内容:
let subjectType = subjectType ?? _getNormalizedType(subject, type: type(of: subject))
这句代码其实是在获取传入对象的真实类型。我看先看源码中的定义。
@_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文件中找到了函数的定义:
在这里面是调用了call
函数。
可以看到,先去获取实例的类型(第一个图),然后再根据俄不同类型,去初始化一个impl
,再传入call函数中(第二个图)。
每个类型都有对应的Impl结构体,我们以EnumImpl
为例。
该结构体继承自ReflectionMirrorImpl
,这是一个抽象类如下图:
回过头,继续看EnumImpl中,我们看是如何找到对应的Field
的。
在之前的文章讲到的,每一个类的Metadata中都有一个Descriptor
,这里是取Metadate
的typeDescriptor
属性。然后再去获取FiedDescuriptor
,然后再获取其中的key
(name)。
代码还原
- 通过源码查找EnumMetaData信息
在源码中打开MetaData.h文件,搜索
TargetEnumMetadata
。注意MetaData.h的文件目录include>swift>ABI>MetaData.h
。
注意:c++中结构体是可以继承的,所有我们想知道它的类型需要去它的父结构体中继续寻找:
找到了descripton
。再去其非结构体TaregetMatedate
中继续寻找:
又找到一个kind
。
根据以上信息可以得到TargetEnumMetadata
的数据类型如下:
struct OSTargetEnumMetadata{
var kind: Int
var typeDescriptor: UnsafeMutablePointer
}
用同样的方法。还原下TargetEnumDescriptor
的数据类型。
这里寻找过程只放部分,需要更详细的可以在源码中慢慢找。 得到数据结构:
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源码中的代码如下:
- 其他结构补全
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
}
打印结果为:
到此我们已经用自己的代码,去模拟实现了Mirror
的部分功能了。