阅读 83

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>(_: 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反射的流程图?

作者:深圳_你要的昵称

原文链接:https://www.jianshu.com/p/ada28a421bd4

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐