所谓反射就是可以动态获取类型、成员信息,在运行时可以调用方法、属性等行为的特性。 在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)")
}
复制代码
上图寻找源码,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>,
复制代码
一层层剥离我们最终还原出: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的动态赋值思路:
- 首先通过内存相对偏移找到FieldOffsets的地址。这是一个buffer的首地址,里面依次存放着每个属性的当前值的地址(相对实例首地址的偏移地址)。
- 根据值的地址获取内存中的value。
- 协议中定义方法,将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
中读取到结构体名字, 从 fieldDescriptor
的 fields
中找到每一项的属性名字,和属性类型(类型的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
复制代码