美文网首页
Swift 五、Mirror源码分析 & StructMetad

Swift 五、Mirror源码分析 & StructMetad

作者: 常在士心 | 来源:发表于2022-01-20 17:05 被阅读0次
    Mirror源码分析.png

    一、Mirror的基本用法

    所谓反射就是可以动态获取类型、成员信息,在运⾏时可以调⽤⽅法、属性等⾏为的特性。在使⽤OC开发时很少强调其反射概念,因为OC的Runtime要⽐其他语⾔中的反射强⼤的多。但是 Swift 是⼀⻔类型安全的语⾔,不⽀持我们像 OC 那样直接操作,它的标准库仍然提供了反射机制来让我们访问成员信息。
    Swift 的反射机制是基于⼀个叫 Mirror 的结构体来实现的。为具体的实例创建⼀个Mirror对象,然后就可以通过它查询这个实例。

    class ZGTeacher {
        var age: Int = 18
        func teach() {
            print("teach")
        }
    }
    
    ///⾸先通过构造⽅法构建⼀个Mirror实例,这⾥传⼊的参数是 Any,也就意味着当前可以是类,结 构体,枚举等
    let mirror = Mirror(reflecting: ZGTeacher())
    
    ///接下来遍历 children 属性,这是⼀个集合
    for child in mirror.children {
        ///然后我们可以直接通过 label 输出当前的名称,value 输出当前反射的值
        print("\(String(describing: child.label)):\(child.value)")
    }
    

    lldb打印

    Optional("age"):18
    

    实际使用案例

    class ZGTeacher {
        var age: Int = 18
        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(ZGTeacher())
    print(reslut)
    

    我们想让我们的函数不管是结构体,类还是枚举还是基础类型也好,都能够具备这个⽅法。这个时候我们是不是就可以⽤协议来做?什么意思那?

    首先,我们定义一个协议,这里就表示如果想要具备模型转字典的方式,那么这里就要遵循这个协议,然后实现jsonMap的方法。

    protocol ZGJsonMap {
        func jsonMap() -> Any
    }
    

    但是大家想一下,这个test方法里面的功能是不是通用的啊,也就意味着我们不需要每一个遵循了ZGJsonMap协议的都自己实现,所以这里我们是不是说可以给这个协议一个默认的实现啊。

    class ZGTeacher {
        var age: Int = 18
        func teach() {
            print("teach")
        }
    }
    
    enum JSONMapError: Error {
        case emptyKey
        case notConformProtocol
    }
    
    protocol JSONMap {
      func jsonMap() throws -> Any
    }
    
    
    extension JSONMap {
      func jsonMap() throws -> 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.emptyKey
              }
           } else {
               return JSONMapError.notConformProtocol
           }
        }
    
        return result
      }
        
    }
    
    ///我们需要对当前的常见类型和自定义类型遵循JSONMap的协议
    ///这样对于没有value才能嵌套解析
    extension ZGTeacher: JSONMap{}
    extension Int: JSONMap{}
    extension String: JSONMap{}
    
    var t = ZGTeacher()
    
    var t1 = try? t.jsonMap()
    print(t1!)
    

    二、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 对象。代码展示如下:

    class ZGTeacher: CustomReflectable {
        var age: Int
        var name: String
        init(age: Int, name: String) {
            self.age = age
            self.name = name
        }
    
        var customMirror: Mirror {
            let info = KeyValuePairs<String, Any>.init(dictionaryLiteral: ("age", age),("name", name))
            let mirror = Mirror.init(self, children: info, displayStyle: .class, ancestorRepresentation: .generated)
            return mirror
        }
    }
    var t = ZGTeacher(age: 18, name: "zhang")
    print("end")
    

    实现这个 CustomReflectable 最直观的区别在于我们在 lldb debug 中会出现更详细的 debug 信息。

    po t
    ▿ <ZGTeacher: 0x1007405a0>
      - age : 18
      - name : "zhang"
    

    回到我们的源码当中,对字符串 Mirror(internalReflecting 在源码当中进⾏检索,我们 就能够快速定位到 ReflectionMirror.swift ⽂件

    extension Mirror {
      internal init(internalReflecting subject: Any,
                  subjectType: Any.Type? = nil,
                  customAncestor: Mirror? = nil)
      {
    ///subject:类型信息
        let subjectType = subjectType ?? _getNormalizedType(subject, type: type(of: subject)) 
    

    在当前文件中搜索getNormalizedType

    @_silgen_name("swift_reflectionMirror_normalizedType")
    internal func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type
    

    这⾥使⽤了⼀个编译器字段 @_silgen_name ,其实是Swift的⼀个隐藏符号,作⽤是将某个C/C++语⾔函数直接映射为Swift函数。
    什么意思那?我们的 Swift 是可以和C语⾔进⾏交互的,⽐如我们在C中定义⼀个函数:

    int zg_add(int a, int b) {
        return  a + b;
    }
    

    接下来如果我们的 Swift 想要使⽤这个函数,我们要怎么做?⾸先要在 .h 头⽂件中暴露这个函数:

    截屏2022-01-18 17.19.55.png

    然后在当前的桥接头⽂件当中暴露


    截屏2022-01-18 17.19.44.png

    这样我们就能在swift里直接使用了。


    截屏2022-01-18 17.22.25.png
    是不是⽐较麻烦,这⾥我们把头⽂件全部都删掉:
    @_silgen_name("zg_add")
    func swift_zg_add(a: Int32, b: Int32) -> Int32
    var value = swift_zg_add(a: 10, b: 20)
    print(value)
    

    这里一样可以正常调用,是不是很简洁方便哪?
    回到我们的源代码,搜索getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type 最终调⽤的是 ReflectionMirror.cpp 中的 C++ 代码。

    // func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type
    SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
    const Metadata *swift_reflectionMirror_normalizedType(OpaqueValue *value,
                                                          const Metadata *type,
                                                          const Metadata *T) {
      return call(value, T, type, [](ReflectionMirrorImpl *impl) { return impl->type; });
    }
    

    我们往上翻⼀翻就能够找到 call 函数的实现,这⾥其实是⼀个回调函数,当前回调的具体数据都是由 ReflectionMirrorImpl 结构体实现。

    auto call = [&](ReflectionMirrorImpl *impl) {
        impl->type = type;
        impl->value = value;
        auto result = f(impl);
        return result;
      };
    

    ReflectionMirrorImpl 结构体的具体实现(可以看到这是⼀个抽象类,也就意味着不同类型的反射都需要去实现 ReflectionMirrorImpl ),这⾥我们在下⾯的代码中也能看到 class, struct, enum, Tuple的具体实现。

    truct ReflectionMirrorImpl {
      const Metadata *type;
      OpaqueValue *value;
      
      virtual char displayStyle() = 0;
      virtual intptr_t count() = 0;
      virtual intptr_t childOffset(intptr_t index) = 0;
      virtual const FieldType childMetadata(intptr_t index,
                                            const char **outName,
                                            void (**outFreeFunc)(const char *)) = 0;
      virtual AnyReturn subscript(intptr_t index, const char **outName,
                                  void (**outFreeFunc)(const char *)) = 0;
      virtual const char *enumCaseName() { return nullptr; }
    
    #if SWIFT_OBJC_INTEROP
      virtual id quickLookObject() { return nil; }
    #endif
      
      // For class types, traverse through superclasses when providing field
      // information. The base implementations call through to their local-only
      // counterparts.
      virtual intptr_t recursiveCount() {
        return count();
      }
      virtual intptr_t recursiveChildOffset(intptr_t index) {
        return childOffset(index);
      }
      virtual const FieldType recursiveChildMetadata(intptr_t index,
                                                     const char **outName,
                                                     void (**outFreeFunc)(const char *))
      {
        return childMetadata(index, outName, outFreeFunc);
      }
    
      virtual ~ReflectionMirrorImpl() {}
    };
    
    

    这⾥我们⾸先以 struct 为例来看⼀下 Mirror 都是如何获取到这些数据的。
    当前的属性数量 (可以看到的是,这⾥通过 Metadata 中的 getDescription查询字段 NumFields )

    intptr_t count() override {
        if (!isReflectable()) {
          return 0;
        }
    
        auto *Struct = static_cast<const StructMetadata *>(type);
        return Struct->getDescription()->NumFields;
      }
    

    其中 getDescription()

    const TargetStructDescriptor<Runtime> *getDescription() const {
        return llvm::cast<TargetStructDescriptor<Runtime>>(this->Description);
      }
    

    又⽐如具体属性的获取

    AnyReturn subscript(intptr_t i, const char **outName,
                          void (**outFreeFunc)(const char *)) override {
        unsigned tag;
        const Metadata *payloadType;
        bool indirect;
    
        auto *caseName = getInfo(&tag, &payloadType, &indirect);
    
        // Copy the enum itself so that we can project the data without destroying
        // the original.
        Any enumCopy;
        auto *enumCopyContainer
          = type->allocateBoxForExistentialIn(&enumCopy.Buffer);
        type->vw_initializeWithCopy(enumCopyContainer,
                                    const_cast<OpaqueValue *>(value));
    
        // Copy the enum payload into a box
        const Metadata *boxType = (indirect ? &METADATA_SYM(Bo).base : payloadType);
        BoxPair pair = swift_allocBox(boxType);
        type->vw_destructiveProjectEnumData(enumCopyContainer);
        boxType->vw_initializeWithTake(pair.buffer, enumCopyContainer);
        type->deallocateBoxForExistentialIn(&enumCopy.Buffer);
        
        value = pair.buffer;
    
        // If the payload is indirect, we need to jump through the box to get it.
        if (indirect) {
          const HeapObject *owner = *reinterpret_cast<HeapObject * const *>(value);
          value = swift_projectBox(const_cast<HeapObject *>(owner));
        }
        
        *outName = caseName;
        *outFreeFunc = nullptr;
        
        Any result;
    
        result.Type = payloadType;
        auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer);
        result.Type->vw_initializeWithCopy(opaqueValueAddr,
                                           const_cast<OpaqueValue *>(value));
    
        swift_release(pair.object);
        return AnyReturn(result);
      }
    

    可以看到是这⾥通篇都是通过 Metadata , getDescription() , FieldDescrition 这⼏个东⻄来去实现的,⼀个是当前类型的元数据,⼀个是当前类型的描述,⼀个是对当前类型属性的描述。所以看到这⾥我们能够明⽩ Mirror 是如何⼯作的。
    同时我们在前⾯的探索过程中也探索了类,结构体和枚举,接下来我们尝试能不能⾃⼰利⽤ Metadata 来还原获取当前类型的各种属性。

    三、StructMetadata源码分析

    3.1 TargetStructMetadata

    • 首先我们搜索TargetStructMetadata,找到TargetStructMetadata的定义
    template <typename Runtime>
    struct TargetStructMetadata : public TargetValueMetadata<Runtime> {
        /// 此处会返回一个TargetStructDescriptor类型的Description
      const TargetStructDescriptor<Runtime> *getDescription() const {
        return llvm::cast<TargetStructDescriptor<Runtime>>(this->Description);
      }
    
    };
    
    TargetStructMetadata.png
    可以看出TargetStructMetadata继承自TargetValueMetadata
    • 搜索TargetValueMetadata,找到TargetValueMetadata的定义
    struct TargetValueMetadata : public TargetMetadata<Runtime> {
      
      TargetSignedPointer<Runtime, const TargetValueTypeDescriptor<Runtime> *
      __ptrauth_swift_type_descriptor> Description;
    
    };
    
    TargetValueMetadata.png
    我们只看到有一个Description属性,它的类型是TargetValueTypeDescriptor
    TargetValueMetadata 继承自TargetMetadata
    • 搜索TargetMetadata,找到TargetMetadata的定义
    struct TargetMetadata {
      
    private:
      /// The kind. Only valid for non-class metadata; getKind() must be used to get
      /// the kind value.
      StoredPointer Kind;
    
    };
    
    TargetMetadata.png
    我们只看到它有一个Kind属性。

    3.2 TargetValueTypeDescriptor

    • 首先我们根据TargetStructMetadata,找到TargetStructMetadata定义中的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>> {
    
    public:
     
      uint32_t NumFields;
      
      uint32_t FieldOffsetVectorOffset;
      
    
    };
    
    
    TargetStructDescriptor.png
    TargetStructDescriptor2.png
    我们可以发现TargetStructDescriptor有两个属性,分别是NumFieldsFieldOffsetVectorOffset
    NumFields主要表示结构体中属性的个数,如果只有一个字段偏移量则表示偏移量的长度。
    FieldOffsetVectorOffset表示这个结构体元数据中存储的属性的字段偏移向量的偏移量,如果是0则表示没有。
    TargetStructDescriptor 继承自 TargetValueTypeDescriptor
    • 点击TargetValueTypeDescriptor,找到TargetValueTypeDescriptor的定义
    class TargetValueTypeDescriptor
        : public TargetTypeContextDescriptor<Runtime> {
    public:
      static bool classof(const TargetContextDescriptor<Runtime> *cd) {
        return cd->getKind() == ContextDescriptorKind::Struct ||
               cd->getKind() == ContextDescriptorKind::Enum;
      }
    };
    
    
    TargetValueTypeDescriptor.png

    我们并没有找到太多有用的信息,那么我们继续向父类寻找。

    • 点击TargetTypeContextDescriptor,找到TargetTypeContextDescriptor的定义
    class TargetTypeContextDescriptor
        : public TargetContextDescriptor<Runtime> {
    public:
      
      TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false> Name;
    
      TargetRelativeDirectPointer<Runtime, MetadataResponse(...),
                                  /*Nullable*/ true> AccessFunctionPtr;
      
      TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor,
                                  /*nullable*/ true> Fields;
     
    };
    
    TargetTypeContextDescriptor.png
    我们发现TargetTypeContextDescriptorName、AccessFunctionPtr、Fields三个属性:
    Name就是类型的名称
    AccessFunctionPtr是该类型元数据访问函数的指针
    Fields 是一个指向该类型的字段描述符的指针
    接下来我们再看看TargetTypeContextDescriptor的父类中还有什么有用的信息
    • 点击TargetContextDescriptor,找到TargetContextDescriptor的定义
    struct TargetContextDescriptor {
      
      ContextDescriptorFlags Flags;
      
      TargetRelativeContextPointer<Runtime> Parent;
    };
    
    TargetContextDescriptor.png
    这里我们可以看到:
    1、这就是descriptor的基类
    2、有两个属性,分别是FlagsParent
    3、Flags是描述上下文的标志,包括它的种类和格式版本。
    4、Parent是记录父类上下文的,如果是顶级则为null

    3.3 Description中的属性

    • Flags
      Flags.png
      从截图的源码中我们可以看到这个FLags实际是个uint32_t的值,按位存储着kind、isGeneric、isUnique、version、kindSpecificFlags等信息。
    • Parent
      Parent的类型是TargetRelativeContextPointer<Runtime>,我们来看一下TargetRelativeContextPointer,点击跳转过去:
      TargetRelativeContextPointer.png
      我们可以看到TargetRelativeContextPointer是取自RelativeIndirectablePointer的别名,继续点击进行查看:
      RelativeIndirectablePointer.png
      根据注释知道这个类的主要作用是存储在内存中的对象的相对引用。通过RelativeOffsetPlusIndirect属性存储相对的地址偏移量。
      再通过get()函数获取,在get()函数中,会调用applyRelativeOffset函数,进行地址的偏移。 applyRelativeOffset.png
    applyRelativeOffset2.png
    最后返回的时候我们可以看到base + extendOffset,基地址加上偏移的值,最后得到真实的地址。
    • Fields
      TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor,
                                  /*nullable*/ true> Fields;
    

    这里看看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;
    
    
      const FieldDescriptorKind Kind;
      const uint16_t FieldRecordSize;
      const uint32_t NumFields;
    };
    

    这里有5个属性:

    1、MangledTypeName

    2、Superclass

    3、kind

    4、FieldRecordSize

    5、NumFields
    关于getFieldRecordBuffer函数的返回值FieldRecord源码如下:

    FieldRecord.png
    FieldRecord主要是封装了一些属性,用于存储这些值。

    3.4 type

    我们来看一下type是怎么取的:
    调用swift_reflectionMirror_normalizedType函数

    // func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type
    SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
    const Metadata *swift_reflectionMirror_normalizedType(OpaqueValue *value,
                                                          const Metadata *type,
                                                          const Metadata *T) {
      return call(value, T, type, [](ReflectionMirrorImpl *impl) { return impl->type; });
    }
    

    比如说这是个结构体,此时的impl就是个StructImpl类型,所以这里的type是StructImpl父类ReflectionMirrorImpl的属性type

    3.5 count

    调用swift_reflectionMirror_count函数

    
    // func _getChildCount<T>(_: T, type: Any.Type) -> Int
    SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
    intptr_t swift_reflectionMirror_count(OpaqueValue *value,
                                          const Metadata *type,
                                          const Metadata *T) {
      return call(value, T, type, [](ReflectionMirrorImpl *impl) {
        return impl->count();
      });
    }
    

    同样还以结构体为例,此时的implStructImpl,内部的count()函数

    structImpl.png
    这里的Struct就是个TargetStructMetadata类型,通过getDescription()函数获取到一个TargetStructDescriptor类型的Description,然后取NumFields**的值就是我们要的**count`

    3.6 属性名和属性值

    我们知道在Mirror中通过其初始化方法返回一个提供该值子元素的AnyCollection<Child>类型的children集合,children是一个元组(label: String?, value: Any)label是一个可选类型的属性名,value是属性值。

    WX20220119-211231@2x.png
    在分析internalReflecting函数的时候,我们说children是懒加载的,而加载的时候会调用getChild方法,getChild方法源码入下:
    getChild.png
    getChild方法中还会调用_getChild方法,源码如下:
    _getChild.png
    _getChild方法同样是使用@_silgen_name修饰符最终调用的C++中的swift_reflectionMirror_subscript函数。
    SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
    AnyReturn swift_reflectionMirror_subscript(OpaqueValue *value, const Metadata *type,
                                               intptr_t index,
                                               const char **outName,
                                               void (**outFreeFunc)(const char *),
                                               const Metadata *T) {
      return call(value, T, type, [&](ReflectionMirrorImpl *impl) {
        return impl->subscript(index, outName, outFreeFunc);
      });
    }
    

    这里我们可以看到是调用了implsubscript函数,同样以结构体为例,我们在StructImpl中找到该函数,源码如下:

    childMetadata.png
    通过subscript函数我们可以看到这里面还会调用childMetadata获取到fieldInfo,其实这里就是获取type,也就是属性名,通过childOffset函数和index获取到对应的偏移量,最后根据内存偏移取到属性值。childMetadata核心点是调用getFieldAt函数获取属性名称
    getFieldAt(const Metadata *base, unsigned index) {
      using namespace reflection;
      
      // If we failed to find the field descriptor metadata for the type, fall
      // back to returning an empty tuple as a standin.
      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};
    }
    

    我们可以看到在上面这个方法中:

    • 首先通过getTypeContextDescriptor获取baseDesc,也就是我们说的Description
    • 然后通过Fields.get()获取到fields
    • 接着通过getFields()[index]获取对应的field
    • 最后通过getFieldName()函数获取到属性名称
    • getTypeContextDescriptor函数在struct TargetMetadata中,通过这个函数获取到一个TargetStructDescriptor,它的父类的父类TargetTypeContextDescriptor中的Fields属性
    • Fields属性的类型TargetRelativeDirectPointer中有get方法
    • 实际中使用的FieldDescriptor类中getFieldRecordBuffer方法返回的FieldRecord中的getFieldName函数
      getFields 源码
    const_iterator begin() const {
        auto Begin = getFieldRecordBuffer();
        auto End = Begin + NumFields;
        return const_iterator { Begin, End };
      }
    
      const_iterator end() const {
        auto Begin = getFieldRecordBuffer();
        auto End = Begin + NumFields;
        return const_iterator { End, End };
      }
    
      llvm::ArrayRef<FieldRecord> getFields() const {
        return {getFieldRecordBuffer(), NumFields};
      }
    

    关于getFields我们可以看到这是一块连续的空间,在begin和end中:

    • begin就是getFieldRecordBuffer
    • getFieldRecordBuffer就是Begin + NumFields
    • 所以这就是一块连续内存的访问
      childOffset 源码
      分析完了属性名的获取,我们来看看偏移量的获取。
    intptr_t childOffset(intptr_t i) override {
        auto *Clas = static_cast<const ClassMetadata*>(type);
        auto description = Clas->getDescription();
    
        if (i < 0 || (size_t)i > description->NumFields)
          swift::crash("Swift mirror subscript bounds check failure");
    
        // FIXME: If the class has ObjC heritage, get the field offset using the ObjC
        // metadata, because we don't update the field offsets in the face of
        // resilient base classes.
        uintptr_t fieldOffset;
        if (usesNativeSwiftReferenceCounting(Clas)) {
          fieldOffset = Clas->getFieldOffsets()[i];
        } else {
      #if SWIFT_OBJC_INTEROP
          Ivar *ivars = class_copyIvarList(
              reinterpret_cast<Class>(const_cast<ClassMetadata *>(Clas)), nullptr);
          fieldOffset = ivar_getOffset(ivars[i]);
          free(ivars);
      #else
          swift::crash("Object appears to be Objective-C, but no runtime.");
      #endif
        }
        return (intptr_t)fieldOffset;
      }
    

    这里面是调用TargetStructMetadata中的getFieldOffsets函数源码如下:

    const uint32_t *getFieldOffsets() const {
        auto offset = getDescription()->FieldOffsetVectorOffset;
        if (offset == 0)
          return nullptr;
        auto asWords = reinterpret_cast<const void * const*>(this);
        return reinterpret_cast<const uint32_t *>(asWords + offset);
      }
    

    我们可以看到这里统一是通过获取Description中的属性,这里使用的属性是FieldOffsetVectorOffset。获取到偏移值后通过内存偏移即可获取到属性值。

    四、还原StructMetadata

    4.1 TargetStructMetadata

    首先我们需要拥有一个结构体的元数据结构,这里我们命名为StructMetadata,里面有继承的kindDescriptor属性,这里的Descriptor属性是一个TargetStructDescriptor类型的指针。

    struct TargetStructMetadata {
        var Kind: Int
        var typeDescription: UnsafeMutablePointer<TargetStructDescriptor>
    }
    

    4.2 TargetStructDescriptor

    对于结构体来说其内部有7个属性
    1、flag是个32位的整形,我们用Int32代替
    2、parent是记录父类的,类型TargetRelativeDirectPointer<Runtime>,这里也可以用Int32代替
    3、name是记录类型的,它的类型是TargetRelativeDirectPointer<char>,所以我们需要实现一个TargetRelativeDirectPointer
    4、AccessFunctionPtrname类似,内部是个指针
    5、Fields也与name类似,内部是个FieldDescriptor
    6、NumFields使用Int32
    7、FieldOffsetVectorOffset也是用Int32
    仿写实现如下:

    struct TargetStructDescriptor {
        var Flags: Int32
        var Parent: Int32
        var Name: TargetRelativeDirectPointer<CChar>
        var AccessFunctionPtr: TargetRelativeDirectPointer<UnsafeRawPointer>
        var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor>
        var NumFields: Int32
        /// 每一个属性距离当前实例对象地址的偏移量
        var FieldOffsetVectorOffset: Int32
            
        var genericArgumentOffset: Int {
            return 2
        }
    }
    

    4.3 TargetRelativeDirectPointer

    • TargetRelativeDirectPointerRelativeDirectPointer的别名,其内部有一个继承的RelativeOffset属性,是int32_t类型,我们可以用Int32代替。
    • 还有一个get方法,内部通过指针偏移获取值。
    struct TargetRelativeDirectPointer<Pointee> {
        var offset: Int32
        ///模拟RelativeDirectPointerImpl类中的get方法 this+offset指针
        mutating func getmeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {
            let offset = self.offset
            return withUnsafePointer(to: &self) { p in
                /*
                 获得self,变为raw,然后+offset
                 
                 - UnsafeRawPointer(p) 表示this
                 - advanced(by: numericCast(offset) 表示移动的步长,即offset
                 - assumingMemoryBound(to: T.self) 表示假定类型是T,即自己指定的类型
                 - UnsafeMutablePointer(mutating:) 表示返回的指针类型
                */
                return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: Pointee.self))
            }
        }
    }
    

    4.4 FieldDescriptor

    FieldDescriptorMirror反射中有着很重要的作用,其内部有5个属性:

    • MangledTypeNameRelativeDirectPointer<const char>类型,我们使用TargetRelativeDirectPointer<CChar>代替
    • SuperclassMangledTypeName一样
    • KindFieldDescriptorKind类型,实际是uint16_t,这里我们使用UInt16代替
    • FieldRecordSizeUInt16也使用使用UInt16代替
    • NumFields使用Int32代替
    • fields,其实从属性上看不到有这个,但是这里面有个getFieldRecordBuffer方法,通过this+1的方式一个一个的访问属性,所以这是一块连续的内存空间,我们使用fields代替
    struct FieldDescriptor {
        var MangledTypeName: TargetRelativeDirectPointer<CChar>
        var Superclass: TargetRelativeDirectPointer<CChar>
        var Kind: UInt16
        var FieldRecordSize:UInt16
        var NumFields: UInt32
        var fields: FiledRecordBuffer<FieldRecord>
    }
    

    4.5 FieldRecord

    FieldRecord存储着属性的相关信息,其内部有三个属性

    • FlagsFieldRecordFlags类型实际是uint32_t,这里我们使用Int32代替
    • MangledTypeName使用TargetRelativeDirectPointer<CChar>代替
    • FieldName使用TargetRelativeDirectPointer<CChar>代替
    struct FieldRecord {
        var Flags: UInt32
        var MangledTypeName: TargetRelativeDirectPointer<CChar>
        var FieldName: TargetRelativeDirectPointer<CChar>
    }
    
    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 ZGPerson {
        var age: Int = 18
        var name: String = "Zhang"
    }
    
    var p = ZGPerson()
    

    5.1 绑定结构体内存

    使用unsafeBitCast按位强转,将ZGPerson绑定到StructMetadata上,注意,这个操作非常危险,没有任何校验和修饰。

    let ptr = unsafeBitCast(ZGPerson.self as Any.Type, to: UnsafeMutablePointer<TargetStructMetadata>.self)
    

    5.2 打印类名和属性个数

    let typeDescription = ptr.pointee.typeDescription
    
    let namePtr = typeDescription.pointee.Name.getmeasureRelativeOffset()
    print("current class name: \(String(cString: namePtr))")
    
    let numFields = typeDescription.pointee.NumFields
    print("当前类属性的个数:\(numFields)")
    

    5.3 打印属性名称和属性值

    1、打印一下属性的名称,首先是获取到FieldDescriptor的指针,然后通过内存偏移的方式访问每一个FieldRecord,最后再访问FieldRecord中的属性名
    2、打印属性值:

    • 首先获取FieldOffsetVectorOffset的值
    • 然后再加上this也就是当前Metadata的指针
    • 这里我们将仿写的StructMetadata的指针ptr重绑定为Int
    • 源码中加上FieldOffsetVectorOffset,这里我们就移动FieldOffsetVectorOffset
    • 然后将上述移动后的绑定为一个Int32的指针
    • 最后使用UnsafeBufferPointer属性个数创建一个buffer数组指针
    • 接下来我们就可以从数组中取出每个属性的偏移值
    • 然后取出结构体实例p的内存地址
    • 然后按照buffer数组中的偏移值进行偏移,重绑定为属性的类型
    • 最后就可以打印出属性值
    var bufferPtr = UnsafeBufferPointer(start: UnsafeRawPointer(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self).advanced(by: numericCast(ptr.pointee.typeDescription.pointee.FieldOffsetVectorOffset))).assumingMemoryBound(to: Int32.self), count: Int(ptr.pointee.typeDescription.pointee.NumFields))
    
    for i in 0 ..< numFields {
        let fieldDespritor = typeDescription.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.FieldName.getmeasureRelativeOffset()
        print("--- fixed \(String(cString: fieldDespritor)) info begin ---")
    
        let mangledTypeName = typeDescription.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.MangledTypeName.getmeasureRelativeOffset()
    
        let genericVector = UnsafeRawPointer(ptr).advanced(by: typeDescription.pointee.genericArgumentOffset * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self)
    
        let fieldType = swift_getTypeByMangledNameInContext(mangledTypeName, 256, UnsafeRawPointer(typeDescription), UnsafeRawPointer(genericVector).assumingMemoryBound(to: Optional<UnsafeRawPointer>.self))
    
        let type = unsafeBitCast(fieldType, to: Any.Type.self)
        let value = customCast(type: type)
        let fieldOffset = bufferPtr[Int(i)]
        let valuePtr = withUnsafeMutablePointer(to: &p) { $0 }
        print("fieldType: \(type) \nfieldValue: \(value.get(from: UnsafeRawPointer(UnsafeRawPointer(valuePtr).advanced(by: numericCast(fieldOffset)))))")
        print("--- field: \(String(cString: fieldDespritor)) info end ---\n")
    }
    

    本文部分资料参考了下列文献
    作者:愤怒的apple
    链接:https://juejin.cn/post/7052644744693809183/
    来源:稀土掘金

    相关文章

      网友评论

          本文标题:Swift 五、Mirror源码分析 & StructMetad

          本文链接:https://www.haomeiwen.com/subject/qupkhrtx.html