iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >Swift的Mirror反射怎么使用
  • 184
分享到

Swift的Mirror反射怎么使用

2023-07-02 10:07:01 184人浏览 泡泡鱼
摘要

这篇文章主要介绍了Swift的Mirror反射怎么使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Swift的Mirror反射怎么使用文章都会有所收获,下面我们一起来看看吧。元类型与.selfAnyObjec

这篇文章主要介绍了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类的类型
    • 代表仅类遵循的协议

    Swift的Mirror反射怎么使用

    这里使用了AnyObjcct来修饰协议,可以看到,当struct类型的structLGTeacher去遵循协议时,编译器会报错。只允许class类型遵循协议。

    我们在和OC交互的过程中,也经常通过AnyObject来表示某种类型的instanceSwift的Mirror反射怎么使用

    我们在代码编写的过程中有时候不知道具体的类型,⽤AnyObject来表示;那如果我们知道了确定了类型,该如何把AnyObject转换成具体的类型,这⾥我们使⽤三个关键字as,as?,as!进行类型转换。

    AnyClass

    AnyClass代表了任意实例的类型,我们可以从源码里面去查看AnyClass的定义

    public typealias AnyClass = AnyObject.Type

    我们可以看到,AnyClass的定义就是AnyObject.Type,也就是实例对象的类型,所以我们不能用具体的实例对象赋值给AnyClass,编译器会报错。 Swift的Mirror反射怎么使用

    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),这个是在运⾏时期确定的类型。

    接下来我们用代码来描述静态类型和动态类型。 Swift的Mirror反射怎么使用

    可以看到,在编译期间就能知道a的类型是Int,因为初始化数据的时候赋值的是Int类型数据,但是在test方法的形参定义的类型是Any,因此在编译期间并不知道其形参类型,所以将a传入时也被编译器当作是Any类型,但是在运行时,可以动态得知传入的aInt类型,因此type(of:)就可以用来获取当前值在运行时的实际类型。

    self

    在Swift中,我们可以使用类型或者实例对象来访问self。

    T.selfT 是实例对象,当前 T.self 返回的就是实例对象本身。如果 T 是类,当前 T.self 返回的就是元类型。我们通过代码验证一下。

    Swift的Mirror反射怎么使用

    从代码中,我们可以看到,当我po t po t1时,可以看到tt1指向同一个地址。然后当我po t2时,发现打印出来的是LGTeaher类型。接下来我用x/8g命令打印t2,打印出t2的内存地址。再打印出t的内存地址,然后打印t里面存储metadata的内存地址。发现和t2的内存地址一模一样。

    所以,在 T.self 中,当 T 为实例对象的时候,T.self 返回的是实例对象本身。当 T 为类的时候,T.self 返回的是一个元类型,也就是前面讲的元数据(metadata)。

    self在方法里面的作用

    Swift的Mirror反射怎么使用

    我们可以看到,在实例方法中,self指向的就是当前调用方法的实例对象,而在类方法里面,self指向的就是当前类型的元数据。

    Self

    Self类型不是特定类型,⽽是让您⽅便地引⽤当前类型,⽽⽆需重复或知道该类型的名称。

    • 在协议声明或协议成员声明中,Self类型是指最终符合协议的类型。

    Swift的Mirror反射怎么使用

    • Self作为实例方法的返回类型代表自身类型

    Swift的Mirror反射怎么使用

    • 计算属性/实例方法中访问自身的类型属性,类型方法

    Swift的Mirror反射怎么使用

    Swift Runtime

    在OC中,我们可以通过Runtime特性来获取一个类的属性和方法。而Swift中没有Runtime,能使用OC的Runtime获取一个类的属性和方法吗?我们通过代码测试一下。 

    Swift的Mirror反射怎么使用

    代码结果没有打印任何东西。

    现在我们往LGTeacher类的属性和方法前面加上@objc标识符,看看会有什么结果? 

    Swift的Mirror反射怎么使用

    这时可以通过Runtime api打印方法和属性名,但是OC无法进行调动。

    对于继承NSObject类的Swift类,如果我们想要动态的获取当前的属性和⽅法,必须在其声明前添加@objc 关键字,否则也是没有办法通过Runtime API获取的。

      Swift的Mirror反射怎么使用

    Swift的Mirror反射怎么使用

    继承NSObject类的Swift类,没有在属性和方法声明前面添加@objc 关键字,只能获取到init方法。

    Swift的Mirror反射怎么使用Swift的Mirror反射怎么使用

    继承NSObject类的Swift类,在属性和方法声明前面添加@objc关键字,不仅可以使用Runtime API获取属性和方法名,也可以在OC中被调用。

    还有一些和Swift Runtime相关的结论,我在这里总结出来,可以自己去试验一下。

    • 纯swift类没有动态性,但在⽅法、属性前添加dynamic修饰,可获得动态性。

    • 继承⾃NSObject的swift类,其继承⾃⽗类的⽅法具有动态性,其它⾃定义⽅法、属性想要获得动态性,需要添加dynamic修饰。 

    • 若⽅法的参数、属性类型为swift特有、⽆法映射到objective-c的类型(如CharacterTuple),则 此⽅法、属性⽆法添加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 协议,并实现其中的属性customMirrorcustomMirror会返回⼀个Mirror对象。代码如下: Swift的Mirror反射怎么使用

    这里通过遵循customReflectable 协议并实现了其中的计算属性customMirror,主要作用是当我们使用lldb debug的时候,可以提供详细的属性信息。

    我们接下来看如果不遵循customRefletable协议的类,那么就会走Mirror(internalReflecting: subject)代码。

    全局搜索internalReflecting,在ReflectionMirror.swift文件里面找到了这个方法的具体实现。

    Swift的Mirror反射怎么使用

    从代码里面我们可以看到,首先需要获取subject的真实类型信息。然后再获取subject的属性信息。 而获取subject的真实类型信息则是通过_getNormalizedType这个方法来获取的。搜索这个方法,然后我们就可以找到它的代码。 

    Swift的Mirror反射怎么使用

    这里使用了一个编译器字段 @ silgen_name 其实是 Swift 的一个隐藏符号,作用是将某个 C/C++语言函数直接映射为 Swift 函数。也可以理解为为 c++ 代码的 swift_reflectionMirror_normalizedType 函数定义一个在 swift 中使用的别名 _getNormalizedType

    所以调用了_getNormalizedType方法实际上是调用了swift_reflectionMirror_normalizedType方法,我在ReflectionMirror.cpp文件中找到了具体实现。

    Swift的Mirror反射怎么使用

    从代码里面可以知道,通过call函数调用了ReflectionMirrorImpl类,然后返回这个类的类型。

    我们先看一下ReflectionMirrorImpl类的具体内容。 

    Swift的Mirror反射怎么使用

    从注释中,我们可以知道,这是一个抽象基类,也就是说不同的类型反射需要不同的类实现。

    我们接下来看一下call函数的具体实现 

    Swift的Mirror反射怎么使用

    Swift的Mirror反射怎么使用

    call函数中有一个Switch方法。根据不同的类型,调用不同的ReflectionMirrorImpl类。 我们就取EnumIpml类去探个究竟。 

    Swift的Mirror反射怎么使用

    上面代码就是EnumIpml类的具体实现。首先是isReflectable()这个方法。这个方法返回这个类型是否可以被反射,也就是找到metadata,再找到metadata中存储的Description,通过它里面存储的 isReflectable来确定。

    接下来我们看一下getInfo方法。在代码里面我们可以看到,获取nameinfo属性信息主要是通过getFieldAt来获取。我们现在就去查看getFieldAt方法。具体代码如下:

    static std::pair<StringRef , FieldType >getFieldAt(const Metadata *base, unsigned index) {    using namespace reflection;    auto failedToFindMetadata = [&]() -> std::pair<StringRef, FieldType> {        auto typeName = swift_getTypeName(base,  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};

    这里可以看到可以看到 所有的信息都是通过MetadatagetDescription()FieldDescrition 这几个东西来去实现的,⼀个是当前类型的元数据,⼀个是当前类型的描述,⼀个是对当前类型属性的描述。

    Enum Metadata探索

    我们在类和结构体这篇文章里面,描述了类的Metadata结构,并把它的C++代码转换成了Swift代码。我们这次来尝试转换EnumstructMetaData结构。首先来探索EnumMetadata结构。

    还原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,        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,  false> Name;    TargetRelativeDirectPointer<Runtime, MetadataResponse(...),  true> AccessFunctionPtr;    TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor, 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中的属性、比如nameFields等,他们的类型都是用TargetRelativeDirectPointer来定义。我们现在来看一下TargetRelativeDirectPointer是什么? Swift的Mirror反射怎么使用

    从上面的源码定义可以知道,它是一个模板类,(接收三个参数,⼀个是Runtime, ⼀个是Pointee , Bool类型默认为True)。接下来我们看一下RelativeDirectPointer

      Swift的Mirror反射怎么使用

    这个指针类代码比较简单,其中 T 就是我们进来的类型,Offset就是int32_t 的类型,从字面意思上看应该是偏移量之类的。我们再看下它的get()方法。 

    Swift的Mirror反射怎么使用

    Swift的Mirror反射怎么使用

    从以上代码,我们可以看出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:  PlanetNumPayloadCasesAndPayloadSizeOffset  0NumEmptyCases  8

    Struct Metadata探索

    现在我们来解析一下struct类型的Metadata

    首先,和上面的Enum一样,通过全局搜索,找到structMetaDataTargetStructMetadata类。通过它的继承链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,                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`中的数据结构也可以修改一下```swiftstruct 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) -> UnsafeRawPointerprotocol 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.NumFieldsprint("当前结构体的名称: \(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: SifieldType: IntfieldValue: 18--- age 属性信息 ------ name 属性信息 ---mangledTypeName: SSfieldType: StringfieldValue: FWJ--- name 属性信息 ------ money 属性信息 ---mangledTypeName: SifieldType: IntfieldValue: 2000--- money 属性信息 ---
    swift_getTypeByMangledNameInContext 函数

    这个函数的源码在MetadataLookup.cpp文件中,我们来看一下它的具体实现

    SWIFT_CC(swift) SWIFT_RUNTIME_EXPORTconst Metadata * _Nullableswift_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反射怎么使用”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网精选频道。

    --结束END--

    本文标题: Swift的Mirror反射怎么使用

    本文链接: https://www.lsjlt.com/news/341334.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档
    猜你喜欢
    • Swift的Mirror反射怎么使用
      这篇文章主要介绍了Swift的Mirror反射怎么使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Swift的Mirror反射怎么使用文章都会有所收获,下面我们一起来看看吧。元类型与.selfAnyObjec...
      99+
      2023-07-02
    • Swift中反射Mirror怎么用
      小编给大家分享一下Swift中反射Mirror怎么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!前言Mirror是Swift中...
      99+
      2024-04-02
    • Swift进阶教程Mirror反射示例详解
      目录元类型与.selfAnyObjectAnyClassAnytype(Of:)selfself在方法里面的作用Self引用Swift RuntimeMirrorMirror的基本用...
      99+
      2024-04-02
    • 【javaSE】 反射与反射的使用
      文章目录 🌲反射的定义🎍反射的用途🌴反射基本信息🍀反射相关的类🚩Class类(反射机制的起源 )🎈...
      99+
      2023-09-16
      开发语言 java 反射
    • C#中怎么使用反射
      在C#中,可以使用System.Reflection命名空间来使用反射。以下是使用反射的一些常用方法: 获取类型信息: Type...
      99+
      2024-04-03
      C#
    • Java反射机制怎么使用
      今天小编给大家分享一下Java反射机制怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。反射的概述JAVA反射机制是在运...
      99+
      2023-06-30
    • Golang反射机制怎么使用
      本文小编为大家详细介绍“Golang反射机制怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Golang反射机制怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。介绍反射的本质就是在程序运行的时候,获...
      99+
      2023-06-30
    • Java反射的setAccessible()方法怎么使用
      本篇内容介绍了“Java反射的setAccessible()方法怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、 什么是Java的...
      99+
      2023-07-02
    • C++中怎么使用反射机制
      今天就跟大家聊聊有关C++中怎么使用反射机制,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。在Java编程中,我们经常要用到反射,通过反射机制实现在配置文件中的灵活配置, 但在C++编...
      99+
      2023-06-17
    • java注解和反射怎么使用
      Java注解和反射是Java编程中非常重要的概念。注解是一种元数据,可以用来为Java程序提供额外的信息,而反射则允许程序在运行时动...
      99+
      2023-05-29
      java注解和反射 java
    • Java基础之反射怎么使用
      本篇内容介绍了“Java基础之反射怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!反射是框架设计的灵魂(使用的前提条件:必须先得到代表...
      99+
      2023-07-05
    • Java中的注解和反射怎么使用
      这篇文章主要讲解了“Java中的注解和反射怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中的注解和反射怎么使用”吧!1、注解(Annotation)1.1 什么是注解(Ann...
      99+
      2023-07-02
    • PHP中的反射机制以及如何使用反射
      在 PHP 中,反射(Reflection)是一种机制,用于在运行时动态地获取类、接口、函数、方法等的信息。反射机制允许我们在运行时分析和修改代码结构,包括类的属性和方法等。 下面是一个简单的示例,展...
      99+
      2023-10-18
      php 开发语言 后端
    • React新框架Mirror怎么使用
      本篇内容介绍了“React新框架Mirror怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Mirror 是一款基于 React、Re...
      99+
      2023-06-04
    • 怎么使用java反射获取属性
      要使用Java反射获取属性,可以按照以下步骤进行:1. 获取要操作的类的Class对象:```javaClass clazz = Y...
      99+
      2023-09-04
      java
    • java反射之invoke方法怎么使用
      Java反射中的invoke方法用于调用指定对象的方法。它的使用方式如下:1. 获取Class对象:首先需要获取到要调用方法的对象的...
      99+
      2023-08-18
      java invoke
    • Golang使用反射的动态方法怎么调用
      这篇文章主要讲解了“Golang使用反射的动态方法怎么调用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang使用反射的动态方法怎么调用”吧!Go是一种静态类型的语言,提供了大量的安全...
      99+
      2023-07-05
    • Java反射如何使用
      这篇文章将为大家详细讲解有关Java反射如何使用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。反射相信刚接触Java的,第一句肯定会问什么是反射呢?反射有什么作用呢?为什么使用反射呢?首先反射是Java的...
      99+
      2023-06-25
    • C#反射如何使用
      这篇文章主要讲解了“C#反射如何使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C#反射如何使用”吧!explicit 和 implicit 的含义?explicit 和 implicit...
      99+
      2023-06-18
    • Java 反射使用大全
      前言 好多年前自己记的关于Java反射的笔记,这么些年下来,自己用到反射的时候就看这一份就够了。 但是笔记以文件形式存放不方便,所以将其放到CSDN中,也希望能给更多小伙伴带来帮助。 Class类 J...
      99+
      2023-09-26
      java android javaEE 反射 reflect
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作