众所周知,在Objective-C中,一个对象的前8个字节有个ISA指针,指向它的类对象,类对象包含着它的对象信息,有属性,成员变量方法等等,而在Swift中,一个纯Swift的class,对象的前8个字节指向HeapMetaData,8-16个字节保存着内存管理相关的数据,本篇文章主要是介绍HeapMetaData的结构如下。
struct HeapMetadata {
/// 种类,通过此字段可以知道是class还是结构体亦或者其他类型
var Kind: Int
/// 父类
var Superclass: Any.Type?
var CacheData1: UnsafeRawPointer
var CacheData2: UnsafeRawPointer
var Data: Int
var flags: Int32
var InstanceAddressPoint: UInt32
var InstanceSize: UInt32
var InstanceAlignMask: UInt16
var Reserved: UInt16
var ClassSize: UInt32
var ClassAddressPoint: UInt32
/// 此属性包含着了类名以及属性信息
var Description: UnsafePointer<TargetClassDescriptor>
var IVarDestroyer: UnsafeRawPointer
}

struct TargetClassDescriptor {
var Flags: UInt32;
var Parent: Int32;
/// 类名,里面存储的是相对偏移位置
var Name: Int32;
var AccessFunctionPtr: Int32;
/// 属性信息,里面存储的也是相对偏移位置,对应的类型是FieldDescriptor
var Fields: Int32
var SuperclassType: UInt32;
var MetadataNegativeSizeInWords: Int32
var MetadataPositiveSizeInWords: Int32
var NumImmediateMembers: Int32;
var NumFields: Int32;
var FieldOffsetVectorOffset: Int32;
}
struct FieldDescriptor {
var MangledTypeName: Int32
var Superclass: Int32
/// 类型种类。结构体,枚举或其他
var FieldDescriptorKind: FieldDescriptorKind
var FieldRecordSize: Int16
var NumFields: Int32
// 属性信息没有直接声明在属性描述的结构体中
// 而是紧接着存放在属性结构体的后面,按照声明的顺序依次进行存放
func fields(pointer: UnsafePointer<FieldDescriptor>) -> [FieldRecordPoint] {
var fields = [FieldRecordPoint]()
let base = unsafeBitCast(pointer + 1, to: UnsafePointer<FieldRecord>.self)
for i in 0..<self.NumFields {
fields.append(FieldRecordPoint(pointer: base + Int(i)))
}
return fields
}
}
struct FieldRecord {
/// 属性的flag,若第一位为1,则表示递归的枚举,若第二位为1,则表示是var类型的变量
var fieldRecordFlags: UInt32
/// 属性名字的偏移位置
var mangledTypeNameOffset: Int32
/// 属性名字的偏移位置
var fieldNameOffset: Int32
func isVar() -> Bool {
return fieldRecordFlags & 2 == 2
}
func IsIndirectCase() -> Bool {
return fieldRecordFlags & 1 == 1
}
}
enum FieldDescriptorKind : UInt16 {
case Struct = 0
case Class
case Enum
case MultiPayloadEnum
case `Protocol`
case ClassProtocol
case ObjCProtocol
case ObjCClass
}
复制代码
通过下面的例子进行一个验证,创建一个class如下
class TestClass {
var age: Int = 18
var name: String = "lkk"
}
复制代码
然后通过转换获取类型,获取类名以及属性名字和类型
/// 把类信息强制转换为HeapMetadata指针
let metadata = unsafeBitCast(TestClass.self, to:UnsafePointer<HeapMetadata>.self)
/// 获取描述信息中类名的偏移地址
let nameOffset = Int(metadata.pointee.Description.pointee.Name)
/// 计算类名的真实地址
let nameAddress = Int(bitPattern: metadata.pointee.Description) + 2 * 4 + nameOffset
/// 通过地址获取类型
let classNamePtr = UnsafePointer<CChar>(bitPattern: nameAddress)
let name = NSString(cString: classNamePtr!, encoding: String.Encoding.utf8.rawValue)
print(name!)
/// 获取描述信息中属性信息的偏移地址
let fileOffset = Int(metadata.pointee.Description.pointee.Fields)
/// 计算属性信息的真实地址
let fileAddress = Int(bitPattern: metadata.pointee.Description) + 4 * 4 + fileOffset
let filePtr = UnsafePointer<FieldDescriptor>(bitPattern: fileAddress)
/// 获取到所有的属性信息
let fileds = filePtr!.pointee.fields(pointer: filePtr!)
for filed in fileds {
let name = filed.fieldName
if let cMangledTypeName = filed.mangledTypeName,
/// 通过此方法把filed.mangledTypeName转成真正的信息
let fieldType = _getTypeByMangledNameInContext(cMangledTypeName, 256, genericContext: metadata.pointee.Description, genericArguments: metadata) {
print("fieldName=\(name),fieldType=\(fieldType)")
}
}
@_silgen_name("swift_getTypeByMangledNameInContext")
public func _getTypeByMangledNameInContext(
_ name: UnsafePointer<UInt8>,
_ nameLength: Int,
genericContext: UnsafeRawPointer?,
genericArguments: UnsafeRawPointer?)
-> Any.Type?
复制代码
最终打印如下:
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END