美文网首页Swift学习SwiftSwift
Swift Mirror原理解析

Swift Mirror原理解析

作者: 深圳_你要的昵称 | 来源:发表于2021-01-27 09:42 被阅读0次

    前言

    上篇Swift Mirror & Error主要是讲解了Mirror的一个常见的应用场景:JSON解析,但是里面的原理是怎样的?底层源码流程是如何处理反射呢?本篇文章将为大家详细解析Mirror的底层实现流程。

    一、Mirror架构大致分析

    首先我们大致来看看Mirror的架构,大概有哪些部分构成👇

    1. Mirror.swift源码路径👇

    swift->stdlib->public->core->Mirror.swift

    2.与反射相关的API

    • ReflectionMirror.swift
    • ReflectionMirror.mm

    其中,ReflectionMirror.swift中定义的函数,会通过@_silgen_name修饰符,这个修饰符的作用就是Swift编译器会将该修饰符修饰的函数符号映射成C++的函数符号

    @_silgen_name示例

    通常,我们在swift的工程中想调用C/C++的函数,一般是这样处理(我们以C函数为例看看):

    1. 先在.h中声明c函数,在.c中实现
    1. 在桥接.h文件中引入C函数的头文件.h
    1. 在.swift中使用C函数

    其实,我们也可以省去第2步,在.swift中使用@_silgen_name修饰符👇

    @_silgen_name("add")
    func swift_add(_ a :Int32, _ b :Int32) -> Int32
    
    var value = swift_add(10, 20)
    print(value)
    

    二、Mirror底层流程解析

    接下来,我们看看Mirror的源码👇

    接着,我们看internalReflecting源码,搜索internalReflecting,发现是在ReflectionMirror.swift中👇

    2.1 internalReflecting源码分析

    再来看初始化internalReflecting源码中调用的几个关键函数:

    _getNormalizedType

    根据上面对Mirror的初始化函数的分析得知,_getNormalizedType是获取当前对象的类型👇

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

    对应的C++函数是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; });
    }
    

    调用的是call

    getChild

    再来看getChild,即获取属性值👇

    internal func getChild<T>(of value: T, type: Any.Type, index: Int) -> (label: String?, value: Any) {
      var nameC: UnsafePointer<CChar>? = nil
      var freeFunc: NameFreeFunc? = nil
      
      let value = _getChild(of: value, type: type, index: index, outName: &nameC, outFreeFunc: &freeFunc)
      
      let name = nameC.flatMap({ String(validatingUTF8: $0) })
      freeFunc?(nameC)
      return (name, value)
    }
    

    其中最关键的就是let value = _getChild(of: value, type: type, index: index, outName: &nameC, outFreeFunc: &freeFunc),继续看看_getChild👇

    @_silgen_name("swift_reflectionMirror_subscript")
    internal func _getChild<T>(
      of: T,
      type: Any.Type,
      index: Int,
      outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
      outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
    ) -> Any
    

    同理,所对应的C++函数是swift_reflectionMirror_subscript👇

    // func _getChild<T>(
    //   of: T,
    //   type: Any.Type,
    //   index: Int,
    //   outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
    //   outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
    // ) -> Any
    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);
      });
    }
    

    最终也是调用的call

    _getDisplayStyle

    _getDisplayStyle是获取当前对象subject的显示类型,源码如下👇

    @_silgen_name("swift_reflectionMirror_displayStyle")
    internal func _getDisplayStyle<T>(_: T) -> CChar
    

    同理,搜索swift_reflectionMirror_displayStyle👇

    // func _getDisplayStyle<T>(_: T) -> CChar
    SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
    char swift_reflectionMirror_displayStyle(OpaqueValue *value, const Metadata *T) {
      return call(value, T, nullptr, [](ReflectionMirrorImpl *impl) { return impl->displayStyle(); });
    }
    

    还是call

    最关键的call函数

    call函数的源码很长,我们一部分一部分的看👇

    • 入参 & 返回值
    template<typename F>
    auto call(OpaqueValue *passedValue, const Metadata *T, const Metadata *passedType,
              const F &f) -> decltype(f(nullptr))
    
    1. passedValue 👉 一个指针,指向swift调用方传递来的值
    2. T 👉 该值的静态类型
    3. passedType 👉 显式传入的且在反射过程中的用到的类型
    4. f 👉 传递被查找到的会被调用的实现的引用对象,可以理解是个闭包的引用对象
    5. 返回值 👉 返回f参数调用后的返回值
    • 部分一:确定反射的真实类型

    其中unwrapExistential源码(其实就是强制类型转换,并取值)👇

    ReflectionMirrorImpl后面再分析。

    • 部分二:针对【类对象】的处理

    *部分三:其它类型的处理

    以上就是对call函数源码的大致流程的分析,最关键的一步就是使用ReflectionMirrorImpl子类的实例去调用f,然后封装成一个匿名函数call(类似于闭包),再根据type对应的类型type->getKind()去走真正的反射流程。

    2.2 调试验证:_getNormalizedType

    上面的源码看起来是不是有些费劲,没关系,接下来我们来断点调试一下_getNormalizedType函数,看看传递的入参具体是什么?

    先分别给swift_reflectionMirror_normalizedTypecall打上断点,如下图所示:

    运行源码,再准备调试示例代码,输入到终端👇

    class LGTeacher{var age = 18}
    var t = LGTeacher()
    let mirror = Mirror(reflecting: t)
    

    触发断点如下图👇(其中var t = LGTeacher()这句也会触发,直接跳过)

    注意:swift源码调试起来很卡,经常容易中断😂

    2.3 ReflectionMirrorImpl 反射子类

    ReflectionMirrorImpl的子类主要有以下几种:

    1. TupleImpl 元组的反射
    2. StructImpl 结构体的反射
    3. EnumImpl 枚举的反射
    4. ClassImpl 类的反射
    5. MetatypeImpl 元数据的反射
    6. OpaqueImpl 不透明类型的反射

    首先查看ReflectionMirrorImpl的底层定义

    我们以类ClassImpl为例,看看👇

    ClassImpl与其它数据结构的不同在于,需要考虑继承链关系,于是多出了父类递归处理的一些函数。其实仔细看看类ClassImpl反射子类中对属性的操作处理,都是先找到metadata,然后找到其description,再根据偏移值fieldOffset,就可得到真正索引i对应的字段(即属性)。

    ClassMetadata

    其中,类Classmetadata类型就是ClassMetadata👇

    ClassMetadata定义👇

    using ClassMetadata = TargetClassMetadata<InProcess>;
    👇
    struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
    ...
    }
    
    👇
    struct TargetAnyClassMetadata : public TargetHeapMetadata<Runtime> {
    ...
    }
    
    👇
    struct TargetHeapMetadata : TargetMetadata<Runtime> {
    ...
    }
    

    根据继承链,最终定位到我们熟悉的TargetMetadata,之前在Swift编译流程 & Swift类中分析过,TargetMetadata中有一个属性kind(相当于OC中的isa),而TargetClassMetadata除了拥有父类的kind,还有一个description,用于记录元数据的描述👇

    TargetClassDescriptor

    接着来到TargetClassDescriptor👇


    TargetClassDescriptor有关键的两个成员变量NumFieldsFieldOffsetVectorOffset,同时继承链的父类包含TargetTypeContextDescriptor👇

    继续沿着继承链向上查找,来到TargetContextDescriptor👇

    template <typename Runtime>
    class TargetTypeContextDescriptor : public TargetContextDescriptor<Runtime>
    

    其中包含2个重要成员变量FlagsParent👇

    至此,结合上面的对ClassMetadataTargetClassDescriptor这两个关键类型的分析,我们在反射中对属性的处理是通过getFieldAt函数👇

    上图可知,getFieldAt中是通过Metadata获取Fields字段,进而getFieldName得到字段名称,而Fields的类型TargetRelativeDirectPointer,其内部的描述信息类型是FieldDescriptor

    偏移量的计算

    上述已拿到了属性名称,剩下的就是属性值的处理,上述已经分析过,属性的是通过偏移量相加来计算的,而这个偏移量也是存储在TargetRelativeDirectPointer指针中(也是Fields中),同理,根据继承链,向上查找👇

    using TargetRelativeDirectPointer
      = typename Runtime::template RelativeDirectPointer<Pointee, Nullable>;
    👇
    class RelativeDirectPointer<T, Nullable, Offset,
        typename std::enable_if<!std::is_function<T>::value>::type>
        : private RelativeDirectPointerImpl<T, Nullable, Offset> {
    ...
    }
    👇
    template<typename T, bool Nullable, typename Offset>
    class RelativeDirectPointerImpl {
    private:
      /// The relative offset of the function's entry point from *this.
      Offset RelativeOffset;
    ...
    }
    

    最终定位到属性RelativeOffset,用于表示属性的相对偏移值,而不是直接存储地址,如下图所示👇

    偏移的计算相关的底层源码👇

      using ValueTy = T;
      using PointerTy = T*;
    
      PointerTy get() const & {
        // Check for null.
        if (Nullable && RelativeOffset == 0)
          return nullptr;
        
        // The value is addressed relative to `this`.
        uintptr_t absolute = detail::applyRelativeOffset(this, RelativeOffset);
        return reinterpret_cast<PointerTy>(absolute);
      }
    
    // 其中,applyRelativeOffset的源码 👇
    template<typename BasePtrTy, typename Offset>
    static inline uintptr_t applyRelativeOffset(BasePtrTy *basePtr, Offset offset) {
      static_assert(std::is_integral<Offset>::value &&
                    std::is_signed<Offset>::value,
                    "offset type should be signed integer");
    
      // 指针地址
      auto base = reinterpret_cast<uintptr_t>(basePtr);
      // We want to do wrapping arithmetic, but with a sign-extended
      // offset. To do this in C, we need to do signed promotion to get
      // the sign extension, but we need to perform arithmetic on unsigned values,
      // since signed overflow is undefined behavior.
      auto extendOffset = (uintptr_t)(intptr_t)offset;
      // 指针地址+存放的offset(偏移地址) --> 内存平移获取值
      return base + extendOffset;
    }
    
    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;
    
        ......
    
      const FieldDescriptorKind Kind;
      const uint16_t FieldRecordSize;
      const uint32_t NumFields;
      
        ......
      
      // 获取所有属性,每个属性用FieldRecord封装
       llvm::ArrayRef<FieldRecord> getFields() const {
        return {getFieldRecordBuffer(), NumFields};
      }
      
      ......
    } 
    

    其中,FieldRecord是对属性的一个封装,定义👇

    class FieldRecord {
      const FieldRecordFlags Flags;
    
    public:
      const RelativeDirectPointer<const char> MangledTypeName;
      const RelativeDirectPointer<const char> FieldName;
    

    三、仿写Mirror

    以上主要分析了类Class通过Mirror反射获取属性和值,还有涉及的重要的类和结构体的相关源码。现在我们以结构体为例,仿写Mirror代码👇

    // 结构体类型
    struct StructMetadata {
        var kind:        Int
        var description: UnsafeMutablePointer<StructMetadataDesc>
    }
    
    // 结构体类型的描述
    struct StructMetadataDesc {
        var flags:                   UInt32
        var parent:                  UInt32  // 展示用Uint32代替,实际是相同大小的结构体,
        var name:                    RelativeDirectPointer<CChar>  // 不在乎具体类型,就先用UnsafeRawPointer
        var accessFunctionPtr:       RelativeDirectPointer<UnsafeRawPointer>  // 不在乎具体类型,就先用UnsafeRawPointer
        var fields:                  RelativeDirectPointer<FieldDescription>  // 记录所有属性内容
        var numFields:               UInt32   // 属性个数
        var fieldOffsetVectorOffset: UInt32
    }
    
    // 记录结构体内所有属性的结构
    struct FieldDescription {
        var MangledTypeName:         RelativeDirectPointer<CChar>
        var Superclass:              RelativeDirectPointer<CChar>
        var Kind:                    UInt16
        var FieldRecordSize:         UInt16
        var NumFields:               UInt32
        var fields:                  FieldRecord // 连续存储空间 (有几个数据,就会在后面添加几个记录,通过内存平移读取)
    }
    
    // 每个属性的内容
    struct FieldRecord {
        var flag:                    Int32
        var mangledTypeName:         RelativeDirectPointer<CChar>
        var fieldName:               RelativeDirectPointer<CChar>   // 属性名称
    }
    
    // 相对位移指针
    struct RelativeDirectPointer<T>{
        var offset: Int32
    
        // 偏移offset位置,获取内容指针
        mutating func get() -> UnsafeMutablePointer<T> {
            let offset = self.offset
            // withUnsafePointer获取指针
            return withUnsafePointer(to: &self) { p in
                // UnsafeMutablePointer 返回T类型对象的指针
                // UnsafeRawPointer将p指针转换为未知类型
                // numericCast将offset转换为偏移单位数
                // advanced进行内存偏移
                // assumingMemoryBound绑定指针为T类型
                return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: T.self))
            }
    
        }
    }
    
    struct LGTeacher {
        var age = 18
        var name = "Luoji"
    }
    
    // 读取将LGTeacher指针内容,赋值给StructMetadata  (unsafeBitCast: 通过字节读取)
    let p = unsafeBitCast(LGTeacher.self as Any.Type, to: UnsafeMutablePointer<StructMetadata>.self)
    
    // 读取当前name的内容指针
    let namePtr = p.pointee.description.pointee.name.get()
    let count = p.pointee.description.pointee.numFields
    print("类型:\(String(cString: namePtr))") // name是CChar类型,转为字符串输出
    print("属性个数:\(count)")
    
    // 单独读取第一个属性名
    var fields = p.pointee.description.pointee.fields.get()
    let fieldRecord1Name = fields.pointee.fields.fieldName.get()
    print(String(cString: fieldRecord1Name))
    
    // 读取所有记录
    print("----读取所有属性名----")
    (0..<count).forEach { index in
        let recordPtr = withUnsafePointer(to: &fields.pointee.fields) {
            return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: FieldRecord.self).advanced(by: Int(index)))
        }
        print(String(cString: recordPtr.pointee.fieldName.get()))
    }
    
    print("----dump----")
    dump(LGTeacher()) // 相似的实现方式
    

    总结

    综上所述,Mirror反射干的事情大致分为三步:

    1. Mirror在实例对象的metadata中找到Descriptor
    2. Descriptor
      2.1 找到name,获取类型(相当于type名称)
      2.2 找到numFields,获取属性个数
    3. 找到FieldDescriptor中的fields,来找到对当前属性描述,然后通过指针内存平移,获取其他属性
      下图是以为例,底层Mirror反射的流程图👇

    相关文章

      网友评论

        本文标题:Swift Mirror原理解析

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