一、Mirror的基本用法
所谓反射就是可以动态获取类型、成员信息,在运⾏时可以调⽤⽅法、属性等⾏为的特性。在使⽤OC开发时很少强调其反射概念,因为OC的Runtime要⽐其他语⾔中的反射强⼤的多。但是 Swift 是⼀⻔类型安全的语⾔,不⽀持我们像 OC 那样直接操作,它的标准库仍然提供了反射机制来让我们访问成员信息。
Swift 的反射机制是基于⼀个叫 Mirror
的结构体来实现的。为具体的实例创建⼀个Mirror
对象,然后就可以通过它查询这个实例。
class ZGTeacher {
var age: Int = 18
func teach() {
print("teach")
}
}
///⾸先通过构造⽅法构建⼀个Mirror实例,这⾥传⼊的参数是 Any,也就意味着当前可以是类,结 构体,枚举等
let mirror = Mirror(reflecting: ZGTeacher())
///接下来遍历 children 属性,这是⼀个集合
for child in mirror.children {
///然后我们可以直接通过 label 输出当前的名称,value 输出当前反射的值
print("\(String(describing: child.label)):\(child.value)")
}
lldb打印
Optional("age"):18
实际使用案例
class ZGTeacher {
var age: Int = 18
func teach() {
print("teach")
}
}
func test(_ mirrorObj: Any) -> Any {
let mirror = Mirror(reflecting: mirrorObj)
guard !mirror.children.isEmpty else { return mirrorObj }
var result: [String : Any] = [ : ]
for child in mirror.children {
if let key = child.label {
result[key] = test(child.value)
} else {
print("No Keys")
}
}
return result
}
var reslut = test(ZGTeacher())
print(reslut)
我们想让我们的函数不管是结构体,类还是枚举还是基础类型也好,都能够具备这个⽅法。这个时候我们是不是就可以⽤协议来做?什么意思那?
首先,我们定义一个协议,这里就表示如果想要具备模型转字典的方式,那么这里就要遵循这个协议,然后实现jsonMap的方法。
protocol ZGJsonMap {
func jsonMap() -> Any
}
但是大家想一下,这个test方法里面的功能是不是通用的啊,也就意味着我们不需要每一个遵循了ZGJsonMap协议的都自己实现,所以这里我们是不是说可以给这个协议一个默认的实现啊。
class ZGTeacher {
var age: Int = 18
func teach() {
print("teach")
}
}
enum JSONMapError: Error {
case emptyKey
case notConformProtocol
}
protocol JSONMap {
func jsonMap() throws -> Any
}
extension JSONMap {
func jsonMap() throws -> Any {
let mirror = Mirror(reflecting: self)
guard !mirror.children.isEmpty else { return self }
var result: [String : Any] = [:]
for child in mirror.children {
if let value = child.value as? JSONMap {
if let key = child.label {
result[key] = try? value.jsonMap()
} else {
return JSONMapError.emptyKey
}
} else {
return JSONMapError.notConformProtocol
}
}
return result
}
}
///我们需要对当前的常见类型和自定义类型遵循JSONMap的协议
///这样对于没有value才能嵌套解析
extension ZGTeacher: JSONMap{}
extension Int: JSONMap{}
extension String: JSONMap{}
var t = ZGTeacher()
var t1 = try? t.jsonMap()
print(t1!)
二、Mirror源码解析
⾸先我们先在源⽂件⾥⾯搜索 Mirror.Swift
,在源码中我们可以很清晰的看到 Mirror
是由结构体
实现的,我们忽略掉⼀些细节,快速定位到初始化的⽅法。
public init(reflecting subject: Any) {
if case let customized as CustomReflectable = subject {
self = customized.customMirror
} else {
self = Mirror(internalReflecting: subject)
}
}
可以看到,这⾥接受⼀个 Any
类型的参数,同样的这⾥有⼀个 if case
的写法来判断当前的 subject
是否遵循了customReflectable
协议,如果是我们就直接调⽤ customMirror
, 否则就进⾏下级函数的调⽤。
这⾥有两个需要注意的点: if case
的写法,这⾥其实枚举 case
的模式匹配,和我们的 Switch ⼀样,这⾥是只有⼀个 case
的 switch 语句。与此同时这⾥出现了⼀个 customRefletable
的协议。
这⾥我们看⼀下具体⽤法: ⾸先我们遵循 customReflectable
协议,并实现其中的属性 customMirror
; customMirror
会返回⼀个 Mirror
对象。代码展示如下:
class ZGTeacher: CustomReflectable {
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
var customMirror: Mirror {
let info = KeyValuePairs<String, Any>.init(dictionaryLiteral: ("age", age),("name", name))
let mirror = Mirror.init(self, children: info, displayStyle: .class, ancestorRepresentation: .generated)
return mirror
}
}
var t = ZGTeacher(age: 18, name: "zhang")
print("end")
实现这个 CustomReflectable
最直观的区别在于我们在 lldb debug 中会出现更详细的 debug 信息。
po t
▿ <ZGTeacher: 0x1007405a0>
- age : 18
- name : "zhang"
回到我们的源码当中,对字符串 Mirror(internalReflecting
在源码当中进⾏检索,我们 就能够快速定位到 ReflectionMirror.swift
⽂件
extension Mirror {
internal init(internalReflecting subject: Any,
subjectType: Any.Type? = nil,
customAncestor: Mirror? = nil)
{
///subject:类型信息
let subjectType = subjectType ?? _getNormalizedType(subject, type: type(of: subject))
在当前文件中搜索getNormalizedType
@_silgen_name("swift_reflectionMirror_normalizedType")
internal func _getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type
这⾥使⽤了⼀个编译器字段 @_silgen_name
,其实是Swift的⼀个隐藏符号,作⽤是将某个C/C++语⾔函数直接映射为Swift函数。
什么意思那?我们的 Swift 是可以和C语⾔进⾏交互的,⽐如我们在C中定义⼀个函数:
int zg_add(int a, int b) {
return a + b;
}
接下来如果我们的 Swift 想要使⽤这个函数,我们要怎么做?⾸先要在 .h 头⽂件中暴露这个函数:
截屏2022-01-18 17.19.55.png然后在当前的桥接头⽂件当中暴露
截屏2022-01-18 17.19.44.png
这样我们就能在swift里直接使用了。
截屏2022-01-18 17.22.25.png
是不是⽐较麻烦,这⾥我们把头⽂件全部都删掉:
@_silgen_name("zg_add")
func swift_zg_add(a: Int32, b: Int32) -> Int32
var value = swift_zg_add(a: 10, b: 20)
print(value)
这里一样可以正常调用,是不是很简洁方便哪?
回到我们的源代码,搜索getNormalizedType<T>(_: T, type: Any.Type) -> Any.Type
最终调⽤的是 ReflectionMirror.cpp 中的 C++ 代码。
// 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 函数
的实现,这⾥其实是⼀个回调函数,当前回调的具体数据都是由 ReflectionMirrorImpl
结构体实现。
auto call = [&](ReflectionMirrorImpl *impl) {
impl->type = type;
impl->value = value;
auto result = f(impl);
return result;
};
ReflectionMirrorImpl
结构体的具体实现(可以看到这是⼀个抽象类,也就意味着不同类型的反射都需要去实现 ReflectionMirrorImpl
),这⾥我们在下⾯的代码中也能看到 class, struct, enum, Tuple
的具体实现。
truct 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() {}
};
这⾥我们⾸先以 struct
为例来看⼀下 Mirror
都是如何获取到这些数据的。
当前的属性数量 (可以看到的是,这⾥通过 Metadata
中的 getDescription
查询字段 NumFields
)
intptr_t count() override {
if (!isReflectable()) {
return 0;
}
auto *Struct = static_cast<const StructMetadata *>(type);
return Struct->getDescription()->NumFields;
}
其中 getDescription()
const TargetStructDescriptor<Runtime> *getDescription() const {
return llvm::cast<TargetStructDescriptor<Runtime>>(this->Description);
}
又⽐如具体属性的获取
AnyReturn subscript(intptr_t i, const char **outName,
void (**outFreeFunc)(const char *)) override {
unsigned tag;
const Metadata *payloadType;
bool indirect;
auto *caseName = getInfo(&tag, &payloadType, &indirect);
// Copy the enum itself so that we can project the data without destroying
// the original.
Any enumCopy;
auto *enumCopyContainer
= type->allocateBoxForExistentialIn(&enumCopy.Buffer);
type->vw_initializeWithCopy(enumCopyContainer,
const_cast<OpaqueValue *>(value));
// Copy the enum payload into a box
const Metadata *boxType = (indirect ? &METADATA_SYM(Bo).base : payloadType);
BoxPair pair = swift_allocBox(boxType);
type->vw_destructiveProjectEnumData(enumCopyContainer);
boxType->vw_initializeWithTake(pair.buffer, enumCopyContainer);
type->deallocateBoxForExistentialIn(&enumCopy.Buffer);
value = pair.buffer;
// If the payload is indirect, we need to jump through the box to get it.
if (indirect) {
const HeapObject *owner = *reinterpret_cast<HeapObject * const *>(value);
value = swift_projectBox(const_cast<HeapObject *>(owner));
}
*outName = caseName;
*outFreeFunc = nullptr;
Any result;
result.Type = payloadType;
auto *opaqueValueAddr = result.Type->allocateBoxForExistentialIn(&result.Buffer);
result.Type->vw_initializeWithCopy(opaqueValueAddr,
const_cast<OpaqueValue *>(value));
swift_release(pair.object);
return AnyReturn(result);
}
可以看到是这⾥通篇都是通过 Metadata , getDescription() , FieldDescrition
这⼏个东⻄来去实现的,⼀个是当前类型的元数据
,⼀个是当前类型的描述
,⼀个是对当前类型属性的描述
。所以看到这⾥我们能够明⽩ Mirror
是如何⼯作的。
同时我们在前⾯的探索过程中也探索了类,结构体和枚举,接下来我们尝试能不能⾃⼰利⽤ Metadata
来还原获取当前类型的各种属性。
三、StructMetadata源码分析
3.1 TargetStructMetadata
- 首先我们搜索
TargetStructMetadata
,找到TargetStructMetadata
的定义
template <typename Runtime>
struct TargetStructMetadata : public TargetValueMetadata<Runtime> {
/// 此处会返回一个TargetStructDescriptor类型的Description
const TargetStructDescriptor<Runtime> *getDescription() const {
return llvm::cast<TargetStructDescriptor<Runtime>>(this->Description);
}
};
TargetStructMetadata.png
可以看出
TargetStructMetadata
继承自TargetValueMetadata
- 搜索
TargetValueMetadata
,找到TargetValueMetadata
的定义
struct TargetValueMetadata : public TargetMetadata<Runtime> {
TargetSignedPointer<Runtime, const TargetValueTypeDescriptor<Runtime> *
__ptrauth_swift_type_descriptor> Description;
};
TargetValueMetadata.png
我们只看到有一个
Description
属性,它的类型是TargetValueTypeDescriptor
。TargetValueMetadata
继承自TargetMetadata
- 搜索
TargetMetadata
,找到TargetMetadata
的定义
struct TargetMetadata {
private:
/// The kind. Only valid for non-class metadata; getKind() must be used to get
/// the kind value.
StoredPointer Kind;
};
TargetMetadata.png
我们只看到它有一个
Kind
属性。
3.2 TargetValueTypeDescriptor
- 首先我们根据
TargetStructMetadata
,找到TargetStructMetadata
定义中的TargetStructDescriptor
,点击TargetStructDescriptor
,找到TargetStructDescriptor
的定义
class TargetStructDescriptor final
: public TargetValueTypeDescriptor<Runtime>,
public TrailingGenericContextObjects<TargetStructDescriptor<Runtime>,
TargetTypeGenericContextDescriptorHeader,
/*additional trailing objects*/
TargetForeignMetadataInitialization<Runtime>,
TargetSingletonMetadataInitialization<Runtime>,
TargetCanonicalSpecializedMetadatasListCount<Runtime>,
TargetCanonicalSpecializedMetadatasListEntry<Runtime>,
TargetCanonicalSpecializedMetadatasCachingOnceToken<Runtime>> {
public:
uint32_t NumFields;
uint32_t FieldOffsetVectorOffset;
};
TargetStructDescriptor.png
TargetStructDescriptor2.png
我们可以发现
TargetStructDescriptor
有两个属性,分别是NumFields
和FieldOffsetVectorOffset
。NumFields
主要表示结构体中属性的个数,如果只有一个字段偏移量则表示偏移量的长度。FieldOffsetVectorOffset
表示这个结构体元数据中存储的属性的字段偏移向量的偏移量,如果是0则表示没有。TargetStructDescriptor
继承自 TargetValueTypeDescriptor
- 点击
TargetValueTypeDescriptor
,找到TargetValueTypeDescriptor
的定义
class TargetValueTypeDescriptor
: public TargetTypeContextDescriptor<Runtime> {
public:
static bool classof(const TargetContextDescriptor<Runtime> *cd) {
return cd->getKind() == ContextDescriptorKind::Struct ||
cd->getKind() == ContextDescriptorKind::Enum;
}
};
TargetValueTypeDescriptor.png
我们并没有找到太多有用的信息,那么我们继续向父类寻找。
- 点击
TargetTypeContextDescriptor
,找到TargetTypeContextDescriptor
的定义
class TargetTypeContextDescriptor
: public TargetContextDescriptor<Runtime> {
public:
TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false> Name;
TargetRelativeDirectPointer<Runtime, MetadataResponse(...),
/*Nullable*/ true> AccessFunctionPtr;
TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor,
/*nullable*/ true> Fields;
};
TargetTypeContextDescriptor.png
我们发现
TargetTypeContextDescriptor
有Name、AccessFunctionPtr、Fields
三个属性:Name
就是类型的名称AccessFunctionPtr
是该类型元数据访问函数的指针Fields
是一个指向该类型的字段描述符的指针接下来我们再看看
TargetTypeContextDescriptor
的父类中还有什么有用的信息
- 点击
TargetContextDescriptor
,找到TargetContextDescriptor
的定义
struct TargetContextDescriptor {
ContextDescriptorFlags Flags;
TargetRelativeContextPointer<Runtime> Parent;
};
TargetContextDescriptor.png
这里我们可以看到:
1、这就是
descriptor
的基类2、有两个属性,分别是
Flags
和Parent
3、
Flags
是描述上下文的标志,包括它的种类和格式版本。4、
Parent
是记录父类上下文的,如果是顶级则为null
。
3.3 Description中的属性
-
Flags
Flags.png
从截图的源码中我们可以看到这个FLags
实际是个uint32_t
的值,按位存储着kind、isGeneric、isUnique、version、kindSpecificFlags
等信息。 -
Parent
Parent
的类型是TargetRelativeContextPointer<Runtime>
,我们来看一下TargetRelativeContextPointer
,点击跳转过去:
TargetRelativeContextPointer.png
我们可以看到TargetRelativeContextPointer
是取自RelativeIndirectablePointer
的别名,继续点击进行查看:
RelativeIndirectablePointer.png
根据注释知道这个类的主要作用是存储在内存中的对象的相对引用
。通过RelativeOffsetPlusIndirect
属性存储相对的地址偏移量。
再通过get()函数
获取,在get()函数
中,会调用applyRelativeOffset函数
,进行地址的偏移。 applyRelativeOffset.png
最后返回的时候我们可以看到
base + extendOffset
,基地址加上偏移的值,最后得到真实的地址。
- Fields
TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor,
/*nullable*/ true> Fields;
这里看看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;
};
这里有5个属性:
1、MangledTypeName
2、Superclass
3、kind
4、FieldRecordSize
5、NumFields
关于getFieldRecordBuffer函数
的返回值FieldRecord
源码如下:
FieldRecord
主要是封装了一些属性,用于存储这些值。
3.4 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 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()函数
:
这里的
Struct
就是个TargetStructMetadata
类型,通过getDescription()函数
获取到一个TargetStructDescriptor
类型的Description
,然后取NumFields**的值就是我们要的**
count`。
3.6 属性名和属性值
我们知道在Mirror
中通过其初始化方法返回一个提供该值子元素的AnyCollection<Child>
类型的children
集合,children
是一个元组(label: String?, value: Any)
,label
是一个可选类型的属性名,value
是属性值。
在分析
internalReflecting
函数的时候,我们说children
是懒加载的,而加载的时候会调用getChild
方法,getChild
方法源码入下:getChild.png
在
getChild
方法中还会调用_getChild
方法,源码如下:_getChild.png
_getChild
方法同样是使用@_silgen_name
修饰符最终调用的C++中的swift_reflectionMirror_subscript
函数。
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);
});
}
这里我们可以看到是调用了impl
的subscript
函数,同样以结构体为例,我们在StructImpl
中找到该函数,源码如下:
通过
subscript
函数我们可以看到这里面还会调用childMetadata
获取到fieldInfo
,其实这里就是获取type
,也就是属性名
,通过childOffset函数和index
获取到对应的偏移量
,最后根据内存偏移取到属性值。childMetadata
核心点是调用getFieldAt
函数获取属性名称
。
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 result = 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.
TypeInfo typeInfo;
if (result.isError()) {
typeInfo = TypeInfo({&METADATA_SYM(EMPTY_TUPLE_MANGLING),
MetadataState::Complete}, {});
auto *error = result.getError();
char *str = error->copyErrorString();
missing_reflection_metadata_warning(
"warning: the Swift runtime was unable to demangle the type "
"of field '%*s'. the mangled type name is '%*s': %s. this field will "
"show up as an empty tuple in Mirrors\n",
(int)name.size(), name.data(), (int)typeName.size(), typeName.data(),
str);
error->freeErrorString(str);
} else {
typeInfo = result.getType();
}
auto fieldType = FieldType(typeInfo.getMetadata());
fieldType.setIndirect(field.isIndirectCase());
fieldType.setReferenceOwnership(typeInfo.getReferenceOwnership());
fieldType.setIsVar(field.isVar());
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) override {
auto *Clas = static_cast<const ClassMetadata*>(type);
auto description = Clas->getDescription();
if (i < 0 || (size_t)i > description->NumFields)
swift::crash("Swift mirror subscript bounds check failure");
// FIXME: If the class has ObjC heritage, get the field offset using the ObjC
// metadata, because we don't update the field offsets in the face of
// resilient base classes.
uintptr_t fieldOffset;
if (usesNativeSwiftReferenceCounting(Clas)) {
fieldOffset = Clas->getFieldOffsets()[i];
} else {
#if SWIFT_OBJC_INTEROP
Ivar *ivars = class_copyIvarList(
reinterpret_cast<Class>(const_cast<ClassMetadata *>(Clas)), nullptr);
fieldOffset = ivar_getOffset(ivars[i]);
free(ivars);
#else
swift::crash("Object appears to be Objective-C, but no runtime.");
#endif
}
return (intptr_t)fieldOffset;
}
这里面是调用TargetStructMetadata
中的getFieldOffsets
函数源码如下:
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
。获取到偏移值后通过内存偏移即可获取到属性值。
四、还原StructMetadata
4.1 TargetStructMetadata
首先我们需要拥有一个结构体的元数据结构,这里我们命名为StructMetadata
,里面有继承的kind
和Descriptor
属性,这里的Descriptor
属性是一个TargetStructDescriptor
类型的指针。
struct TargetStructMetadata {
var Kind: Int
var typeDescription: UnsafeMutablePointer<TargetStructDescriptor>
}
4.2 TargetStructDescriptor
对于结构体来说其内部有7个属性
1、flag
是个32位的整形,我们用Int32
代替
2、parent
是记录父类的,类型TargetRelativeDirectPointer<Runtime>
,这里也可以用Int32
代替
3、name
是记录类型的,它的类型是TargetRelativeDirectPointer<char>
,所以我们需要实现一个TargetRelativeDirectPointer
4、AccessFunctionPtr
与name
类似,内部是个指针
5、Fields
也与name
类似,内部是个FieldDescriptor
6、NumFields
使用Int32
7、FieldOffsetVectorOffset
也是用Int32
仿写实现如下:
struct TargetStructDescriptor {
var Flags: Int32
var Parent: Int32
var Name: TargetRelativeDirectPointer<CChar>
var AccessFunctionPtr: TargetRelativeDirectPointer<UnsafeRawPointer>
var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor>
var NumFields: Int32
/// 每一个属性距离当前实例对象地址的偏移量
var FieldOffsetVectorOffset: Int32
var genericArgumentOffset: Int {
return 2
}
}
4.3 TargetRelativeDirectPointer
-
TargetRelativeDirectPointer
是RelativeDirectPointer
的别名,其内部有一个继承的RelativeOffset
属性,是int32_t
类型,我们可以用Int32
代替。 - 还有一个
get
方法,内部通过指针偏移
获取值。
struct TargetRelativeDirectPointer<Pointee> {
var offset: Int32
///模拟RelativeDirectPointerImpl类中的get方法 this+offset指针
mutating func getmeasureRelativeOffset() -> UnsafeMutablePointer<Pointee> {
let offset = self.offset
return withUnsafePointer(to: &self) { p in
/*
获得self,变为raw,然后+offset
- UnsafeRawPointer(p) 表示this
- advanced(by: numericCast(offset) 表示移动的步长,即offset
- assumingMemoryBound(to: T.self) 表示假定类型是T,即自己指定的类型
- UnsafeMutablePointer(mutating:) 表示返回的指针类型
*/
return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: Pointee.self))
}
}
}
4.4 FieldDescriptor
FieldDescriptor
在Mirror反射
中有着很重要的作用,其内部有5个属性:
-
MangledTypeName
是RelativeDirectPointer<const char>
类型,我们使用TargetRelativeDirectPointer<CChar>
代替 -
Superclass
与MangledTypeName
一样 -
Kind
是FieldDescriptorKind
类型,实际是uint16_t
,这里我们使用UInt16
代替 -
FieldRecordSize
是UInt16
也使用使用UInt16
代替 -
NumFields
使用Int32
代替 -
fields
,其实从属性上看不到有这个,但是这里面有个getFieldRecordBuffer
方法,通过this+1
的方式一个一个的访问属性,所以这是一块连续的内存空间,我们使用fields
代替
struct FieldDescriptor {
var MangledTypeName: TargetRelativeDirectPointer<CChar>
var Superclass: TargetRelativeDirectPointer<CChar>
var Kind: UInt16
var FieldRecordSize:UInt16
var NumFields: UInt32
var fields: FiledRecordBuffer<FieldRecord>
}
4.5 FieldRecord
FieldRecord
存储着属性的相关信息,其内部有三个属性
-
Flags
是FieldRecordFlags
类型实际是uint32_t
,这里我们使用Int32
代替 -
MangledTypeName
使用TargetRelativeDirectPointer<CChar>
代替 -
FieldName
使用TargetRelativeDirectPointer<CChar>
代替
struct FieldRecord {
var Flags: UInt32
var MangledTypeName: TargetRelativeDirectPointer<CChar>
var FieldName: TargetRelativeDirectPointer<CChar>
}
struct FiledRecordBuffer<Element> {
var element: Element
mutating func buffer(n: Int) -> UnsafeBufferPointer<Element> {
return withUnsafePointer(to: &self) {
let ptr = $0.withMemoryRebound(to: Element.self, capacity: 1) { start in
return start
}
return UnsafeBufferPointer(start: ptr, count: n)
}
}
mutating func index(of i: Int) -> UnsafeMutablePointer<Element> {
return withUnsafePointer(to: &self) {
return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self).advanced(by: i))
}
}
}
五、打印属性信息和属性值
定义一个结构体:
struct ZGPerson {
var age: Int = 18
var name: String = "Zhang"
}
var p = ZGPerson()
5.1 绑定结构体内存
使用unsafeBitCast
按位强转,将ZGPerson
绑定到StructMetadata
上,注意,这个操作非常危险,没有任何校验和修饰。
let ptr = unsafeBitCast(ZGPerson.self as Any.Type, to: UnsafeMutablePointer<TargetStructMetadata>.self)
5.2 打印类名和属性个数
let typeDescription = ptr.pointee.typeDescription
let namePtr = typeDescription.pointee.Name.getmeasureRelativeOffset()
print("current class name: \(String(cString: namePtr))")
let numFields = typeDescription.pointee.NumFields
print("当前类属性的个数:\(numFields)")
5.3 打印属性名称和属性值
1、打印一下属性的名称,首先是获取到FieldDescriptor
的指针,然后通过内存偏移的方式访问每一个FieldRecord
,最后再访问FieldRecord
中的属性名
。
2、打印属性值:
- 首先获取
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.typeDescription.pointee.FieldOffsetVectorOffset))).assumingMemoryBound(to: Int32.self), count: Int(ptr.pointee.typeDescription.pointee.NumFields))
for i in 0 ..< numFields {
let fieldDespritor = typeDescription.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.FieldName.getmeasureRelativeOffset()
print("--- fixed \(String(cString: fieldDespritor)) info begin ---")
let mangledTypeName = typeDescription.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.MangledTypeName.getmeasureRelativeOffset()
let genericVector = UnsafeRawPointer(ptr).advanced(by: typeDescription.pointee.genericArgumentOffset * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self)
let fieldType = swift_getTypeByMangledNameInContext(mangledTypeName, 256, UnsafeRawPointer(typeDescription), UnsafeRawPointer(genericVector).assumingMemoryBound(to: Optional<UnsafeRawPointer>.self))
let type = unsafeBitCast(fieldType, to: Any.Type.self)
let value = customCast(type: type)
let fieldOffset = bufferPtr[Int(i)]
let valuePtr = withUnsafeMutablePointer(to: &p) { $0 }
print("fieldType: \(type) \nfieldValue: \(value.get(from: UnsafeRawPointer(UnsafeRawPointer(valuePtr).advanced(by: numericCast(fieldOffset)))))")
print("--- field: \(String(cString: fieldDespritor)) info end ---\n")
}
本文部分资料参考了下列文献
作者:愤怒的apple
链接:https://juejin.cn/post/7052644744693809183/
来源:稀土掘金
网友评论