Swift的Mirror反射怎么使用
这篇文章主要介绍了Swift的Mirror反射怎么使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Swift的Mirror反射怎么使用文章都会有所收获,下面我们一起来看看吧。
元类型与.self
AnyObject
在Swift开发中,我们经常会使用AnyObject
来代表任意类的实例、类的类型、以及仅类遵守的协议。
代表任意类的实例、类的类型
class LGTeacher { var age = 18 } var t = LGTeacher() var t1: AnyObject = t //代表LGTeacher类的实例 var t2: AnyObject = LGTeacher.self //代表LGTeacher类的类型
代表仅类遵循的协议
这里使用了AnyObjcct
来修饰协议,可以看到,当struct
类型的structLGTeacher
去遵循协议时,编译器会报错。只允许class
类型遵循协议。
我们在和OC交互的过程中,也经常通过AnyObject
来表示某种类型的instance
。
我们在代码编写的过程中有时候不知道具体的类型,⽤AnyObject
来表示;那如果我们知道了确定了类型,该如何把AnyObject
转换成具体的类型,这⾥我们使⽤三个关键字as,as?,as!
进行类型转换。
AnyClass
AnyClass
代表了任意实例的类型,我们可以从源码里面去查看AnyClass
的定义
public typealias AnyClass = AnyObject.Type
我们可以看到,AnyClass
的定义就是AnyObject.Type
,也就是实例对象的类型,所以我们不能用具体的实例对象赋值给AnyClass
,编译器会报错。
Any
Any
可以代表任意类型(枚举、结构体、类),也包括函数类型和Optional
类型。
var array:[AnyObject] = [1,2]
上面这段代码会报错,因为AnyObject
代表任意类的实例和类型,而我们传入的是Int类型,是属于值类型,无法用AnyObject
表示。这时,我们要使用Any
。
var array:[Any] = [1,2]
type(Of:)
type(Of:)
⽤来获取⼀个值的动态类型。什么是动态类型呢?
静态类型(static type),这个是在编译时期确定的类型。
动态类型(dynamic Type),这个是在运⾏时期确定的类型。
接下来我们用代码来描述静态类型和动态类型。
可以看到,在编译期间就能知道a
的类型是Int
,因为初始化数据的时候赋值的是Int
类型数据,但是在test
方法的形参定义的类型是Any
,因此在编译期间并不知道其形参类型,所以将a
传入时也被编译器当作是Any
类型,但是在运行时,可以动态得知传入的a
是Int
类型,因此type(of:)
就可以用来获取当前值在运行时的实际类型。
self
在Swift中,我们可以使用类型或者实例对象来访问self。
T.self
: T
是实例对象,当前 T.self
返回的就是实例对象本身。如果 T
是类,当前 T.self
返回的就是元类型。我们通过代码验证一下。
从代码中,我们可以看到,当我po t
和po t1
时,可以看到t
和t1
指向同一个地址。然后当我po t2
时,发现打印出来的是LGTeaher
类型。接下来我用x/8g
命令打印t2
,打印出t2
的内存地址。再打印出t
的内存地址,然后打印t
里面存储metadata
的内存地址。发现和t2
的内存地址一模一样。
所以,在 T.self
中,当 T
为实例对象的时候,T.self
返回的是实例对象本身。当 T
为类的时候,T.self
返回的是一个元类型,也就是前面讲的元数据(metadata)。
self在方法里面的作用
我们可以看到,在实例方法中,self
指向的就是当前调用方法的实例对象,而在类方法里面,self
指向的就是当前类型的元数据。
Self
Self
类型不是特定类型,⽽是让您⽅便地引⽤当前类型,⽽⽆需重复或知道该类型的名称。
在协议声明或协议成员声明中,
Self
类型是指最终符合协议的类型。
Self
作为实例方法的返回类型代表自身类型
计算属性/实例方法中访问自身的类型属性,类型方法
Swift Runtime
在OC中,我们可以通过Runtime特性来获取一个类的属性和方法。而Swift中没有Runtime,能使用OC的Runtime获取一个类的属性和方法吗?我们通过代码测试一下。
代码结果没有打印任何东西。
现在我们往LGTeacher
类的属性和方法前面加上@objc
标识符,看看会有什么结果?
这时可以通过Runtime API
打印方法和属性名,但是OC无法进行调动。
对于继承NSObject
类的Swift类,如果我们想要动态的获取当前的属性和⽅法,必须在其声明前添加@objc
关键字,否则也是没有办法通过Runtime API
获取的。
继承NSObject
类的Swift类,没有在属性和方法声明前面添加@objc
关键字,只能获取到init
方法。
继承NSObject
类的Swift类,在属性和方法声明前面添加@objc
关键字,不仅可以使用Runtime API
获取属性和方法名,也可以在OC中被调用。
还有一些和Swift Runtime
相关的结论,我在这里总结出来,可以自己去试验一下。
纯swift类没有动态性,但在⽅法、属性前添加
dynamic
修饰,可获得动态性。继承⾃NSObject的swift类,其继承⾃⽗类的⽅法具有动态性,其它⾃定义⽅法、属性想要获得动态性,需要添加
dynamic
修饰。若⽅法的参数、属性类型为swift特有、⽆法映射到objective-c的类型(如
Character
、Tuple
),则 此⽅法、属性⽆法添加dynamic
修饰(编译器报错)
Mirror
Mirror的基本用法
所谓反射就是可以动态获取类型、成员信息,在运⾏时可以调⽤⽅法、属性等⾏为的特性。在使⽤OC开发时很少强调其反射概念,因为OC的Runtime
要⽐其他语⾔中的反射强⼤的多。但是 Swift 是⼀⻔类型安全的语⾔,不⽀持我们像 OC 那样直接操作,它的标准库仍然提供了反射机制来让我们访问成员信息,Swift 的反射机制是基于⼀个叫 Mirror
的结构体来实现的。然后就可以通过它查询这个实例。
Mirror的基本使用如下
class LGTeacher { var age:Int = 18 func teach() { print("teach") } } //⾸先通过构造⽅法构建⼀个Mirror实例,这⾥传⼊的参数是 Any,也就意味着当前可以是类,结构体,枚举等 let mirror = Mirror(reflecting: LGTeacher()) //接下来遍历 children 属性,这是⼀个集合 for pro in mirror.children { //然后我们可以直接通过 label 输出当前的名称,value 输出当前反射的值 print("\(pro.label) : \(pro.value)") }
Mirror的简单应用-JSON解析
class LGTeacher { var age:Int = 18 var name = "FY" } enum JSONMapError: Error { case emptyKey case notConformProtocol } protocol JSONMap { func jsonMap() -> Any } extension JSONMap { func jsonMap() -> Any { let mirror = Mirror(reflecting: self) guard !mirror.children.isEmpty else { return self } var result: [String: Any] = [:] for child in mirror.children { if let value = child.value as? JSONMap { if let key = child.label { result[key] = try? value.jsonMap() } } else { return JSONMapError.notConformProtocol } } return result } } extension LGTeacher: JSONMap{} extension Int: JSONMap{} extension String: JSONMap{} print(LGTeacher().jsonMap()) // ["age": 18, "name": "FY"]
Mirror源码解析
⾸先我们现在源⽂件⾥⾯搜索Mirror.Swift
,在源码中我们可以很清晰的看到Mirror
是由结构体实现的,我们忽略掉⼀些细节,快速定位到初始化的⽅法
public init(reflecting subject: Any) { if case let customized as CustomReflectable = subject { self = customized.customMirror } else { self = Mirror(internalReflecting: subject) } }
可以看到,这⾥接受⼀个Any
类型的参数,同样的这⾥有⼀个if case
的写法来判断当前的subject
是否遵循了customReflectable
协议,如果是我们就直接调⽤customMirror
, 否则就进⾏下级函数的调⽤。
这⾥有两个需要注意的点if case
的写法,这⾥其实枚举Case
的模式匹配,和我们的Switch
⼀样,这⾥是只有⼀个case
的 switch 语句。
于此同时这⾥出现了⼀个customRefletable
的协议。我们来看一下它的用法。⾸先我们遵循 customReflectable
协议,并实现其中的属性customMirror
,customMirror
会返回⼀个Mirror
对象。代码如下:
这里通过遵循customReflectable
协议并实现了其中的计算属性customMirror
,主要作用是当我们使用lldb debug
的时候,可以提供详细的属性信息。
我们接下来看如果不遵循customRefletable
协议的类,那么就会走Mirror(internalReflecting: subject)
代码。
全局搜索internalReflecting
,在ReflectionMirror.swift
文件里面找到了这个方法的具体实现。
从代码里面我们可以看到,首先需要获取subject
的真实类型信息。然后再获取subject
的属性信息。 而获取subject
的真实类型信息则是通过_getNormalizedType
这个方法来获取的。搜索这个方法,然后我们就可以找到它的代码。
这里使用了一个编译器字段 @ silgen_name
其实是 Swift
的一个隐藏符号,作用是将某个 C/C++
语言函数直接映射为 Swift
函数。也可以理解为为 C++
代码的 swift_reflectionMirror_normalizedType
函数定义一个在 swift
中使用的别名 _getNormalizedType
。
所以调用了_getNormalizedType
方法实际上是调用了swift_reflectionMirror_normalizedType
方法,我在ReflectionMirror.cpp
文件中找到了具体实现。
从代码里面可以知道,通过call
函数调用了ReflectionMirrorImpl
类,然后返回这个类的类型。
我们先看一下ReflectionMirrorImpl
类的具体内容。
从注释中,我们可以知道,这是一个抽象基类,也就是说不同的类型反射需要不同的类实现。
我们接下来看一下call
函数的具体实现
在call
函数中有一个Switch
方法。根据不同的类型,调用不同的ReflectionMirrorImpl
类。 我们就取EnumIpml
类去探个究竟。
上面代码就是EnumIpml
类的具体实现。首先是isReflectable()
这个方法。这个方法返回这个类型是否可以被反射,也就是找到metadata
,再找到metadata
中存储的Description
,通过它里面存储的 isReflectable
来确定。
接下来我们看一下getInfo
方法。在代码里面我们可以看到,获取name
、info
属性信息主要是通过getFieldAt
来获取。我们现在就去查看getFieldAt
方法。具体代码如下:
static std::pair<StringRef /*name*/, FieldType /*fieldInfo*/> getFieldAt(const Metadata *base, unsigned index) { using namespace reflection; auto failedToFindMetadata = [&]() -> std::pair<StringRef, FieldType> { auto typeName = swift_getTypeName(base, /*qualified*/ true); missing_reflection_metadata_warning( "warning: the Swift runtime found no field metadata for " "type '%*s' that claims to be reflectable. Its fields will show up as " "'unknown' in Mirrors\n", (int)typeName.length, typeName.data); return {"unknown", FieldType(&METADATA_SYM(EMPTY_TUPLE_MANGLING))}; }; auto *baseDesc = base->getTypeContextDescriptor(); if (!baseDesc) return failedToFindMetadata(); auto *fields = baseDesc->Fields.get(); if (!fields) return failedToFindMetadata(); auto &field = fields->getFields()[index]; // Bounds are always valid as the offset is constant. auto name = field.getFieldName(); // Enum cases don't always have types. if (!field.hasMangledTypeName()) return {name, FieldType::untypedEnumCase(field.isIndirectCase())}; auto typeName = field.getMangledTypeName(); SubstGenericParametersFromMetadata substitutions(base); auto result = swift_getTypeByMangledName( MetadataState::Complete, typeName, substitutions.getGenericArgs(), [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index); }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); }); // If demangling the type failed, pretend it's an empty type instead with // a log message. TypeInfo typeInfo; if (result.isError()) { typeInfo = TypeInfo({&METADATA_SYM(EMPTY_TUPLE_MANGLING), MetadataState::Complete}, {}); auto *error = result.getError(); char *str = error->copyErrorString(); missing_reflection_metadata_warning( "warning: the Swift runtime was unable to demangle the type " "of field '%*s'. the mangled type name is '%*s': %s. this field will " "show up as an empty tuple in Mirrors\n", (int)name.size(), name.data(), (int)typeName.size(), typeName.data(), str); error->freeErrorString(str); } else { typeInfo = result.getType(); } auto fieldType = FieldType(typeInfo.getMetadata()); fieldType.setIndirect(field.isIndirectCase()); fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership()); fieldType.setIsVar(field.isVar()); return {name, fieldType};
这里可以看到可以看到 所有的信息都是通过Metadata
、getDescription()
、FieldDescrition
这几个东西来去实现的,⼀个是当前类型的元数据,⼀个是当前类型的描述,⼀个是对当前类型属性的描述。
Enum Metadata探索
我们在类和结构体这篇文章里面,描述了类的Metadata
结构,并把它的C++
代码转换成了Swift
代码。我们这次来尝试转换Enum
和struct
的MetaData
结构。首先来探索Enum
的Metadata
结构。
还原TargetEnumMetadata
通过源码全局搜索EnumMetadata
,我们找到了TargetEnumMetadata
。沿着TargetEnumMetadata
的继承链往上查找,TargetEnumMetadata
-> TargetValueMetadata
-> TargetMetadata
struct TargetEnumMetadata : public TargetValueMetadata<Runtime> { } struct TargetValueMetadata : public TargetMetadata<Runtime> { TargetSignedPointer<Runtime, const TargetValueTypeDescriptor<Runtime> *Description; } struct TargetMetadata { StoredPointer Kind; }
从上面的源码中我们可以知道,TargetMetadata
有一个属性Kind
,这个Kind
主要是存储MetadataKind
类,是个int_32
类型。TargetValueMetadata
里面有Description
属性,因此我们可以把TargetEnumMetadata
转成这样的结构体
struct TargetEnumMetadata { var kind: Int var typeDescriptor: UnsafeRawPointer }
还原TargetEnumDescriptor
接下来,我们要还原typeDescriptor
的结构,虽然在TargetValueMetadata
类中是TargetValueTypeDescriptor
类,而我们在TargetMetadata
发现Description
属性是TargetEnumDescriptor
类,所以,Description
属性和TargetMetadata
一样,应该也是有继承链的。
在TargetMetadata
源码中,获取Description
属性的方法是这样的:
const TargetEnumDescriptor<Runtime> *getDescription() const { return llvm::cast<TargetEnumDescriptor<Runtime>>(this->Description); }
然后我们去源码里面查看TargetEnumDescriptor
类,得到它的继承链TargetEnumDescriptor
-> TargetValueTypeDescriptor
-> TargetTypeContextDescriptor
-> TargetContextDescriptor
它们包含的属性的代码如下:
class TargetEnumDescriptor final : public TargetValueTypeDescriptor<Runtime>, public TrailingGenericContextObjects<TargetEnumDescriptor<Runtime>, TargetTypeGenericContextDescriptorHeader, /*additional trailing objects*/ TargetForeignMetadataInitialization<Runtime>, TargetSingletonMetadataInitialization<Runtime>, TargetCanonicalSpecializedMetadatasListCount<Runtime>, TargetCanonicalSpecializedMetadatasListEntry<Runtime>, TargetCanonicalSpecializedMetadatasCachingOnceToken<Runtime>> { uint32_t NumPayloadCasesAndPayloadSizeOffset; uint32_t NumEmptyCases; } class TargetValueTypeDescriptor: public TargetTypeContextDescriptor<Runtime> { } class TargetTypeContextDescriptor: public TargetContextDescriptor<Runtime> { TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false> Name; TargetRelativeDirectPointer<Runtime, MetadataResponse(...), /*Nullable*/ true> AccessFunctionPtr; TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor,/*nullable*/ true> Fields; } struct TargetContextDescriptor { ContextDescriptorFlags Flags; TargetRelativeContextPointer<Runtime> Parent; }
从上面的代码我们可以把TargetEnumDescriptor
使用Swift把它还原出来,还原的代码如下:
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 }
此时TargetMetadata
中的数据结构也可以修改一下
struct TargetEnumMetadata{ var kind: Int var typeDescriptor: UnsafeMutablePointer<TargetEnumDescriptor> }
相对偏移指针
在上面的源码中,我们可以发现,TargetValueTypeDescriptor
中的属性、比如name
、Fields
等,他们的类型都是用TargetRelativeDirectPointer
来定义。我们现在来看一下TargetRelativeDirectPointer
是什么?
从上面的源码定义可以知道,它是一个模板类,(接收三个参数,⼀个是Runtime
, ⼀个是Pointee
, Bool
类型默认为True
)。接下来我们看一下RelativeDirectPointer
。
这个指针类代码比较简单,其中 T
就是我们进来的类型,Offset
就是int32_t
的类型,从字面意思上看应该是偏移量之类的。我们再看下它的get()
方法。
从以上代码,我们可以看出TargetRelativeDirectPointer
应该是一个用来相对寻址的指针类。在 Swift中引⽤⼀个实例对象有两种情况:一种是直接寻址,另外一种是相对寻址。比如 TargetEnumDescriptor 中的 Name
,这个 Name
存储的值并不是 Name
表意上的值,Name
存储的是一个叫做相对偏移量或者叫偏移信息。此时,我们拿到 Name
的值的内存地址做法是:Name 的内存地址 + 相对偏移量。在 Swift 里面有很多这样的偏移信息,这样做可以节省内存空间,避免存储大量的内存地址。
对此,我们把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)) } } }
打印枚举中的属性
最后,我们来打印一下枚举中的属性。代码如下:
enum Planet { case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune } let ptr = unsafeBitCast(Planet.self as Any.Type to:UnsafeMutablePointer<TargetEnumMetadata>.self) let namePtr = ptr.pointee.typeDescriptor.pointee.name.getmeasureRelativeOffset() print("name: ",String(cString: namePtr)) print("NumPayloadCasesAndPayloadSizeOffset ",ptr.pointee.typeDescriptor.pointee.NumPayloadCasesAndPayloadSizeOffset) print("NumEmptyCases ",ptr.pointee.typeDescriptor.pointee.NumEmptyCases)
打印结果如下
name: Planet NumPayloadCasesAndPayloadSizeOffset 0 NumEmptyCases 8
Struct Metadata探索
现在我们来解析一下struct
类型的Metadata
。
首先,和上面的Enum
一样,通过全局搜索,找到struct
的MetaData
是TargetStructMetadata
类。通过它的继承链TargetStructMetadata
-> TargetValueMetadata
-> TargetMetadata
可以知道,TargetStructMetadata
的数据结构和TargetEnumMetadata
一样,因此,我们可以还原一下 TargetStructMetadata
的数据结构如下:
struct TargetStructMetadata { var kind: Int var typeDescriptor: UnsafeRawPointer }
然后,我们去寻找typeDescriptor
的类型,从TargetStructMetadata
的源码中,我们找到typeDescriptor
的类型是TargetStructDescriptor
。我们去搜索TargetStructDescriptor
的源码,得到了TargetStructDescriptor
类的定义以及属性如下:
class TargetStructDescriptor final: public TargetValueTypeDescriptor<Runtime>, public TrailingGenericContextObjects<TargetStructDescriptor<Runtime>, TargetTypeGenericContextDescriptorHeader, /*additional trailing objects*/ TargetForeignMetadataInitialization<Runtime>, TargetSingletonMetadataInitialization<Runtime>, TargetCanonicalSpecializedMetadatasListCount<Runtime>, TargetCanonicalSpecializedMetadatasListEntry<Runtime>, TargetCanonicalSpecializedMetadatasCachingOnceToken<Runtime>> { uint32_t NumFields; uint32_t FieldOffsetVectorOffset; }
从源码中我们可以看到,TargetStructDescriptor
继承自TargetValueTypeDescriptor
,因此继承链和TargetEnumDescriptor
一样,我们还原出来的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 ``` func getFieldOffsets(_ metadata: UnsafeRawPointer) -> UnsafePointer<Int32> { return UnsafeRawPointer(metadata.assumingMemoryBound(to: Int.self).advanced(by: numericCast(self.FieldOffsetVectorOffset))).assumingMemoryBound(to: Int32.self) } //参考handyjson 中 var genericArgumentOffset: Int { return 2 }
}
此时`TargetStructMetadata`中的数据结构也可以修改一下 ```swift struct TargetStructMetadata{ var kind: Int var typeDescriptor: UnsafeMutablePointer<TargetStructDescriptor> }
接着我们还原FieldDescriptor
的数据结构。我们先找到FieldDescriptor
类的源码,找出它的属性,和方法,代码如下:
class FieldDescriptor { const FieldRecord *getFieldRecordBuffer() const { return reinterpret_cast<const FieldRecord *>(this + 1); } public: const RelativeDirectPointer<const char> MangledTypeName; const RelativeDirectPointer<const char> Superclass; FieldDescriptor() = delete; const FieldDescriptorKind Kind; const uint16_t FieldRecordSize; const uint32_t NumFields; llvm::ArrayRef<FieldRecord> getFields() const { return {getFieldRecordBuffer(), NumFields}; } }
我们来看一下getFields()
方法,这个方法就是获取 fields
的方法,fields
存的是 FieldRecords,通过getFieldRecordBuffer()
来读取FieldRecords。
在getFieldRecordBuffer()
方法中,通过 reinterpret_cast
将 (this + 1)
强制转换成 FieldRecord *
类型。所以我们可以推测这个 fields
是一块连续的内存空间,这一块连续的内存空间存储的是 FieldRecord 类型,并且 NumFields
是它的容量大小。
在 C++ 中, this
是一个指向该对象的指针,由于它是一个指针,因此它可以应用指针算术甚至数组索引。如果这个 this 是数组中的一个元素,(this + 1) 则将指向数组中的下一个对象。
所以我们可以把FieldDescriptor
的数据结构还原成下面结构。
struct FieldDescriptor { var MangledTypeName: TargetRelativeDirectPointer<CChar> var Superclass: TargetRelativeDirectPointer<CChar> var Kind: UInt16 var FieldRecordSize:UInt16 var NumFields: UInt32 var fields: FieldRecordBuffer<FieldRecord> }
其中FieldRecordBuffer
我们可以把它还原成一个有连续空间的数组,容量为NumFields
,存储的是FieldRecord
。还原结构如下:
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)) } } }
最后我们看一下FieldRecord
的源码,得到的源码属性如下:
class FieldRecord { const FieldRecordFlags Flags; public: const RelativeDirectPointer<const char> MangledTypeName; const RelativeDirectPointer<const char> FieldName; }
还原FieldRecord
得到的数据结构如下:
struct FieldRecord { var Flags: UInt32 var MangledTypeName: TargetRelativeDirectPointer<CChar> var FieldName: TargetRelativeDirectPointer<CChar> }
至此,我们把结构体的MetaData
结构都还原出来。
获取结构体的属性
现在我们来验证一下结构体,获取结构体的属性。代码如下:
@_silgen_name("swift_getTypeByMangledNameInContext") func swift_H_getTypeByMangledNameInContext(typeName: UnsafeRawPointer, len: Int, context: UnsafeRawPointer, generic: UnsafeRawPointer) -> UnsafeRawPointer protocol BridgeProtocol { } extension BridgeProtocol { static func get(from pointer: UnsafeRawPointer) -> Any { pointer.assumingMemoryBound(to: Self.self).pointee } } struct BridgeProtocolMetadata { let type: Any.Type let witness: Int } func customCast(type: Any.Type) -> BridgeProtocol.Type { let container = BridgeProtocolMetadata(type: type, witness: 0) let cast = unsafeBitCast(container, to: BridgeProtocol.Type.self) return cast } struct LGStudent { var age = 18 var name = "FWJ" let money = 2000 } var t = LGStudent() let ptr = unsafeBitCast(LGStudent.self as Any.Type, to: UnsafeMutablePointer<TargetStructMetadata>.self) print("----------开始解析---------------") let namePtr = ptr.pointee.typeDescriptor.pointee.name.getmeasureRelativeOffset() let filedNum = ptr.pointee.typeDescriptor.pointee.NumFields print("当前结构体的名称: \(String(cString: namePtr))") print("当前结构体的属性数量 \(filedNum)") print("============开始解析属性============") let offsets = ptr.pointee.typeDescriptor.pointee.getFieldOffsets(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self)) for i in 0..<filedNum { let fieldRecord = ptr.pointee.typeDescriptor.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)) let fieldOffset = offsets[Int(i)] let fieldName = fieldRecord.pointee.FieldName.getmeasureRelativeOffset() print("--- \(String(cString: fieldName)) 属性信息 ---") let mangledTypeName = fieldRecord.pointee.MangledTypeName.getmeasureRelativeOffset() print("mangledTypeName: \(String(cString: mangledTypeName))") let typeNameLength = Int(256) let genericVector = UnsafeRawPointer(ptr).advanced(by: ptr.pointee.typeDescriptor.pointee.genericArgumentOffset * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self) **let** fieldType = swift_H_getTypeByMangledNameInContext(typeName: mangledTypeName, len: typeNameLength, context: UnsafeRawPointer(ptr.pointee.typeDescriptor), generic: genericVector) let type = unsafeBitCast(fieldType, to: Any.Type.self) print("fieldType: \(type)") let brigeProtocolType = customCast(type: type) let instanceAddress = withUnsafePointer(to: &t) { return UnsafeRawPointer($0) } let fieldValue = brigeProtocolType.get(from: instanceAddress.advanced(by: Int(fieldOffset))) print("fieldValue: \(fieldValue)") print("--- \(String(cString: fieldName)) 属性信息 ---") } //打印结果 ----------开始解析--------------- 当前结构体的名称: LGStudent** 当前结构体的属性数量 3 ============开始解析属性============ --- age 属性信息 --- mangledTypeName: Si fieldType: Int fieldValue: 18 --- age 属性信息 --- --- name 属性信息 --- mangledTypeName: SS fieldType: String fieldValue: FWJ --- name 属性信息 --- --- money 属性信息 --- mangledTypeName: Si fieldType: Int fieldValue: 2000 --- money 属性信息 ---
swift_getTypeByMangledNameInContext 函数
这个函数的源码在MetadataLookup.cpp
文件中,我们来看一下它的具体实现
SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT const Metadata * _Nullable swift_getTypeByMangledNameInContext( const char *typeNameStart, size_t typeNameLength, const TargetContextDescriptor<InProcess> *context, const void * const *genericArgs) { llvm::StringRef typeName(typeNameStart, typeNameLength); SubstGenericParametersFromMetadata substitutions(context, genericArgs); return swift_getTypeByMangledName(MetadataState::Complete, typeName, genericArgs, [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index); }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); }).getType().getMetadata(); }
这个函数返回的是Metadata
类型的指针,也就是Swift函数中的Type
类型。可以通过这个函数获取到每个函数的类型。但是这个函数是C++
函数,需要把它转换成Swift
函数。这里参考了HandyJSON
这个第三方库,使用@_silgen_name
映射成swift函数。具体代码如下:
@_silgen_name("swift_getTypeByMangledNameInContext") func swift_H_getTypeByMangledNameInContext(typeName: UnsafeRawPointer, len: Int, context: UnsafeRawPointer, generic: UnsafeRawPointer) -> UnsafeRawPointer
获取属性的值
我们可以根据类型和 FieldOffsetVectorOffset
属性值存储相对于实例的偏移量获取属性值,获取属性值存储的指针。参考 HandyJSON
通过协议中Self
代表真实的类型去读取指针的值。代码如下:
protocol BridgeProtocol { } extension BridgeProtocol { static func get(from pointer: UnsafeRawPointer) -> Any { pointer.assumingMemoryBound(to: Self.self).pointee } } struct BridgeProtocolMetadata { let type: Any.Type let witness: Int } func customCast(type: Any.Type) -> BridgeProtocol.Type { let container = BridgeProtocolMetadata(type: type, witness: 0) let cast = unsafeBitCast(container, to: BridgeProtocol.Type.self) return cast }
这个函数传入一个
Any.Type
的类型,通过它来创建一个协议的Metadata
结构相同的BrigeProtocolMetadata
实例通过
BrigeProtocolMetadata
转换成协议的BrigeProtocol.Type.self
,也就是协议的Metadata
,那么此时这个协议类型可以获取到属性的真实类型将属性值指针转换为
Self
类型的类型指针,通过pointee
就可以获取真实的值
关于“Swift的Mirror反射怎么使用”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Swift的Mirror反射怎么使用”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注蜗牛博客行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:niceseo99@gmail.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
评论