Swift中的反射Mirror
[TOC]
前言
Mirror
是Swift
中的反射机制,对于C#
和Java
开发人员来说,应该很熟悉反射这个概念。反射就是可以动态的获取类型以及成员信息,同时也可以在运行时动态的调用方法和属性等。
对于iOS
开发人员来说,入门时使用的Objective-C
是很少强调反射概念的,因为OC
的Runtime
要比其他语言的反射强大的多。
另外,在阅读本篇文章前建议先看看我的另一篇文章:
Swift 中的类型
1. Mirror 简介
Mirror
是Swift
中的反射机制的实现,它的本质是一个结构体。其部分源码(Swift 5.3.1
)如下:
public struct Mirror {
/// A suggestion of how a mirror's subject is to be interpreted.
///
/// Playgrounds and the debugger will show a representation similar
/// to the one used for instances of the kind indicated by the
/// `DisplayStyle` case name when the mirror is used for display.
public enum DisplayStyle {
case `struct`, `class`, `enum`, tuple, optional, collection
case dictionary, `set`
}
/// The static type of the subject being reflected.
///
/// This type may differ from the subject's dynamic type when this mirror
/// is the `superclassMirror` of another mirror.
public let subjectType: Any.Type
/// A collection of `Child` elements describing the structure of the
/// reflected subject.
public let children: Children
/// A suggested display style for the reflected subject.
public let displayStyle: DisplayStyle?
/// A mirror of the subject's superclass, if one exists.
public var superclassMirror: Mirror? {
return _makeSuperclassMirror()
}
}
-
subjectType
:表示类型,被反射主体的类型 -
children
:子元素集合 -
displayStyle
:显示类型,基本类型为nil 枚举值:struct, class, enum, tuple, optional, collection, dictionary, set
-
superclassMirror
:父类反射, 没有父类为nil
除了这些属性还有一些初始化方法,我们最常用的就是初始化方法就是:
/// Creates a mirror that reflects on the given instance.
///
/// If the dynamic type of `subject` conforms to `CustomReflectable`, the
/// resulting mirror is determined by its `customMirror` property.
/// Otherwise, the result is generated by the language.
///
/// If the dynamic type of `subject` has value semantics, subsequent
/// mutations of `subject` will not observable in `Mirror`. In general,
/// though, the observability of mutations is unspecified.
///
/// - Parameter subject: The instance for which to create a mirror.
public init(reflecting subject: Any) {
if case let customized as CustomReflectable = subject {
self = customized.customMirror
} else {
self = Mirror(internalReflecting: subject)
}
}
根据源码我们还可以看到能够使用customMirror
,这个没太研究,在源码中可以看到很多CustomMirror
的身影,感兴趣的可以去研究研究。对于非customMirror
的统一使用Mirror(internalReflecting:)
进行初始化。
关于customMirror
的补充,摘抄自swiftGG Mirror 的工作原理。
Mirror
允许类型用遵循 CustomReflectable
协议的方式提供一个自定义的表示方式。这给那些想表示得比内建形式更友好的类型提供一种有效的方法。 比如 Array
类型遵守 CustomReflectable
协议并且暴露其中的元素为无标签的 Children
。Dictionary
使用这种方法暴露其中的键值对为带标签的 Children
。
2. Mirror的简单使用
2.1 基本使用
这里我们通过使用Mirror
打印对象的属性名称和属性值。
class Person {
var name: String = "xiaohei"
var age: Int = 18
var height = 1.85
}
var p = Person()
var mirror = Mirror(reflecting: p.self)
print("对象类型:\(mirror.subjectType)")
print("对象属性个数:\(mirror.children.count)")
print("对象的属性及属性值")
for child in mirror.children {
print("\(child.label!)---\(child.value)")
}
打印结果:
image我们可以看到,属性名称和值都已经正常打印。
2.2 将对象转换为字典
首先我们来体验一下将对象转换为字典。
class Animal {
var name: String?
var color: String?
private var birthday: Date = Date(timeIntervalSince1970: 0)
}
class Cat: Animal {
var master = "小黑"
var like: [String] = ["mouse", "fish"]
override init() {
super.init()
color = "黄色"
}
}
func mapDic(mirror: Mirror) -> [String: Any] {
var dic: [String: Any] = [:]
for child in mirror.children {
// 如果没有labe就会被抛弃
if let label = child.label {
let propertyMirror = Mirror(reflecting: child.value)
print(propertyMirror)
dic[label] = child.value
}
}
// 添加父类属性
if let superMirror = mirror.superclassMirror {
let superDic = mapDic(mirror: superMirror)
for p in superDic {
dic[p.key] = p.value
}
}
return dic
}
// Mirror使用
let cat = Cat()
cat.name = "大橘为重"
let mirror = Mirror(reflecting: cat)
let mirrorDic = mapDic(mirror: mirror)
print(mirrorDic)
打印结果:
image通过打印结果我们可以看到,对于一些基本类型,已经可选类型的数据都已经转换为字典值,对于私有属性也可以完成转换。如果里面包含类则还需要进行递归处理。
2.3 转 JSON
注: 这里并没有真正的转换成json字符串,还是只转换成了字典,重要在思想,如果需要转换成json还需要很多优化,以及特殊字符串的考量。
其实提到反射我们想到最多的应该就是JSON
了,这里我们利用Mirror
的特性,将对象转换成字典,对基本类型和类做了相应的处理,体会一下转json的思路。
首先我们定义一个Person
对象,代码如下:
struct Person {
var name: String = "xiaohei"
var age: Int = 18
var isMale: Bool = true
var address: Address? = Address(street: "xizhimen North")
var height = 1.85
var like: Array = ["eat", "sleep", "play"]
var weight: Float = 75.0
var some: Int?
}
struct Address {
var street: String
}
// 创建一个Person对象
let p = Person()
为了通用性,我们可以编写一个协议,用来为所有类型提供转换的方法,只需要遵守该协议就可以使用协议中的方法。
//可以转换为 Json 的协议
protocol CustomJSONProtocol {
func toJSON() throws -> Any?
}
协议的实现过程中会有些错误,我们也简单的定义个枚举,方便处理。为了更加详细的描述错误信息,我们添加了错误描述和错误code。
// 转 json 时的错误类型
enum JSONMapError: Error{
case emptyKey
case notConformProtocol
}
// 错误描述
extension JSONMapError: LocalizedError{
var errorDescription: String?{
switch self {
case .emptyKey:
return "key 为空"
case .notConformProtocol:
return "没遵守协议"
}
}
}
// errorcode
extension JSONMapError: CustomNSError{
var errorCode: Int{
switch self {
case .emptyKey:
return 100
case .notConformProtocol:
return 101
}
}
}
协议实现的代码:
extension CustomJSONProtocol {
func toJSON() throws -> Any? {
//创建 Mirror 类型
let mirror = Mirror(reflecting: self)
// 如果没有属性,比如一般类型String、Int等,直接返回自己
guard !mirror.children.isEmpty else { return self }
var result: [String:Any] = [:]
// 遍历
for children in mirror.children {
if let value = children.value as? CustomJSONProtocol{
if let key = children.label {
print(key)
result[key] = try value.toJSON()
} else {
throw JSONMapError.emptyKey
}
} else {
throw JSONMapError.notConformProtocol
}
}
return result
}
}
将用到的类型都遵守协议
//将一般类型都遵从 CustomJSONProtocol 协议
extension Person: CustomJSONProtocol {}
extension String: CustomJSONProtocol {}
extension Int: CustomJSONProtocol {}
extension Bool: CustomJSONProtocol {}
extension Double: CustomJSONProtocol {}
extension Float: CustomJSONProtocol {}
extension Address: CustomJSONProtocol {}
// 数组需要单独处理,要不然就会报错emptyKey
extension Array: CustomJSONProtocol {
func toJSON() throws -> Any? {
return self
}
}
//Optionai 需要特别对待,原因是如果直接返回,则会是 .Some: [...]
extension Optional: CustomJSONProtocol {
func toJSON() throws -> Any? {
if let x = self {
if let value = x as? CustomJSONProtocol {
return try value.toJSON()
}
throw JSONMapError.notConformProtocol
}
return nil
}
}
最后我们打印一下:
do {
print(try p.toJSON()!)
} catch {
print(error.localizedDescription)
print((error as? JSONMapError)?.errorCode)
}
打印结果:
image我们看到,对于some
这空值,并没有存储到字典中,因为swift
中的字典对于空值是删除的意思。
如果想将其转换成json
还需修改"[]"为"{}",这个对于数组和对象还不好区分,另外对于json
字符串内的一些value
也有可能是应一串json
还需要添加转义字符等。
所以总的来说,思路是这样的,要想真正的做成通用的转json
的方案还需要很多的优化,比如说,我们不可能将所有的基本类型都去遵守一个协议,这时候我们也可以考虑使用泛型去作为方法的参数。
3. Mirror 源码解析
源码版本Swift 5.3.1
在本章节我们将分析Mirror
的部分源码,查看其底层实现,最后通过Swift
代码使用内存重绑定的形式,仿写一下Mirror
,来更好的探索Mirror
的原理,理解Mirror
的思想。
我们知道Swift
是一门静态语言,那么在底层是如何实现的获取对应的属性值的呢?又或者说Swift
的反射特性是如何实现的呢?下面我们通过对Mirror
底层源码的探索来寻找答案。
3.1 代码结构
Mirror
的实现是由一部分Swift
代码加上另一部分C++
代码。Swift
代码实现在ReflectionMirror.swift
文件中,C++
代码实现在ReflectionMirror.mm
文件中。Swift
更适合用在实现更Swift
的接口,但是在Swift
中不能直接访问C++
的类。这里使用了@_silgen_name
来实现Swift
调用C++
中的方法。举个例子:
@_silgen_name("swift_reflectionMirror_count")
internal func _getChildCount<T>(_: T, type: Any.Type) -> Int
@_silgen_name
修饰符会通知Swift
编译器将这个函数映射成swift_reflectionMirror_count
符号,而不是Swift
通常对应到的_getChildCount
方法名修饰。需要注意的是,最前面的下划线表示这个修饰是被保留在标准库中的。在C++
这边,这个函数是这样的。
// 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();
});
}
SWIFT_CC(swift)
会告诉编译器这个函数使用的是Swift
的调用约定,而不是C/C++的,SWIFT_RUNTIME_STDLIB_API
标记这个函数,在Swift
侧的一部分接口中,而且它还有标记为extern "C"
的作用,从而避免C++
的方法名修饰,并确保它在Swift
侧会有预期的符号。同时C++
的参数会去特意匹配在Swift
中声明的函数调用。当Swift
调用_getChildCount
时,C++
会用包含Swift
值指针的value
,包含类型参数type
,包含类型响应的泛型<T>
的T
的函数参数来调用此函数。
简单的说就是使用@_silgen_name("xxx")
修饰符修饰的Swift
方法会调用括号中的xxx
的符号,不管是C++
的还是C
的都可以。
Mirror
的在Swift
和C++
之间的全部接口由以下函数组成:
@_silgen_name("swift_isClassType")
internal func _isClassType(_: Any.Type) -> Bool
@_silgen_name("swift_getMetadataKind")
internal func _metadataKind(_: Any.Type) -> UInt
@_silgen_name("swift_reflectionMirror_normalizedType")
internal func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type
@_silgen_name("swift_reflectionMirror_count")
internal func _getChildCount<T>(_: T, type: Any.Type) -> Int
@_silgen_name("swift_reflectionMirror_recursiveCount")
internal func _getRecursiveChildCount(_: Any.Type) -> Int
@_silgen_name("swift_reflectionMirror_recursiveChildMetadata")
internal func _getChildMetadata(
_: Any.Type,
index: Int,
outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
) -> Any.Type
@_silgen_name("swift_reflectionMirror_recursiveChildOffset")
internal func _getChildOffset(
_: Any.Type,
index: Int
) -> Int
internal typealias NameFreeFunc = @convention(c) (UnsafePointer<CChar>?) -> Void
@_silgen_name("swift_reflectionMirror_subscript")
internal func _getChild<T>(
of: T,
type: Any.Type,
index: Int,
outName: UnsafeMutablePointer<UnsafePointer<CChar>?>,
outFreeFunc: UnsafeMutablePointer<NameFreeFunc?>
) -> Any
// Returns 'c' (class), 'e' (enum), 's' (struct), 't' (tuple), or '\0' (none)
@_silgen_name("swift_reflectionMirror_displayStyle")
internal func _getDisplayStyle<T>(_: T) -> CChar
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)
}
#if _runtime(_ObjC)
@_silgen_name("swift_reflectionMirror_quickLookObject")
internal func _getQuickLookObject<T>(_: T) -> AnyObject?
@_silgen_name("_swift_stdlib_NSObject_isKindOfClass")
internal func _isImpl(_ object: AnyObject, kindOf: UnsafePointer<CChar>) -> Bool
3.2 初始化
在一开始我们简单的介绍了Mirror
的部分源码,也由此知道Mirror(reflecting:)
初始化方法可以接受任意值,返回一个提供该值子元素集合Children
的相关信息的实例。
通过Mirror(reflecting:)
源码我们可以知道,其底层调用的是internalReflecting
方法。在ReflectionMirror.swift
文件的extension Mirror
中我们可以找到该方法。其源码如下:
3.2.1 internalReflecting
internal init(internalReflecting subject: Any,
subjectType: Any.Type? = nil,
customAncestor: Mirror? = nil)
{
let subjectType = subjectType ?? _getNormalizedType(subject, type: type(of: subject))
let childCount = _getChildCount(subject, type: subjectType)
let children = (0 ..< childCount).lazy.map({
getChild(of: subject, type: subjectType, index: $0)
})
self.children = Children(children)
self._makeSuperclassMirror = {
guard let subjectClass = subjectType as? AnyClass,
let superclass = _getSuperclass(subjectClass) else {
return nil
}
// Handle custom ancestors. If we've hit the custom ancestor's subject type,
// or descendants are suppressed, return it. Otherwise continue reflecting.
if let customAncestor = customAncestor {
if superclass == customAncestor.subjectType {
return customAncestor
}
if customAncestor._defaultDescendantRepresentation == .suppressed {
return customAncestor
}
}
return Mirror(internalReflecting: subject,
subjectType: superclass,
customAncestor: customAncestor)
}
let rawDisplayStyle = _getDisplayStyle(subject)
switch UnicodeScalar(Int(rawDisplayStyle)) {
case "c": self.displayStyle = .class
case "e": self.displayStyle = .enum
case "s": self.displayStyle = .struct
case "t": self.displayStyle = .tuple
case "\0": self.displayStyle = nil
default: preconditionFailure("Unknown raw display style '\(rawDisplayStyle)'")
}
self.subjectType = subjectType
self._defaultDescendantRepresentation = .generated
}
源码分析:
- 首先是获取
subjectType
,如果传入的有值就使用传入的值,否则就通过_getNormalizedType
函数去获取 - 接下来就是通过
_getChildCount
获取childCount
- 接下来是
children
,注意这里是懒加载的 - 紧接着是
SuperclassMirror
,这里使用的是一个闭包的形式 - 最后会获取并解析显示的样式,并设置
Mirror
剩下的属性。
3.2.2 _getNormalizedType
下面我们就来看看_getNormalizedType
函数内部的实现。根据上面的分析我们知道实际调用是swift_reflectionMirror_normalizedType
。在ReflectionMirror.mm
文件中我们可以看到其源码:
// 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; });
}
3.2.3 call函数
我们可以看到这里调用了一个call
函数,最后返回的是impl
的type
。首先我们看看这call
函数:
template<typename F>
auto call(OpaqueValue *passedValue, const Metadata *T, const Metadata *passedType,
const F &f) -> decltype(f(nullptr))
{
const Metadata *type;
OpaqueValue *value;
std::tie(type, value) = unwrapExistential(T, passedValue);
if (passedType != nullptr) {
type = passedType;
}
auto call = [&](ReflectionMirrorImpl *impl) {
impl->type = type;
impl->value = value;
auto result = f(impl);
return result;
};
auto callClass = [&] {
if (passedType == nullptr) {
// Get the runtime type of the object.
const void *obj = *reinterpret_cast<const void * const *>(value);
auto isa = _swift_getClass(obj);
// Look through artificial subclasses.
while (isa->isTypeMetadata() && isa->isArtificialSubclass()) {
isa = isa->Superclass;
}
passedType = isa;
}
#if SWIFT_OBJC_INTEROP
// If this is a pure ObjC class, reflect it using ObjC's runtime facilities.
// ForeignClass (e.g. CF classes) manifests as a NULL class object.
auto *classObject = passedType->getClassObject();
if (classObject == nullptr || !classObject->isTypeMetadata()) {
ObjCClassImpl impl;
return call(&impl);
}
#endif
// Otherwise, use the native Swift facilities.
ClassImpl impl;
return call(&impl);
};
switch (type->getKind()) {
case MetadataKind::Tuple: {
TupleImpl impl;
return call(&impl);
}
case MetadataKind::Struct: {
StructImpl impl;
return call(&impl);
}
case MetadataKind::Enum:
case MetadataKind::Optional: {
EnumImpl impl;
return call(&impl);
}
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass:
case MetadataKind::Class: {
return callClass();
}
case MetadataKind::Metatype:
case MetadataKind::ExistentialMetatype: {
MetatypeImpl impl;
return call(&impl);
}
case MetadataKind::Opaque: {
#if SWIFT_OBJC_INTEROP
// If this is the AnyObject type, use the dynamic type of the
// object reference.
if (type == &METADATA_SYM(BO).base) {
return callClass();
}
#endif
// If this is the Builtin.NativeObject type, and the heap object is a
// class instance, use the dynamic type of the object reference.
if (type == &METADATA_SYM(Bo).base) {
const HeapObject *obj
= *reinterpret_cast<const HeapObject * const*>(value);
if (obj->metadata->getKind() == MetadataKind::Class) {
return callClass();
}
}
LLVM_FALLTHROUGH;
}
/// TODO: Implement specialized mirror witnesses for all kinds.
default:
break;
// Types can't have these kinds.
case MetadataKind::HeapLocalVariable:
case MetadataKind::HeapGenericLocalVariable:
case MetadataKind::ErrorObject:
swift::crash("Swift mirror lookup failure");
}
// If we have an unknown kind of type, or a type without special handling,
// treat it as opaque.
OpaqueImpl impl;
return call(&impl);
}
乍一看这个call
函数代码相对有点多,其实主要就是一个大型的switch
声明,和一些对特殊情况的处理,这里重要的就是,它会用一个ReflectionMirrorImpl
的子类实例去结束调用f
,然后会调用这个实例上的方法去让真正的工作完成,这也就是为什么在swift_reflectionMirror_normalizedType
函数中最后会return impl->type;
感兴趣的可以通过源码去调试一下,这里我也调试了,没什么指的说的,这就就不写调试过程了,下面我们就来看看ReflectionMirrorImpl
。
3.2.4 ReflectionMirrorImpl
ReflectionMirrorImpl
有以下6个子类:
- TupleImpl 元组的反射
- StructImpl 结构体的反射
- EnumImpl 枚举的反射
- ClassImpl 类的反射
- MetatypeImpl 元数据的反射
- OpaqueImpl 不透明类型的反射
ReflectionMirrorImpl 源码:
// Abstract base class for reflection implementations.
struct 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() {}
};
ReflectionMirrorImpl
源码不多,但是我们可以看到type
以及count
等都在其中。下面我们以结构体为例,看看其子类的源码。
3.2.5 结构体的反射
// Implementation for structs.
struct StructImpl : ReflectionMirrorImpl {
bool isReflectable() {
const auto *Struct = static_cast<const StructMetadata *>(type);
const auto &Description = Struct->getDescription();
return Description->isReflectable();
}
char displayStyle() {
return 's';
}
intptr_t count() {
if (!isReflectable()) {
return 0;
}
auto *Struct = static_cast<const StructMetadata *>(type);
return Struct->getDescription()->NumFields;
}
intptr_t childOffset(intptr_t i) {
auto *Struct = static_cast<const StructMetadata *>(type);
if (i < 0 || (size_t)i > Struct->getDescription()->NumFields)
swift::crash("Swift mirror subscript bounds check failure");
// Load the offset from its respective vector.
return Struct->getFieldOffsets()[i];
}
const FieldType childMetadata(intptr_t i, const char **outName,
void (**outFreeFunc)(const char *)) {
StringRef name;
FieldType fieldInfo;
std::tie(name, fieldInfo) = getFieldAt(type, i);
assert(!fieldInfo.isIndirect() && "indirect struct fields not implemented");
*outName = name.data();
*outFreeFunc = nullptr;
return fieldInfo;
}
AnyReturn subscript(intptr_t i, const char **outName,
void (**outFreeFunc)(const char *)) {
auto fieldInfo = childMetadata(i, outName, outFreeFunc);
auto *bytes = reinterpret_cast<char*>(value);
auto fieldOffset = childOffset(i);
auto *fieldData = reinterpret_cast<OpaqueValue *>(bytes + fieldOffset);
return copyFieldContents(fieldData, fieldInfo);
}
};
- 首先一个判断是否支持反射的方法,最中是访问的
Description->isReflectable()
- 这里使用
‘s’
来显式的表明这是一个结构体 - 然后是获取属性个数
- 紧接着是获取每个属性的偏移值
- 然后获取了
FieldType
内部还可以获取到属性的名称。 - 最后
subscript
方法可以获取到属性名称和属性偏移的指针,也就是属性值。
3.3 Description
在此处我们看到很多关于Description
的代码,看来这个Description
存储着很多信息,在获取Description
的时候是从StructMetadata
通过getDescription()
方法获取到。所以这些信息基本确定是从MetaData
中获取到的。StructMetadata
是TargetStructMetadata
的别名,我们以此为例:
3.3.1 TargetStructMetadata
在TargetStructMetadata
我们可以看到如下代码:
const TargetStructDescriptor<Runtime> *getDescription() const {
return llvm::cast<TargetStructDescriptor<Runtime>>(this->Description);
}
说明此处会返回一个TargetStructDescriptor
类型的Description
,但是在这里并没有找到这个属性,可能在父类中,我们可以看到TargetStructMetadata
继承自TargetValueMetadata
。
3.3.2 TargetValueMetadata
在这里我们可以看到如下代码:
/// An out-of-line description of the type.
TargetSignedPointer<Runtime, const TargetValueTypeDescriptor<Runtime> * __ptrauth_swift_type_descriptor> Description;
getDescription() const {
return Description;
}
这里我们便找到了:
-
Description
属性,它的类型是TargetValueTypeDescriptor
,应该是TargetStructDescriptor
的父类。 -
getDescription()
方法,在TargetStructMetadata
是重写的这个方法
3.3.3 TargetStructDescriptor
跳转到TargetStructDescriptor
中后,我们可以看到:
-
TargetValueTypeDescriptor
果然是它的父类 - 在其源码中我们还可以发现两个属性,分别是
NumFields
和FieldOffsetVectorOffset
源码如下:
/// The number of stored properties in the struct.
/// If there is a field offset vector, this is its length.
uint32_t NumFields;
/// The offset of the field offset vector for this struct's stored
/// properties in its metadata, if any. 0 means there is no field offset
/// vector.
uint32_t FieldOffsetVectorOffset;
- 其中
NumFields
主要表示结构体中属性的个数,如果只有一个字段偏移量则表示偏移量的长度 -
FieldOffsetVectorOffset
表示这个结构体元数据中存储的属性的字段偏移向量的偏移量,如果是0则表示没有
3.3.4 TargetValueTypeDescriptor
源码如下,很少:
template <typename Runtime>
class TargetValueTypeDescriptor
: public TargetTypeContextDescriptor<Runtime> {
public:
static bool classof(const TargetContextDescriptor<Runtime> *cd) {
return cd->getKind() == ContextDescriptorKind::Struct ||
cd->getKind() == ContextDescriptorKind::Enum;
}
};
在这里我们并没有找到太多有用的信息,我们继续向父类寻找,其继承自TargetTypeContextDescriptor
3.3.5 TargetTypeContextDescriptor
部分源码:
template <typename Runtime>
class TargetTypeContextDescriptor
: public TargetContextDescriptor<Runtime> {
public:
/// The name of the type.
TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false> Name;
/// A pointer to the metadata access function for this type.
///
/// The function type here is a stand-in. You should use getAccessFunction()
/// to wrap the function pointer in an accessor that uses the proper calling
/// convention for a given number of arguments.
TargetRelativeDirectPointer<Runtime, MetadataResponse(...),
/*Nullable*/ true> AccessFunctionPtr;
/// A pointer to the field descriptor for the type, if any.
TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor,
/*nullable*/ true> Fields;
}
我们可以看到:
- 该类继承自
TargetContextDescriptor
- 有
Name
、AccessFunctionPtr
、Fields
三个属性- 其中
name
就是类型的名称 -
AccessFunctionPtr
是该类型元数据访问函数的指针 -
Fields
是一个指向该类型的字段描述符的指针
- 其中
3.3.6 TargetContextDescriptor
接下来我们再看看TargetTypeContextDescriptor
的父类中还有什么有用的信息。部分源码如下:
/// Base class for all context descriptors.
template<typename Runtime>
struct TargetContextDescriptor {
/// Flags describing the context, including its kind and format version.
ContextDescriptorFlags Flags;
/// The parent context, or null if this is a top-level context.
TargetRelativeContextPointer<Runtime> Parent;
}
这里我们可以看到:
- 这就是
descriptors
的基类 - 有两个属性,分别是
Flags
和Parent
- 其中
Flags
是描述上下文的标志,包括它的种类和格式版本。 -
Parent
是记录父类上下文的,如果是顶级则为null
- 其中
3.3.7 小结
至此我们对结构体Description
的层级结构基本就理清楚了,现总结如下:
从上图我们可以看到,对于一个结构体的Description
来说,继承链上一共四个类,7个属性。下面我们就对这些属性作进一步的分析
3.4 Description中的属性
3.4.1 Flags
Flags
的类型是ContextDescriptorFlags
,我们点击跳转进去,我们可以看到:
/// Common flags stored in the first 32-bit word of any context descriptor.
struct ContextDescriptorFlags {
private:
uint32_t Value;
explicit constexpr ContextDescriptorFlags(uint32_t Value)
: Value(Value) {}
public:
constexpr ContextDescriptorFlags() : Value(0) {}
constexpr ContextDescriptorFlags(ContextDescriptorKind kind,
bool isGeneric,
bool isUnique,
uint8_t version,
uint16_t kindSpecificFlags)
: ContextDescriptorFlags(ContextDescriptorFlags()
.withKind(kind)
.withGeneric(isGeneric)
.withUnique(isUnique)
.withVersion(version)
.withKindSpecificFlags(kindSpecificFlags))
{}
......
}
从以上的代码中我们可以看到这个FLags
实际是个uint32_t
的值,按位存储着kind
、isGeneric
、isUnique
、version
等信息。
3.4.2 Parent
Parent
的类型是TargetRelativeContextPointer<Runtime>
,我们看看TargetRelativeContextPointer
,点击跳转过去:
using TargetRelativeContextPointer =
RelativeIndirectablePointer<const Context<Runtime>,
/*nullable*/ true, int32_t,
TargetSignedContextPointer<Runtime, Context>>;
我们可以看到TargetRelativeContextPointer
是取自RelativeIndirectablePointer
的别名,继续点击进行查看:
/// A relative reference to an object stored in memory. The reference may be
/// direct or indirect, and uses the low bit of the (assumed at least
/// 2-byte-aligned) pointer to differentiate.
template<typename ValueTy, bool Nullable = false, typename Offset = int32_t, typename IndirectType = const ValueTy *>
class RelativeIndirectablePointer {
/// The relative offset of the pointer's memory from the `this` pointer.
/// If the low bit is clear, this is a direct reference; otherwise, it is
/// an indirect reference.
Offset RelativeOffsetPlusIndirect;
const ValueTy *get() const & {
static_assert(alignof(ValueTy) >= 2 && alignof(Offset) >= 2,
"alignment of value and offset must be at least 2 to "
"make room for indirectable flag");
// Check for null.
if (Nullable && RelativeOffsetPlusIndirect == 0)
return nullptr;
Offset offsetPlusIndirect = RelativeOffsetPlusIndirect;
uintptr_t address = detail::applyRelativeOffset(this,
offsetPlusIndirect & ~1);
// If the low bit is set, then this is an indirect address. Otherwise,
// it's direct.
if (offsetPlusIndirect & 1) {
return *reinterpret_cast<IndirectType const *>(address);
} else {
return reinterpret_cast<const ValueTy *>(address);
}
}
}
根据注释我们就可以轻松的知道这个类的主要作用是存储在内存中的对象的相对引用。这个意思在后面也有相关的解释就是在内存中引用可以是直接的也可以是间接的,直接的就是存储的绝对地址(也不一定,还有ASLR
等),直接访问这个地址就可以拿到对应的数据,而这里的相对引用就是间接的,这里面通过RelativeOffsetPlusIndirect
属性存储相对的地址偏移量,在通过get()
函数获取,在get()函数中,会调用applyRelativeOffset
函数,进行地址的偏移,applyRelativeOffset
源码:
/// Apply a relative offset to a base pointer. The offset is applied to the base
/// pointer using sign-extended, wrapping arithmetic.
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;
return base + extendOffset;
}
最后返回的时候我们可以看到base + extendOffset;
基地址加上偏移的值,最后得到真实的地址。
3.4.2 name
name
的类型是TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false>
,看着应该和Parent
差不多。我们点击TargetRelativeDirectPointer
跳转到它的源码处:
template <typename Runtime, typename Pointee, bool Nullable = true>
using TargetRelativeDirectPointer
= typename Runtime::template RelativeDirectPointer<Pointee, Nullable>;
这里依旧是取别名,继续跳转到RelativeDirectPointer
,这里有两个选择,我们选择相对引用那个(通过注释区别)。源码如下:
/// A direct relative reference to an object that is not a function pointer.
template <typename T, bool Nullable, typename Offset>
class RelativeDirectPointer<T, Nullable, Offset,
typename std::enable_if<!std::is_function<T>::value>::type>
: private RelativeDirectPointerImpl<T, Nullable, Offset>
{
using super = RelativeDirectPointerImpl<T, Nullable, Offset>;
public:
using super::get;
using super::super;
RelativeDirectPointer &operator=(T *absolute) & {
super::operator=(absolute);
return *this;
}
operator typename super::PointerTy() const & {
return this->get();
}
const typename super::ValueTy *operator->() const & {
return this->get();
}
using super::isNull;
};
在源码中我们可以看到很多关于supper
的东西,有两处都是通过get()
方法返回的,分别是PointerTy
和ValueTy
,下面我们就来到父类方法中看看。父类是RelativeDirectPointerImpl
,其部分源码如下:
/// A relative reference to a function, intended to reference private metadata
/// functions for the current executable or dynamic library image from
/// position-independent constant data.
template<typename T, bool Nullable, typename Offset>
class RelativeDirectPointerImpl {
private:
/// The relative offset of the function's entry point from *this.
Offset RelativeOffset;
public:
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);
}
}
这里同样有一个Offset
类型RelativeOffset
,以及get()
方法,同样get()
方法也会调用applyRelativeOffset
函数进行地址的相加,关于applyRelativeOffset
方法在介绍Parent
的时候提到过。
这里面多了ValueTy
和PointerTy
,ValueTy
是传入泛型的值,PointerTy
是其指针。
name
中还有个const char
,这里是直接用const char
类型存储的类型的名称。
3.4.4 AccessFunctionPtr
AccessFunctionPtr
的类型是TargetRelativeDirectPointer<Runtime, MetadataResponse(...), /*Nullable*/ true>
跟name
一样的使用偏移指针TargetRelativeDirectPointer
,只不过是const char
替换成了MetadataResponse(...)
,点击MetadataResponse
跳转后我们可以看到如下代码:
MetadataResponse() : Metadata(nullptr) {}
/// A metadata response that might not be dynamically complete.
explicit MetadataResponse(llvm::Value *metadata, llvm::Value *state,
MetadataState staticLowerBoundState)
: Metadata(metadata), DynamicState(state),
StaticState(staticLowerBoundState) {
assert(metadata && "must be valid");
}
所以这里只是一个Metadata
的 指针。
3.4.5 Fields
Fields
的类型是TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor, /*nullable*/ true>
TargetRelativeDirectPointer
就不多说了,这里看看FieldDescriptor
点击跳转到其源码处,部分源码如下:
// Field descriptors contain a collection of field records for a single
// class, struct or enum declaration.
class FieldDescriptor {
const FieldRecord *getFieldRecordBuffer() const {
return reinterpret_cast<const FieldRecord *>(this + 1);
}
public:
const RelativeDirectPointer<const char> MangledTypeName;
const RelativeDirectPointer<const char> Superclass;
FieldDescriptor() = delete;
const FieldDescriptorKind Kind;
const uint16_t FieldRecordSize;
const uint32_t NumFields;
}
这里有5个属性:
MangledTypeName
Superclass
kind
FieldRecordSize
NumFields
关于getFieldRecordBuffer
函数的返回值FieldRecord
源码如下:
class FieldRecord {
const FieldRecordFlags Flags;
public:
const RelativeDirectPointer<const char> MangledTypeName;
const RelativeDirectPointer<const char> FieldName;
.....
}
FieldRecord
主要是封装了一些属性,用于存储这些值。
3.4.6 NumFields
NumFields
的类型是uint32_t
这就没什么好说的了,一个整形存储属性个数
3.4.7 FieldOffsetVectorOffset
FieldOffsetVectorOffset
也是个uint32_t
的整形,存储偏移量。
3.5 Mirror取值
对于Description
分析基本很透彻了,那么我们就回到最初的位置,看看Mirror
都是怎样从Description
取出相应的值的。
3.5.1 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.2 count
关于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();
});
}
同样还以结构体为例,此时的impl
为StructImpl
,内部的count()
函数:
intptr_t count() {
if (!isReflectable()) {
return 0;
}
auto *Struct = static_cast<const StructMetadata *>(type);
return Struct->getDescription()->NumFields;
}
这里的Struct
就是个TargetStructMetadata
类型,通过getDescription()
函数获取到一个TargetStructDescriptor
类型的Description
,然后取NumFields
的值就是我们要的count
。
3.5.3 属性名和属性值
我们知道在Mirror
中通过其初始化方法返回一个提供该值子元素的AnyCollection<Child>
类型的children
集合,Child
是一个元组(label: String?, value: Any)
,label
是一个可选类型的属性名,value
是属性值。
在分析internalReflecting
函数的时候,我们说children
是懒加载的,而加载的时候会调用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)
}
在getChild
方法中还会调用_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
_getChild
方法同样是使用@_silgen_name
修饰符最终调用的C++
中的swift_reflectionMirror_subscript
函数。
// We intentionally use a non-POD return type with this entry point to give
// it an indirect return ABI for compatibility with Swift.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
// 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);
});
}
#pragma clang diagnostic pop
这里我们可以看到是调用了impl
的subscript
函数,同样以结构体为例,我们在StructImpl
中找到该函数,源码如下:
AnyReturn subscript(intptr_t i, const char **outName,
void (**outFreeFunc)(const char *)) {
auto fieldInfo = childMetadata(i, outName, outFreeFunc);
auto *bytes = reinterpret_cast<char*>(value);
auto fieldOffset = childOffset(i);
auto *fieldData = reinterpret_cast<OpaqueValue *>(bytes + fieldOffset);
return copyFieldContents(fieldData, fieldInfo);
}
通过subscript
函数我们可以看到这里面还会调用childMetadata
获取到fieldInfo
,其实这里就是获取type
,也就是属性名,通过childOffset
函数和index
获取到对于的偏移量,最后根据内存偏移去到属性值。
childMetadata源码:
const FieldType childMetadata(intptr_t i, const char **outName,
void (**outFreeFunc)(const char *)) {
StringRef name;
FieldType fieldInfo;
std::tie(name, fieldInfo) = getFieldAt(type, i);
assert(!fieldInfo.isIndirect() && "indirect struct fields not implemented");
*outName = name.data();
*outFreeFunc = nullptr;
return fieldInfo;
}
这里面的关键点是调用调用getFieldAt
函数获取属性名称,
getFieldAt源码:
static std::pair<StringRef /*name*/, FieldType /*fieldInfo*/>
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 typeInfo = 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.
if (!typeInfo.getMetadata()) {
typeInfo = TypeInfo({&METADATA_SYM(EMPTY_TUPLE_MANGLING),
MetadataState::Complete}, {});
missing_reflection_metadata_warning(
"warning: the Swift runtime was unable to demangle the type "
"of field '%*s'. the mangled type name is '%*s'. this field will "
"show up as an empty tuple in Mirrors\n",
(int)name.size(), name.data(),
(int)typeName.size(), typeName.data());
}
auto fieldType = FieldType(typeInfo.getMetadata());
fieldType.setIndirect(field.isIndirectCase());
fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership());
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) {
auto *Struct = static_cast<const StructMetadata *>(type);
if (i < 0 || (size_t)i > Struct->getDescription()->NumFields)
swift::crash("Swift mirror subscript bounds check failure");
// Load the offset from its respective vector.
return Struct->getFieldOffsets()[i];
}
这里面是调用TargetStructMetadata
中的getFieldOffsets
函数源码如下:
/// Get a pointer to the field offset vector, if present, or null.
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
。
获取到偏移值后通过内存偏移即可获取到属性值。
3.6 小结
至此我们对Mirror
的原理基本探索完毕了,现在总结一下:
-
Mirror
通过初始化方法返回一会Mirror实例
- 这个实例对象根据传入对象的类型去对应的
Metadata
中找到Description
- 在
Description
可以获取name
也就是属性的名称 - 通过内存偏移获取到属性值
- 还可以通过
numFields
获取属性的个数
下面通过该流程图总结一下swift
中的mirror
对结构体进行反射的主要流程
关于其他类型的反射也大同小异,还有元组、枚举、类、元数据以及不透明类型的反射,当然也有不完全支持反射的类型,比如结构体就是不完全支持反射的类型,感兴趣的可以继续探索一下。
-
swift
中的type(of:)
、dump(t)
就是基于Mirror
的反射原理来实现的 -
Swift
中的json
解析框架HandyJSON
的主要原理与Mirror
类似,本质上就是利用metadata
中的Description
,通过字段的访问,做内存的赋值。
4. 仿写 Mirror
为了加深对Mirror
的理解,我们使用Swift
语言仿写一下。还是以结构体为例。
4.1 TargetStructMetadata
首先我们需要拥有一个结构体的元数据结构,这里我们命名为StructMetadata
,里面有继承的kind
和Descriptor
属性,这里的Descriptor
属性是一个TargetStructDescriptor
类型的指针。仿写代码如下:
struct StructMetadata{
var kind: Int
var Descriptor: UnsafeMutablePointer<StructDescriptor>
}
4.2 TargetStructDescriptor
在4.1
中我们用到的Descriptor
属性的内部结构现在也来实现一下。这个是Mirror
中用到很多的属性。对于结构体来说其内部有7个属性
-
flag
是个32位的整形,我们用Int32
代替 -
parent
是记录父类的,类型是TargetRelativeContextPointer<Runtime>
,这里也可以用Int32
代替 -
name
记录类型的,它的类型是TargetRelativeDirectPointer<char>
,所以我们需要实现一个TargetRelativeDirectPointer
-
AccessFunctionPtr
与name
类似,内部是个指针 -
Fields
也与name
类似,内部是个FieldDescriptor
-
NumFields
使用Int32
-
FieldOffsetVectorOffset
也是用Int32
仿写实现如下:
struct StructDescriptor {
let flags: Int32
let parent: Int32
var name: RelativePointer<CChar>
var AccessFunctionPtr: RelativePointer<UnsafeRawPointer>
var Fields: RelativePointer<FieldDescriptor>
var NumFields: Int32
var FieldOffsetVectorOffset: Int32
}
4.3 TargetRelativeDirectPointer
-
TargetRelativeDirectPointer
是RelativeDirectPointer
的别名,其内部有一个继承的RelativeOffset
属性,是int32_t
类型,我们可以用Int32
代替。 - 还有一个
get
方法。内部通过指针偏移获取值
仿写实现如下:
struct RelativePointer<T> {
var offset: Int32
mutating func get() -> UnsafeMutablePointer<T>{
let offset = self.offset
return withUnsafePointer(to: &self) { p in
return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: T.self))
}
}
}
4.4 FieldDescriptor
FieldDescriptor
在Mirror
反射中有着很重要的作用,其内部有5个属性:
-
MangledTypeName
是RelativeDirectPointer<const char>
类型,我们使用RelativePointer<CChar>
代替 -
Superclass
与MangledTypeName
一样 -
kind
是FieldDescriptorKind
类型,实际是uint16_t
,这里我们使用UInt16
代替 -
fieldRecordSize
是uint16_t
也使用使用UInt16
代替 -
numFields
使用Int32
代替 -
fields
,其实从属性上看不到有这个,但是这里面有个getFieldRecordBuffer
方法,通过this+1
的方式一个一个的访问属性,所以这是一块连续的内存空间,我们使用fields
代替
仿写代码如下:
struct FieldDescriptor {
var MangledTypeName: RelativePointer<CChar>
var Superclass: RelativePointer<CChar>
var kind: UInt16
var fieldRecordSize: Int16
var numFields: Int32
var fields: FieldRecord //连续的存储空间
}
4.5 FieldRecord
FieldRecord
存储着属性的相关信息,其内部有三个属性
-
Flags
是FieldRecordFlags
类型实际是uint32_t
,这里我们使用Int32
代替 -
MangledTypeName
使用RelativePointer<CChar>
代替 -
FieldName
使用RelativePointer<CChar>
代替
仿写带如下:
struct FieldRecord {
var Flags: Int32
var MangledTypeName: RelativePointer<CChar>
var FieldName: RelativePointer<CChar>
}
4.6 测试
下面我们使用内存绑定的计数访问一个结构体
定义一个结构体:
struct Person {
var name: String = "xiaohei"
var age: Int = 18
var height = 1.85
}
var p = Person()
4.6.1 绑定结构体内存
使用unsafeBitCast
按位强转,将Person
绑定到StructMetadata
上,这个操作非常危险,没有任何校验和修饰
let ptr = unsafeBitCast(Person.self as Any.Type, to: UnsafeMutablePointer<StructMetadata>.self)
4.6.2 打印类型和属性个数
下面我们就打印一下结构体的类型(也就是它的名称)和其中属性的个数:
let namePtr = ptr.pointee.Descriptor.pointee.name.get()
print(String(cString: namePtr))
print(ptr.pointee.Descriptor.pointee.NumFields)
打印结果:
image这里我们就可以看到结构体的名称和其属性个数的正确打印了。
4.6.3 打印属性名称
下面我们就来打印一下属性的名称,首先是获取到FieldDescriptor
的指针,然后通过内存偏移的方式访问每一个FieldRecord
,最后在访问FieldRecord
中的属性名。代码如下:
let fieldDescriptorPtr = ptr.pointee.Descriptor.pointee.Fields.get()
let recordPtr = withUnsafePointer(to: &fieldDescriptorPtr.pointee.fields) {
return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: FieldRecord.self).advanced(by: 2))
}
print(String(cString: recordPtr.pointee.FieldName.get()))
打印结果:
image此时我们就可以看到第三属性height
的打印,如果advanced(by: 0)
则打印第一个属性,以此类推。
4.6.1 打印属性值
下面我们访问一下属性值:
首先是获取属性偏移量的数组,也就是getFieldOffsets
函数返回的值。根据源码:
- 首先获取
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.Descriptor.pointee.FieldOffsetVectorOffset))).assumingMemoryBound(to: Int32.self), count: Int(ptr.pointee.Descriptor.pointee.NumFields))
var fieldOffset = bufferPtr[2]
var valuePtr = withUnsafeMutablePointer(to: &p) { $0 }
var bufferPtr1 = UnsafeRawPointer(UnsafeRawPointer(valuePtr).advanced(by: numericCast(bufferPtr[0]))).assumingMemoryBound(to: String.self)
print(bufferPtr1.pointee)
var bufferPtr2 = UnsafeRawPointer(UnsafeRawPointer(valuePtr).advanced(by: numericCast(bufferPtr[1]))).assumingMemoryBound(to: Int.self)
print(bufferPtr2.pointee)
var bufferPtr3 = UnsafeRawPointer(UnsafeRawPointer(valuePtr).advanced(by: numericCast(bufferPtr[2]))).assumingMemoryBound(to: Double.self)
print(bufferPtr3.pointee)
打印结果:
image
网友评论