Swift Mirror原理解析
前言
上篇Swift Mirror & Error主要是讲解了Mirror的一个常见的应用场景:JSON解析,但是里面的原理是怎样的?底层源码流程是如何处理反射呢?本篇文章将为大家详细解析Mirror的底层实现流程。
一、Mirror架构大致分析
首先我们大致来看看Mirror的架构,大概有哪些部分构成👇
-
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函数为例看看):
- 先在.h中声明c函数,在.c中实现
- 在桥接.h文件中引入C函数的头文件.h
- 在.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))
-
passedValue
👉 一个指针,指向swift调用方传递来的值 -
T
👉 该值的静态类型
-
passedType
👉显式
传入的且在反射过程中的用到的类型 -
f
👉 传递被查找到的会被调用的实现的引用对象,可以理解是个闭包的引用对象 -
返回值
👉 返回f参数调用
后的返回值
- 部分一:确定反射的真实类型
其中unwrapExistential
源码(其实就是强制类型转换,并取值)👇
ReflectionMirrorImpl
后面再分析。
- 部分二:针对【类对象】的处理
*部分三:其它类型的处理
以上就是对call
函数源码的大致流程的分析,最关键的一步就是使用ReflectionMirrorImpl子类
的实例去调用f
,然后封装成一个匿名函数call
(类似于闭包),再根据type对应的类型type->getKind()
去走真正的反射流程。
2.2 调试验证:_getNormalizedType
上面的源码看起来是不是有些费劲,没关系,接下来我们来断点调试一下_getNormalizedType
函数,看看传递的入参具体是什么?
先分别给swift_reflectionMirror_normalizedType
和call
打上断点,如下图所示:
运行源码,再准备调试示例代码,输入到终端👇
class LGTeacher{var age = 18}
var t = LGTeacher()
let mirror = Mirror(reflecting: t)
触发断点如下图👇(其中var t = LGTeacher()
这句也会触发,直接跳过)
注意:swift源码调试起来很卡,经常容易中断😂
2.3 ReflectionMirrorImpl 反射子类
ReflectionMirrorImpl
的子类主要有以下几种:
-
TupleImpl
元组的反射 -
StructImpl
结构体的反射 -
EnumImpl
枚举的反射 -
ClassImpl
类的反射 -
MetatypeImpl
元数据的反射 -
OpaqueImpl
不透明类型的反射
首先查看ReflectionMirrorImpl的底层定义
我们以类ClassImpl
为例,看看👇
类ClassImpl
与其它数据结构的不同在于,需要考虑继承链
关系,于是多出了父类递归处理的一些函数。其实仔细看看类ClassImpl
反射子类中对属性的操作处理,都是先找到metadata
,然后找到其description
,再根据偏移值fieldOffset
,就可得到真正索引i
对应的字段(即属性)。
ClassMetadata
其中,类Class
的metadata
类型就是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
有关键的两个成员变量NumFields
和FieldOffsetVectorOffset
,同时继承链的父类包含TargetTypeContextDescriptor
👇
继续沿着继承链向上查找,来到TargetContextDescriptor
👇
template <typename Runtime>
class TargetTypeContextDescriptor : public TargetContextDescriptor<Runtime>
其中包含2个重要成员变量Flags
和 Parent
👇
至此,结合上面的对ClassMetadata
和 TargetClassDescriptor
这两个关键类型的分析,我们在反射中对属性的处理是通过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反射
干的事情大致分为三步:
- Mirror在实例对象的
metadata
中找到Descriptor
- 在
Descriptor
中
2.1 找到name
,获取类型(相当于type名称)
2.2 找到numFields
,获取属性个数 - 找到
FieldDescriptor
中的fields
,来找到对当前属性
的描述
,然后通过指针内存平移
,获取其他属性
下图是以类
为例,底层Mirror反射的流程图👇