在swift源码中有这样一段代码,可以看到所有的类型都是有元类型的,
类
、枚举
、结构体
都被已经被定义了,下面就通过mirror
来还原TargetStructMetadata结构。
1、Mirror
struct Teacher{
var age = 18
var name = "henry"
var height = 1.8
var hobby = "woman"
}
let t = Teacher()
let mirror = Mirror(reflecting: t)
for pro in mirror.children{
print("\(pro.label):\(pro.value)")
}
为什么Mirror可以获取到结构体的属性名称?
1.1 Mirror源码
public struct Mirror {
//初始化方法
public init(reflecting subject: Any) {
if case let customized as CustomReflectable = subject {
self = customized.customMirror
} else {
self = Mirror(internalReflecting: subject)
}
}
}
1.2 Mirror初始化
extension Mirror {
internal init(internalReflecting subject: Any,
subjectType: Any.Type? = nil,
customAncestor: Mirror? = nil)
{
// type(of:)获取到subject的真实类型
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
}
if let customAncestor = customAncestor {
if superclass == customAncestor.subjectType {
return customAncestor
}
if customAncestor._defaultDescendantRepresentation == .suppressed {
return customAncestor
}
}
return Mirror(internalReflecting: subject,
subjectType: superclass,
customAncestor: customAncestor)
}
// 获取并解析显示的样式,并设置Mirror的其他属性
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
}
-
type(of:)
获取到subject的真实类型
2、TargetStructMetadata结构
源码中继承关系比较复杂,因为设计到继承和一些模板类,所以只放出一张结构图:
转换为代码形式:
struct StructMetaData{
var kind : Int32
var description : UnsafePointer<StructDescriptor>
}
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
}
struct FieldDescriptor {
var MangledTypeName: RelativePointer<CChar>
var Superclass: RelativePointer<CChar>
var kind: UInt16
var fieldRecordSize: Int16
var numFields: Int32
var fields: FieldRecord //连续的存储空间
}
struct FieldRecord {
var Flags: Int32
var MangledTypeName: RelativePointer<CChar>
var FieldName: RelativePointer<CChar>
}
struct RelativePointer<T> {
var offset:Int32
mutating public func get() -> UnsafePointer<T> {
let offSet = self.offset
return withUnsafePointer(to: &self){ ptr in
UnsafeRawPointer(ptr).advanced(by: numericCast(offSet)).assumingMemoryBound(to: T.self)
}
}
}
3、还原结构
根据源码要先获取元类指针,2种方式均可。swift指针使用起来有点难度(摊牌了我是个菜鸡。。。)swift底层探索 06 - 指针简单使用
- withUnsafeMutablePointer
var type = Teacher.self
//指向元类指针的指针
let a = withUnsafeMutablePointer(to: &type) { ptr in
UnsafeRawPointer(ptr).bindMemory(to: UnsafeMutablePointer<StructMetaData>.self, capacity: 1)
}
// 获取元类指针
let metaPtr = a.pointee
- 注意:
a是 : 指向元类指针的指针
- unsafeBitCast按位强转
let metaPtr = unsafeBitCast(Teacher.self as Any.Type, to: UnsafePointer<StructMetaData>.self)
- unsafeBitCast需要两个参数的内存大小相同。必须使用:
Teacher.self as Any.Type
进行转换,因为根据测试发现Teacher.self
获取内存大小为0
(这个地方是真的坑),如下图:
Teacher.self as Any.Type 动作
根据sil查看发现
Teacher.self as Any.Type
在底层的操作;
- sil官方文档
- 创建 T 的元类型。
3.1 获取结构体名
let structName = metaPtr.pointee.description.pointee.name.get()
let structNameStr = String(cString: structName)
print("获取结构体名:\(structNameStr)")
输出:
3.2 获取内部属性个数
let cnt = metaPtr.pointee.description.pointee.NumFields
print("获取结构体属性个数:\(cnt)")
输出:
3.3 获取所有属性
let fieldsPtr = b.pointee.description.pointee.Fields.get()
for i in 0..<cnt {
let record = withUnsafePointer(to: &fieldsPtr.pointee.fields) {
UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: FieldRecord.self).advanced(by: numericCast(i)))
}
let recordNameStr = String(cString: record.pointee.FieldName.get())
let manNameStr = String(cString: record.pointee.MangledTypeName.get())
print("类型名:\(recordNameStr) ---- 类型类型:\(manNameStr)")
}
输出:
- si : 其实就是Int
- ss : 其实就是String
- sd : 其实就是Double