解析swift-HeapMetaData

众所周知,在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
}

![截屏2021-06-20 下午11.51.14.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fa7294ac9ea84325b2bf4c07bb5c04d8~tplv-k3u1fbpfcp-watermark.image)
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?
复制代码

最终打印如下:

截屏2021-06-20 下午11.51.14.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享