元类型与.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来表明某种类型的instance

Swift进阶(七)—— 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.self:T是实例目标,当时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反射
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 /*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};

这儿能够看到能够看到 一切的信息都是经过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,
/*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中的特点、比方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: Planet
NumPayloadCasesAndPayloadSizeOffset 0
NumEmptyCases 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,
/*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就能够获取实在的值