Swift Mirror原理解析

前言

上篇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, type: Any.Type) -> Any.Type

对应的C++函数是swift_reflectionMirror_normalizedType

// func _getNormalizedType(_: 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(of value: T, type: Any.Type, index: Int) -> (label: String?, value: Any) {
  var nameC: UnsafePointer? = 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(
  of: T,
  type: Any.Type,
  index: Int,
  outName: UnsafeMutablePointer?>,
  outFreeFunc: UnsafeMutablePointer
) -> Any

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

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

同理,搜索swift_reflectionMirror_displayStyle

// func _getDisplayStyle(_: 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
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;

struct TargetClassMetadata : public TargetAnyClassMetadata {
...
}


struct TargetAnyClassMetadata : public TargetHeapMetadata {
...
}


struct TargetHeapMetadata : TargetMetadata {
...
}

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

TargetClassDescriptor

接着来到TargetClassDescriptor


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

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

template 
class TargetTypeContextDescriptor : public TargetContextDescriptor

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

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

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

偏移量的计算

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

using TargetRelativeDirectPointer
  = typename Runtime::template RelativeDirectPointer;

class RelativeDirectPointer::value>::type>
    : private RelativeDirectPointerImpl {
...
}

template
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(absolute);
  }

// 其中,applyRelativeOffset的源码 
template
static inline uintptr_t applyRelativeOffset(BasePtrTy *basePtr, Offset offset) {
  static_assert(std::is_integral::value &&
                std::is_signed::value,
                "offset type should be signed integer");

  // 指针地址
  auto base = reinterpret_cast(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(this + 1);
  }

public:
  const RelativeDirectPointer MangledTypeName;
  const RelativeDirectPointer Superclass;

    ......

  const FieldDescriptorKind Kind;
  const uint16_t FieldRecordSize;
  const uint32_t NumFields;
  
    ......
  
  // 获取所有属性,每个属性用FieldRecord封装
   llvm::ArrayRef getFields() const {
    return {getFieldRecordBuffer(), NumFields};
  }
  
  ......
} 

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

class FieldRecord {
  const FieldRecordFlags Flags;

public:
  const RelativeDirectPointer MangledTypeName;
  const RelativeDirectPointer FieldName;

三、仿写Mirror

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

// 结构体类型
struct StructMetadata {
    var kind:        Int
    var description: UnsafeMutablePointer
}

// 结构体类型的描述
struct StructMetadataDesc {
    var flags:                   UInt32
    var parent:                  UInt32  // 展示用Uint32代替,实际是相同大小的结构体,
    var name:                    RelativeDirectPointer  // 不在乎具体类型,就先用UnsafeRawPointer
    var accessFunctionPtr:       RelativeDirectPointer  // 不在乎具体类型,就先用UnsafeRawPointer
    var fields:                  RelativeDirectPointer  // 记录所有属性内容
    var numFields:               UInt32   // 属性个数
    var fieldOffsetVectorOffset: UInt32
}

// 记录结构体内所有属性的结构
struct FieldDescription {
    var MangledTypeName:         RelativeDirectPointer
    var Superclass:              RelativeDirectPointer
    var Kind:                    UInt16
    var FieldRecordSize:         UInt16
    var NumFields:               UInt32
    var fields:                  FieldRecord // 连续存储空间 (有几个数据,就会在后面添加几个记录,通过内存平移读取)
}

// 每个属性的内容
struct FieldRecord {
    var flag:                    Int32
    var mangledTypeName:         RelativeDirectPointer
    var fieldName:               RelativeDirectPointer   // 属性名称
}

// 相对位移指针
struct RelativeDirectPointer{
    var offset: Int32

    // 偏移offset位置,获取内容指针
    mutating func get() -> UnsafeMutablePointer {
        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.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..

总结

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

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

你可能感兴趣的:(Swift Mirror原理解析)