swift进阶StructMetadata分析和还原
本文主要介绍StructMetadata源码的分析,然后还原StructMetadata,最后打印属性信息和属性值
一、StructMetadata源码分析
1.1 TargetStructMetadata
- 首先我们搜索
TargetStructMetadata
,进入TargetStructMetadata
的定义
```
template
// 此处会返回一个TargetStructDescriptor类型的Description
const TargetStructDescriptor
可以看到TargetStructMetadata
继承自TargetValueMetadata
- 继续搜索
TargetValueMetadata
```
template
ConstTargetMetadataPointer
我们只看到有一个Description
属性,它的类型是TargetValueTypeDescriptor
。TargetValueMetadata
继承自TargetMetadata
- 继续搜索
TargetMetadata
template <typename Runtime>
struct TargetMetadata {
private:
/// The kind. Only valid for non-class metadata; getKind() must be used to get
/// the kind value.
StoredPointer Kind;
}
我们只看到有一个Kind
属性
接下来我们搜索TargetValueTypeDescriptor
1.2 TargetValueTypeDescriptor
```
template
public: uint32_t NumFields; uint32_t FieldOffsetVectorOffset; } ```
看到TargetStructDescriptor
继承自TargetValueTypeDescriptor
我们还可以发现两个属性,分别是NumFields
和FieldOffsetVectorOffset
NumFields
主要表示结构体中属性的个数,如果只有一个字段偏移量则表示偏移量的长度-
FieldOffsetVectorOffset
表示这个结构体元数据中存储的属性的字段偏移向量的偏移量,如果是0
则表示没有 -
搜索
TargetValueTypeDescriptor
没有找到太多有用的信息,我们继续向父类寻找。
- 搜索
TargetTypeContextDescriptor
- 该类继承自
TargetContextDescriptor
- 有
Name
、AccessFunctionPtr
、Fields
三个属性 Name
就是类型的名称AccessFunctionPtr
是该类型元数据访问函数的指针-
Fields
是一个指向该类型的字段描述符的指针 -
TargetContextDescriptor
接下来我们再看看TargetTypeContextDescriptor
的父类中还有什么有用的信息
这里我们可以看到:
- 这就是
descriptors
的基类 - 有两个属性,分别是
Flags
和Parent
Flags
是描述上下文的标志,包括它的种类和格式版本。Parent
是记录父类上下文的,如果是顶级则为null
1.3 Description中的属性
- Flags
从以上的代码中我们可以看到这个FLags实际是个uint32_t
的值,按位存储着kind
、isGeneric
、isUnique
、version
等信息。
- Parent
Parent
的类型是TargetRelativeContextPointer<Runtime>
,我们看看TargetRelativeContextPointer
,点击跳转过去:
我们可以看到TargetRelativeContextPointer
是取自RelativeIndirectablePointer
的别名,继续点击进行查看:
根据注释知道这个类的主要作用是存储在内存中的对象的相对引用。通过RelativeOffsetPlusIndirect
属性存储相对的地址偏移量
在通过get()
函数获取,在get()
函数中,会调用applyRelativeOffset
函数,进行地址的偏移,applyRelativeOffset
源码:
最后返回的时候我们可以看到base + extendOffset
;基地址加上偏移的值,最后得到真实的地址。
- Fields
TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor,
/*nullable*/ true> Fields;
这里看看FieldDescriptor
点击跳转到其源码处,部分源码如下:
这里有5个属性:
-
MangledTypeName
-
Superclass
-
kind
-
FieldRecordSize
-
NumFields
关于getFieldRecordBuffer
函数的返回值FieldRecord
源码如下:
FieldRecord
主要是封装了一些属性,用于存储这些值。
1.5 type
首先我们看看type
是怎么取的:
首先是调用swift_reflectionMirror_normalizedType
函数
比如说这是个结构体,此时的impl
就是个StructImpl
类型,所以这里的type
是StructImpl
父类ReflectionMirrorImpl
的属性type
。
1.6 count
关于count
的获取首先是调用swift_reflectionMirror_count
函数
同样还以结构体为例,此时的impl
为StructImpl
,内部的count()
函数:
这里的Struct
就是个TargetStructMetadata
类型,通过getDescription()
函数获取到一个TargetStructDescriptor
类型的Description
,然后取NumFields
的值就是我们要的count
。
1.7 属性名和属性值
我们知道在Mirror
中通过其初始化方法返回一个提供该值子元素的AnyCollection<Child>
类型的children
集合,Child
是一个元组(label: String?
, value: Any
),label
是一个可选类型的属性名,value是属性值。
在分析internalReflecting
函数的时候,我们说children
是懒加载的,而加载的时候会调用getChild
方法,getChild
方法源码入下:
在getChild
方法中还会调用_getChild
方法,源码如下
_getChild
方法同样是使用@_silgen_name
修饰符最终调用的C++
中的swift_reflectionMirror_subscript
函数。
这里我们可以看到是调用了impl
的subscript
函数,同样以结构体为例,我们在StructImpl
中找到该函数,源码如下:
通过subscript
函数我们可以看到这里面还会调用childMetadata
获取到fieldInfo
,其实这里就是获取type
,也就是属性名,通过childOffset
函数和index
获取到对于的偏移量,最后根据内存偏移去到属性值。childMetadata核心点是调用getFieldAt
函数获取属性名称。
我们可以看到在上面这个方法中:
- 首先通过
getTypeContextDescriptor
获取baseDesc
,也就是我们说的Description
- 然后通过
Fields.get()
获取到fields
- 接着通过
getFields()[index]
或取对应的field
- 最后通过
getFieldName()
函数获取到属性名称 getTypeContextDescriptor
函数在struct TargetMetadata
中,通过这个函数获取到一个TargetStructDescriptor
,它的父类的父类TargetTypeContextDescriptor
中的Fields
属性Fields
属性的类型TargetRelativeDirectPointer
中有get
方法- 实际中使用的
FieldDescriptor
类中getFieldRecordBuffer
方法返回的FieldRecord
中的getFieldName
函数
getFields 源码:
关于getFields
我们可以看到这是一块连续的空间,在begin
和end
中:
begin
就是getFieldRecordBuffer
getFieldRecordBuffer
就是Begin + NumFields
- 所以这就是一块连续内存的访问
childOffset 源码:
分析完了属性名的获取,我们来看看偏移量的获取
这里面是调用TargetStructMetadata
中的getFieldOffsets
函数源码如下:
我们可以看到这里统一是通过获取Description
中的属性,这里使用的属性是FieldOffsetVectorOffset
。获取到偏移值后通过内存偏移即可获取到属性值。
二、还原StructMetadata
2.1 TargetStructMetadata
首先我们需要拥有一个结构体的元数据结构,这里我们命名为StructMetadata
,里面有继承的kind
和Descriptor
属性,这里的Descriptor
属性是一个TargetStructDescriptor
类型的指针。
struct TargetStructMetadata {
var Kind: Int
var typeDescription: UnsafeMutablePointer<TargetStructDescriptor>
}
2.2 TargetStructDescriptor
对于结构体来说其内部有7个属性
-
flag
是个32
位的整形,我们用Int32代替 -
parent
是记录父类的,类型是TargetRelativeDirectPointer<Runtime>
,这里也可以用Int32代替 -
name
记录类型的,它的类型是TargetRelativeDirectPointer<char>
,所以我们需要实现一个TargetRelativeDirectPointer
-
AccessFunctionPtr
与name
类似,内部是个指针 -
Fields
也与name
类似,内部是个FieldDescriptor
-
NumFields
使用Int32
-
FieldOffsetVectorOffset
也是用Int32
仿写实现如下:
```
struct TargetStructDescriptor {
var Flags: Int32
var Parent: Int32
var Name: TargetRelativeDirectPointer
var genericArgumentOffset: Int {
return 2
}
} ```
2.3 TargetRelativeDirectPointer
TargetRelativeDirectPointer
是RelativeDirectPointer
的别名,其内部有一个继承的RelativeOffset
属性,是int32_t
类型,我们可以用Int32
代替。- 还有一个
get
方法,内部通过指针偏移获取值。
仿写实现如下:
```
struct TargetRelativeDirectPointer
- 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))
}
}
} ```
2.4 FieldDescriptor
FieldDescriptor
在Mirror
反射中有着很重要的作用,其内部有5个属性:
MangledTypeName
是RelativeDirectPointer<const char>
类型,我们使用TargetRelativeDirectPointer<CChar>
代替Superclass
与MangledTypeName
一样Kind
是FieldDescriptorKind
类型,实际是uint16_t
,这里我们使用UInt16
代替FieldRecordSize
是uint16_t
也使用使用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: FieldRecordBuffer<FieldRecord>
}
2.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 Person {
var age: Int = 18
var name: String = "tony"
}
var p = Person()
3.1 绑定结构体内存
使用unsafeBitCast
按位强转,将Person
绑定到StructMetadata
上,这个操作非常危险,没有任何校验和修饰
let ptr = unsafeBitCast(Person.self as Any.Type, to: UnsafeMutablePointer<TargetStructMetadata>.self)
3.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)") ```
3.3 打印属性名称和属性值
- 打印一下属性的名称,首先是获取到
FieldDescriptor
的指针,然后通过内存偏移的方式访问每一个FieldRecord
,最后在访问FieldRecord
中的属性名。 - 打印属性值:
- 首先获取
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")
} ```
打印结果: