該篇主要是關(guān)于研究Class和Struct的底層結(jié)構(gòu),以及Swift反射的相關(guān)知識(shí)。
1.Struct和Class的內(nèi)存分布
class Size {
var width = 1
var height = 2
}
struct Point {
var x = 3
var y = 4
}
眾所周知,結(jié)構(gòu)體的值是直接儲(chǔ)存在棾诔担空間,類的值是儲(chǔ)存在堆空間蒲每,椃柞耍空間是堆的地址指針。
那我們所研究的底層結(jié)構(gòu)又是什么邀杏?儲(chǔ)存在哪里贫奠?
2. 從Mirror的解析開始
說要獲取底層結(jié)構(gòu),不得不先介紹Mirror。不同于OC的動(dòng)態(tài)性質(zhì)唤崭,可以通過Runtime獲取到對象的屬性類型等信息拷恨。swift標(biāo)準(zhǔn)庫提供了反射機(jī)制,用來訪問成員信息谢肾,即Mirror腕侄。
Mirror反射是指可以動(dòng)態(tài)獲取類型、成員信息勒叠,在運(yùn)行時(shí)可以調(diào)用方法兜挨、屬性等行為的特性。
class Person {
var name: String = "xiaohei"
var age: Int = 18
var height = 1.85
}
var p = Person()
var mirror = Mirror(reflecting: p.self)
print("對象類型:\(mirror.subjectType)")
print("對象屬性個(gè)數(shù):\(mirror.children.count)")
print("對象的屬性及屬性值")
for child in mirror.children {
print("\(child.label!)---\(child.value)")
}
通過Mirror眯分,我們可以更簡單的獲取到對象的屬性值拌汇。那系統(tǒng)是如何通過Mirror獲取對應(yīng)的屬性以及值的?
通過源碼分析弊决,我們可以看到我們所需要的信息都是從Mirror這個(gè)結(jié)構(gòu)體獲取的噪舀。在我看來,Mirror更像一個(gè)包裝類飘诗,因?yàn)樗b了更底層結(jié)構(gòu)的東西与倡。
public struct Mirror {
public enum DisplayStyle {
case `struct`, `class`, `enum`, tuple, optional, collection
case dictionary, `set`
}
public let subjectType: Any.Type
public let children: Children
public let displayStyle: DisplayStyle?
public var superclassMirror: Mirror? {
return _makeSuperclassMirror()
}
// 初始化方法
public init(reflecting subject: Any) {
if case let customized as CustomReflectable = subject {
self = customized.customMirror
} else {
self = Mirror(internalReflecting: subject)
}
}
}
Mirror的實(shí)現(xiàn)是由一部分Swift代碼加上另一部分C++代碼。很多代碼就不截取了昆稿,多了反而亂纺座,這邊主要講的是思路,具體解析文章可以參考這篇《Swift中的反射Mirror》溉潭。
Mirror內(nèi)部的初始化方法是通過實(shí)例(包括對象净响,結(jié)構(gòu)體等)創(chuàng)建的,獲取類型通過_getNormalizedType喳瓣,獲取count 通過_getChildCount馋贤。
internal init(internalReflecting subject: Any,
subjectType: Any.Type? = nil,
customAncestor: Mirror? = nil)
{
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
}
// Handle custom ancestors. If we've hit the custom ancestor's subject type,
// or descendants are suppressed, return it. Otherwise continue reflecting.
if let customAncestor = customAncestor {
if superclass == customAncestor.subjectType {
return customAncestor
}
if customAncestor._defaultDescendantRepresentation == .suppressed {
return customAncestor
}
}
return Mirror(internalReflecting: subject,
subjectType: superclass,
customAncestor: customAncestor)
}
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
}
比如獲取類型的底層實(shí)現(xiàn)來說配乓,其本質(zhì)就是獲取ReflectionMirrorImpl對應(yīng)的type(當(dāng)然惠毁,更下面還有一層犹芹,Impl通常也只作為一層包裝)仁讨。
// 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; });
}
ReflectionMirrorImpl 是一個(gè)“基類”,提供了一些基礎(chǔ)的屬性洞豁。ReflectionMirrorImpl有以下6個(gè)子類:
TupleImpl 元組的反射
StructImpl 結(jié)構(gòu)體的反射
EnumImpl 枚舉的反射
ClassImpl 類的反射
MetatypeImpl 元數(shù)據(jù)的反射
OpaqueImpl 不透明類型的反射
所以終究來說盐固,比如結(jié)構(gòu)體的Mirror獲取的是對應(yīng)的StructImpl的東西荒给。這邊我們也以結(jié)構(gòu)體為例刁卜,來講解結(jié)構(gòu)體反射的思路蛔趴。
// Implementation for structs.
struct StructImpl : ReflectionMirrorImpl {
bool isReflectable() {
const auto *Struct = static_cast<const StructMetadata *>(type);
const auto &Description = Struct->getDescription();
return Description->isReflectable();
}
// 表明是結(jié)構(gòu)體
char displayStyle() {
return 's';
}
// 獲取屬性個(gè)數(shù)
intptr_t count() {
if (!isReflectable()) {
return 0;
}
auto *Struct = static_cast<const StructMetadata *>(type);
return Struct->getDescription()->NumFields;
}
// 獲取每個(gè)屬性的偏移值
intptr_t childOffset(intptr_t i) {
auto *Struct = static_cast<const StructMetadata *>(type);
if (i < 0 || (size_t)i > Struct->getDescription()->NumFields)
swift::crash("Swift mirror subscript bounds check failure");
// Load the offset from its respective vector.
return Struct->getFieldOffsets()[i];
}
const FieldType childMetadata(intptr_t i, const char **outName,
void (**outFreeFunc)(const char *)) {
StringRef name;
FieldType fieldInfo;
std::tie(name, fieldInfo) = getFieldAt(type, i);
assert(!fieldInfo.isIndirect() && "indirect struct fields not implemented");
*outName = name.data();
*outFreeFunc = nullptr;
return fieldInfo;
}
// 可以獲取到屬性名稱和屬性偏移的指針,也就是屬性值鱼蝉。
AnyReturn subscript(intptr_t i, const char **outName,
void (**outFreeFunc)(const char *)) {
auto fieldInfo = childMetadata(i, outName, outFreeFunc);
auto *bytes = reinterpret_cast<char*>(value);
auto fieldOffset = childOffset(i);
auto *fieldData = reinterpret_cast<OpaqueValue *>(bytes + fieldOffset);
return copyFieldContents(fieldData, fieldInfo);
}
};
首先一個(gè)判斷是否支持反射的方法箫荡,最中是訪問的Description->isReflectable()。
從 isReflectable 的實(shí)現(xiàn)來看洁奈,我們可以看出最根本的數(shù)據(jù)結(jié)構(gòu)StructMetadata绞灼。獲取屬性個(gè)數(shù)和偏移值也是同樣的思路。而且都是通過Struct->getDescription()獲取到Description所獲取的印叁,關(guān)于Description有一系列繼承關(guān)系军掂,這邊我就不一一解釋良姆,我們可以從下圖的繼承鏈中幔戏,看出Description存儲(chǔ)著很多信息(基本包括所有我們想要的信息)。
類型名稱痊剖,屬性數(shù)量垒玲,屬性偏移量這些都是比較常用的,我們重點(diǎn)講解Fields叮贩。
Fields是一個(gè)指針,指向一個(gè)FieldDescriptor彪蓬。類似Description捺萌,F(xiàn)ieldDescriptor存儲(chǔ)著屬性的相關(guān)信息。仿寫的結(jié)構(gòu)體如下:
struct FieldDescriptor {
enum FieldDescriptorKind: UInt16 {
case Struct
case Class
case Enum
// Fixed-size multi-payload enums have a special descriptor format that encodes spare bits.
case MultiPayloadEnum
// A Swift opaque protocol. There are no fields, just a record for the type itself.
case kProtocol
// A Swift class-bound protocol.
case ClassProtocol
// An Objective-C protocol, which may be imported or defined in Swift.
case ObjCProtocol
// An Objective-C class, which may be imported or defined in Swift.
// In the former case, field type metadata is not emitted, and must be obtained from the Objective-C runtime.
case ObjCClass
}
var MangledTypeName: RelativeDirectPointer<CChar>//類型命名重整
var Superclass: RelativeDirectPointer<CChar>//父類名
var Kind: FieldDescriptorKind//類型酷誓,看枚舉
var FieldRecordSize: Int16 //這個(gè)值乘上NumFields會(huì)拿到RecordSize
var NumFields: Int32//還是屬性個(gè)數(shù)
//獲取每個(gè)屬性态坦,得到FieldRecord
mutating func getField(index: Int) -> UnsafeMutablePointer<FieldRecord> {
return withUnsafeMutablePointer(to: &self) {
let arrayPtr = UnsafeMutableRawPointer($0.advanced(by: 1)).assumingMemoryBound(to: FieldRecord.self)
return arrayPtr.advanced(by: index)
}
}
}
FieldRecord是每個(gè)屬性的信息,包含屬性名娘扩,屬性的性質(zhì)壮锻。通過Index,從FieldDescriptor獲取到對應(yīng)的屬性信息猜绣。那屬性值哪里獲取呢掰邢?按照我的理解,結(jié)構(gòu)體的元數(shù)據(jù)是存儲(chǔ)在堆上的掰伸,用于創(chuàng)建實(shí)例怀估,實(shí)例的溯源狮鸭。屬性值是不固定的歧蕉,屬于實(shí)例的信息康铭,存儲(chǔ)在棧上的。所以屬性值得另外通過偏移值去獲取催跪。
struct FieldRecord {
struct FieldRecordFlags {
var Data: UInt32
/// Is this an indirect enum case?
func isIndirectCase() -> Bool {
return (Data & 0x1) == 0x1;
}
/// Is this a mutable `var` property?
func isVar() -> Bool {
return (Data & 0x2) == 0x2;
}
}
var Flags: FieldRecordFlags //標(biāo)記位
var MangledTypeName: RelativeDirectPointer<CChar>//類型命名重整
var FieldName: RelativeDirectPointer<CChar>//屬性名
}
具體怎么獲取屬性的值呢?有如下步驟:(具體代碼有空再補(bǔ)上匿沛,這段也是抄的哈哈)
- 首先獲取FieldOffsetVectorOffset的值
- 然后在加上this也就是當(dāng)前Metadata的指針
- 這里我們將仿寫的StructMetadata的指針ptr重綁定為Int
- 源碼中加上FieldOffsetVectorOffset逃呼,這里我們就移動(dòng)FieldOffsetVectorOffset
- 然后將上述移動(dòng)后的綁定為一個(gè)Int32的指針
- 最后使用UnsafeBufferPointer和屬性個(gè)數(shù)創(chuàng)建一個(gè)buffer數(shù)組指針
- 接下來我們就可以從數(shù)組中取出每個(gè)屬性的偏移值
- 然后取出結(jié)構(gòu)體實(shí)例p的內(nèi)存地址
- 然后按照buffer數(shù)組中的偏移值進(jìn)行偏移者娱,重綁定為屬性的類型
- 最后就可以打印出屬性值了
所以,我們可以總結(jié)一下:
Mirror是一個(gè)包裝類推姻,通過傳入的對象類型獲取對應(yīng)的ReflectionMirrorImpl框沟,比如結(jié)構(gòu)體對應(yīng)的Impl類型為StructImpl,StructImpl會(huì)在內(nèi)部創(chuàng)建一個(gè)StructMetadata拧晕。Mirror獲取屬性值都是間接從StructMetadata獲取梅垄,屬性名队丝,屬性類型這一類固定的信息可以直接獲取,屬性值是通過StructMetadata獲取的屬性偏移地址獲取的臭墨。
3. Struct的底層結(jié)構(gòu)
在上面Mirror的解析中膘盖,已經(jīng)對Struct的底層結(jié)構(gòu)有了進(jìn)一步研究。
很明顯,再概括性的解釋下StructMetaData践图,Struct的底層結(jié)構(gòu)就清晰可見了沉馆。
如上一部分Mirror解析所說的,Mirror底層的核心邏輯揖盘,就是通過getKind()方法獲取該類型的元數(shù)據(jù)類型兽狭,然后根據(jù)該類型Metadata獲取相應(yīng)的屬性,比如類型名稱服球、屬性名字颠焦,屬性個(gè)數(shù)等。
每種類型都有對應(yīng)的Metadata粉渠,所以研究Struct或者Class都是通過對應(yīng)的Metadata來研究的霸株。
通過對StructMetadata源碼的解析盯捌,我們可以概括(仿寫)下StructMetadata的結(jié)構(gòu)饺著,這樣便于理解。
struct StructMetadata {
var Kind: InProcess // MetadataKind靴跛,結(jié)構(gòu)體的枚舉值是0x200
var Description: UnsafeMutablePointer<TargetStructDescriptor>// 結(jié)構(gòu)體的描述渡嚣,包含了結(jié)構(gòu)體的所有信息识椰,是一個(gè)指針
//獲得每個(gè)屬性的在結(jié)構(gòu)體中內(nèi)存的起始位置、
mutating func getFieldOffset(index: Int) -> Int {
if Description.pointee.NumFields == 0 {
print("結(jié)構(gòu)體沒有屬性")
return 0
}
let fieldOffsetVectorOffset = self.Description.pointee.FieldOffsetVectorOffset
return withUnsafeMutablePointer(to: &self) {
//獲得自己本身的起始位置
let selfPtr = UnsafeMutableRawPointer($0).assumingMemoryBound(to: InProcess.self)
//以指針的步長偏移FieldOffsetVectorOffset
let fieldOffsetVectorOffsetPtr = selfPtr.advanced(by: numericCast(fieldOffsetVectorOffset))
//屬性的起始偏移量已32位整形存儲(chǔ)的藏畅,轉(zhuǎn)一下指針
let tramsformPtr = UnsafeMutableRawPointer(fieldOffsetVectorOffsetPtr).assumingMemoryBound(to: UInt32.self)
return numericCast(tramsformPtr.advanced(by: index).pointee)
}
}
}
可以看出StructMetadata主要由Kind和Description組成功咒,Kind用來表明自己是一個(gè)Struct的Metadata。Description則隱藏著Struct的各種信息幽七。
那Description的具體結(jié)構(gòu)又有什么溅呢?仿寫的TargetStructDescriptor結(jié)構(gòu)如下:
struct TargetStructDescriptor {
// 存儲(chǔ)在任何上下文描述符的第一個(gè)公共標(biāo)記
var Flags: ContextDescriptorFlags
// 復(fù)用的RelativeDirectPointer這個(gè)類型藕届,其實(shí)并不是,但看下來原理一樣
// 父級上下文梁厉,如果是頂級上下文則為null踏兜。獲得的類型為InProcess,里面存放的應(yīng)該是一個(gè)指針肉盹,測下來結(jié)構(gòu)體里為0疹尾,相當(dāng)于null了
var Parent: RelativeDirectPointer<InProcess>
// 獲取Struct的名稱
var Name: RelativeDirectPointer<CChar>
// 這里的函數(shù)類型是一個(gè)替身纳本,需要調(diào)用getAccessFunction()拿到真正的函數(shù)指針(這里沒有封裝),會(huì)得到一個(gè)MetadataAccessFunction元數(shù)據(jù)訪問函數(shù)的指針的包裝器類吓笙,該函數(shù)提供operator()重載以使用正確的調(diào)用約定來調(diào)用它(可變長參數(shù))巾腕,意外發(fā)現(xiàn)命名重整會(huì)調(diào)用這邊的方法(目前不太了解這塊內(nèi)容)尊搬。
var AccessFunctionPtr: RelativeDirectPointer<UnsafeRawPointer>
// 一個(gè)指向類型的字段描述符的指針(如果有的話)。類型字段的描述幌墓,可以從里面獲取結(jié)構(gòu)體的屬性。
var Fields: RelativeDirectPointer<FieldDescriptor>
// 結(jié)構(gòu)體屬性個(gè)數(shù)
var NumFields: Int32
// 存儲(chǔ)這個(gè)結(jié)構(gòu)的字段偏移向量的偏移量(記錄你屬性起始位置的開始的一個(gè)相對于metadata的偏移量,具體看metadata的getFieldOffsets方法)袭祟,如果為0捞附,說明你沒有屬性
var FieldOffsetVectorOffset: Int32
}
//這個(gè)類型是通過當(dāng)前地址的偏移值獲得真正的地址鸟召,有點(diǎn)像文件目錄,用當(dāng)前路徑的相對路徑獲得絕對路徑压状。
struct RelativeDirectPointer<T> {
var offset: Int32 //存放的與當(dāng)前地址的偏移值
//通過地址的相對偏移值獲得真正的地址
mutating func get() -> UnsafeMutablePointer<T> {
let offset = self.offset
return withUnsafeMutablePointer(to: &self) {
return UnsafeMutableRawPointer($0).advanced(by: numericCast(offset)).assumingMemoryBound(to: T.self)
}
}
}
舉個(gè)例子??:
struct Teacher: Codable {
var name = "Tom"
var age = 23
var city = "ShangHai"
var height = 175
}
// 通過源碼我們可以知道Type類型對應(yīng)的就是Metadata跟继,這里記住要轉(zhuǎn)成Any.Type舔糖,不然typesize不一致,不讓轉(zhuǎn)
let ptr = unsafeBitCast(Teacher.self as Any.Type, to: UnsafeMutablePointer<StructMetadata>.self)
print("0x\(String(ptr.pointee.Kind.PointerSize, radix: 16))") //kind枚舉值是0x200十兢,代表著結(jié)構(gòu)體
let descriptionptr = ptr.pointee.Description
let Flags = descriptionptr.pointee.Flags
print(Flags.getContextDescriptorKind()!) // 公共標(biāo)記中獲取kind為Struct
let ParentPtr = descriptionptr.pointee.Parent.get()
print(ParentPtr.pointee.PointerSize) // 結(jié)果為0旱物,說明已經(jīng)是頂級上下文了
let structName = descriptionptr.pointee.Name.get()
print(String(cString: structName)) // 拿到Teacher字符串
//拿到屬性個(gè)數(shù)跟匆,屬性名字,屬性在內(nèi)存的起始位置烤蜕,這樣就可以取值迹冤,mirror的原理就是這個(gè)E葆恪!
let propertyCount = Int(descriptionptr.pointee.NumFields)
print("屬性個(gè)數(shù):\(propertyCount)")
print("---------")
(0..<propertyCount).forEach {
let propertyPtr = descriptionptr.pointee.Fields.get().pointee.getField(index: $0)
print("""
屬性名:\(String(cString: propertyPtr.pointee.FieldName.get()))
起始位置:\(ptr.pointee.getFieldOffset(index: $0))
類型命名重整:\(String(cString: propertyPtr.pointee.MangledTypeName.get()))
是否是var修飾的變量:\(propertyPtr.pointee.Flags.isVar() ? "是" : "否" )
---------
""")
}
4.Class的底層結(jié)構(gòu)
類似StructMetaData莉兰,Class的底層結(jié)構(gòu)是ClassMetadata糖荒。當(dāng)然不是這么一個(gè)簡單的結(jié)構(gòu),是有層層繼承鏈的(TargetClassMetadata蜘矢,TargetAnyClassMetadata)综看。
不同層有著不同的屬性红碑。所以這邊不打算理清它們的繼承關(guān)系,簡單地總結(jié)下ClassMetadata的相關(guān)屬性镣典。
// Swift中的class的metadata兼容OC的類
struct ClassMetadata {
// 在oc中放的就是isa唾琼,在swift中kind大于0x7FF表示的就是類
var Kind: InProcess
// 父類的Metadata锡溯,如果是null說明是最頂級的類了
var Superclass: UnsafeMutablePointer<ClassMetadata>
// 緩存數(shù)據(jù)用于某些動(dòng)態(tài)查找,它由運(yùn)行時(shí)擁有芜茵,通常需要與Objective-C的使用進(jìn)行互操作倡蝙。(說到底就是OC的東西)
var CacheData1: UnsafeMutablePointer<UnsafeRawPointer>
var CacheData2: UnsafeMutablePointer<UnsafeRawPointer>
// 除了編譯器設(shè)置低位以表明這是Swift元類型外寺鸥,這個(gè)data里存的指針,用于行外元數(shù)據(jù)烤低,通常是不透明的(應(yīng)該也是OC的)
var Data: InProcess
// Swift-specific class flags.
var Flags: ClassFlags
// The address point of instances of this type.
var InstanceAddressPoint: UInt32
// The required size of instances of this type.(實(shí)例對象在堆內(nèi)存的大小)
var InstanceSize: UInt32
// The alignment mask of the address point of instances of this type. (根據(jù)這個(gè)mask來獲取內(nèi)存中的對齊大小)
var InstanceAlignMask: UInt16
// Reserved for runtime use.(預(yù)留給運(yùn)行時(shí)使用)
var Reserved: UInt16
// The total size of the class object, including prefix and suffix extents.
var ClassSize: UInt32
// The offset of the address point within the class object.
var ClassAddressPoint: UInt32
// 一個(gè)對類型的超行的swift特定描述扑馁,如果這是一個(gè)人工子類,則為null复罐。目前不提供動(dòng)態(tài)創(chuàng)建非人工子類的機(jī)制雄家。
var Description: UnsafeMutablePointer<TargetClassDescriptor>
// 銷毀實(shí)例變量的函數(shù)咳短,用于在構(gòu)造函數(shù)早期返回后進(jìn)行清理蛛淋。如果為null褐荷,則不會(huì)執(zhí)行清理操作,并且所有的ivars都必須是簡單的层宫。
var IVarDestroyer: UnsafeMutablePointer<ClassIVarDestroyer>
}
對比StructMeteData其监,ClassMetadata多了很多屬性抖苦。
// Swift中的class的metadata兼容OC的類
struct ClassMetadata {
// 在oc中放的就是isa,在swift中kind大于0x7FF表示的就是類
var Kind: InProcess
// 父類的Metadata贮庞,如果是null說明是最頂級的類了
var Superclass: UnsafeMutablePointer<ClassMetadata>
// 緩存數(shù)據(jù)用于某些動(dòng)態(tài)查找究西,它由運(yùn)行時(shí)擁有卤材,通常需要與Objective-C的使用進(jìn)行互操作。(說到底就是OC的東西)
var CacheData1: UnsafeMutablePointer<UnsafeRawPointer>
var CacheData2: UnsafeMutablePointer<UnsafeRawPointer>
// 除了編譯器設(shè)置低位以表明這是Swift元類型外伏伐,這個(gè)data里存的指針晕拆,用于行外元數(shù)據(jù),通常是不透明的(應(yīng)該也是OC的)
var Data: InProcess
// 該對象是否是有效的swift類型元數(shù)據(jù)? 也就是說堤器,它可以安全地向下轉(zhuǎn)換到類元數(shù)據(jù)(ClassMetadata)嗎?
func isTypeMetadata() -> Bool {
return ((Data & 2) != 0)
}
func isPureObjC() -> Bool {
return !isTypeMetadata()
}
/**
源碼中
上面的字段都是TargetAnyClassMetadata父類的末贾,類元數(shù)據(jù)對象中與所有類兼容的部分拱撵,即使是非swift類。
下面的字段都是TargetClassMetadata的乓旗,只有在isTypeMetadata()時(shí)才有效集索,所以在源碼在使用時(shí)都比較小心务荆,會(huì)經(jīng)常調(diào)用isTypeMetadata()
Objective-C運(yùn)行時(shí)知道下面字段的偏移量
*/
// Swift-specific class flags.
var Flags: ClassFlags
// The address point of instances of this type.
var InstanceAddressPoint: UInt32
// The required size of instances of this type.(實(shí)例對象在堆內(nèi)存的大小)
var InstanceSize: UInt32
// The alignment mask of the address point of instances of this type. (根據(jù)這個(gè)mask來獲取內(nèi)存中的對齊大小)
var InstanceAlignMask: UInt16
// Reserved for runtime use.(預(yù)留給運(yùn)行時(shí)使用)
var Reserved: UInt16
// The total size of the class object, including prefix and suffix extents.
var ClassSize: UInt32
// The offset of the address point within the class object.
var ClassAddressPoint: UInt32
// 一個(gè)對類型的超行的swift特定描述,如果這是一個(gè)人工子類娱据,則為null吸耿。目前不提供動(dòng)態(tài)創(chuàng)建非人工子類的機(jī)制酷窥。
var Description: UnsafeMutablePointer<TargetClassDescriptor>
// 銷毀實(shí)例變量的函數(shù)蓬推,用于在構(gòu)造函數(shù)早期返回后進(jìn)行清理。如果為null糕珊,則不會(huì)執(zhí)行清理操作毅糟,并且所有的ivars都必須是簡單的。
var IVarDestroyer: UnsafeMutablePointer<ClassIVarDestroyer>
//獲得每個(gè)屬性的在結(jié)構(gòu)體中內(nèi)存的起始位置
mutating func getFieldOffset(index: Int) -> Int {
if Description.pointee.NumFields == 0 || Description.pointee.FieldOffsetVectorOffset == 0 {
print("沒有屬性")
return 0
}
let fieldOffsetVectorOffset = self.Description.pointee.FieldOffsetVectorOffset
return withUnsafeMutablePointer(to: &self) {
//獲得自己本身的起始位置
let selfPtr = UnsafeMutableRawPointer($0).assumingMemoryBound(to: InProcess.self)
//以指針的步長偏移FieldOffsetVectorOffset
let fieldOffsetVectorOffsetPtr = selfPtr.advanced(by: numericCast(fieldOffsetVectorOffset))
//屬性的起始偏移量已32位整形存儲(chǔ)的喇肋,轉(zhuǎn)一下指針
let tramsformPtr = UnsafeMutableRawPointer(fieldOffsetVectorOffsetPtr).assumingMemoryBound(to: InProcess.self)
return numericCast(tramsformPtr.advanced(by: index).pointee)
}
}
}
除了Struct有的一些屬性外,多了一些適配OC的屬性甚侣。當(dāng)然存儲(chǔ)的屬性信息也是放在Description间学。Description中的FieldRecord和Struct的一樣低葫,我就不貼出來了。
struct TargetClassDescriptor {
// 存儲(chǔ)在任何上下文描述符的第一個(gè)公共標(biāo)記
var Flags: ContextDescriptorFlags
// 復(fù)用的RelativeDirectPointer這個(gè)類型殷绍,其實(shí)并不是鹊漠,但看下來原理一樣
// 父級上下文躯概,如果是頂級上下文則為null畔师。
var Parent: RelativeDirectPointer<InProcess>
// 獲取類的名稱
var Name: RelativeDirectPointer<CChar>
// 這里的函數(shù)類型是一個(gè)替身看锉,需要調(diào)用getAccessFunction()拿到真正的函數(shù)指針(這里沒有封裝),會(huì)得到一個(gè)MetadataAccessFunction元數(shù)據(jù)訪問函數(shù)的指針的包裝器類呻此,該函數(shù)提供operator()重載以使用正確的調(diào)用約定來調(diào)用它(可變長參數(shù))焚鲜,意外發(fā)現(xiàn)命名重整會(huì)調(diào)用這邊的方法(目前不太了解這塊內(nèi)容)放前。
var AccessFunctionPtr: RelativeDirectPointer<UnsafeRawPointer>
// 一個(gè)指向類型的字段描述符的指針(如果有的話)凭语。類型字段的描述,可以從里面獲取結(jié)構(gòu)體的屬性吨些。
var Fields: RelativeDirectPointer<FieldDescriptor>
// The type of the superclass, expressed as a mangled type name that can refer to the generic arguments of the subclass type.
var SuperclassType: RelativeDirectPointer<CChar>
// 下面兩個(gè)屬性在源碼中是union類型,所以取size大的類型作為屬性(這里貌似一樣)挽拔,具體還得判斷是否have a resilient superclass
// 有resilient superclass螃诅,用ResilientMetadataBounds状囱,表示對保存元數(shù)據(jù)擴(kuò)展的緩存的引用
var ResilientMetadataBounds: RelativeDirectPointer<TargetStoredClassMetadataBounds>
// 沒有resilient superclass使用MetadataNegativeSizeInWords亭枷,表示該類元數(shù)據(jù)對象的負(fù)大小(用字節(jié)表示)
var MetadataNegativeSizeInWords: UInt32 {
get {
return UInt32(ResilientMetadataBounds.offset)
}
}
// 有resilient superclass,用ExtraClassFlags猾编,表示一個(gè)Objective-C彈性類存根的存在
var ExtraClassFlags: ExtraClassDescriptorFlags
// 沒有resilient superclass使用MetadataPositiveSizeInWords升敲,表示該類元數(shù)據(jù)對象的正大小(用字節(jié)表示)
var MetadataPositiveSizeInWords: UInt32 {
get {
return ExtraClassFlags.Bits
}
}
/**
此類添加到類元數(shù)據(jù)的其他成員的數(shù)目驴党。默認(rèn)情況下,這些數(shù)據(jù)對運(yùn)行時(shí)是不透明的倔既,而不是在其他成員中公開;它實(shí)際上只是NumImmediateMembers * sizeof(void*)字節(jié)的數(shù)據(jù)渤涌。
這些字節(jié)是添加在地址點(diǎn)之前還是之后把还,取決于areImmediateMembersNegative()方法笨篷。
*/
var NumImmediateMembers: UInt32
// 屬性個(gè)數(shù),不包含父類的
var NumFields: Int32
// 存儲(chǔ)這個(gè)結(jié)構(gòu)的字段偏移向量的偏移量(記錄你屬性起始位置的開始的一個(gè)相對于metadata的偏移量练俐,具體看metadata的getFieldOffsets方法)冕臭,如果為0,說明你沒有屬性
// 如果這個(gè)類含有一個(gè)彈性的父類归形,那么從他的彈性父類的metaData開始偏移
var FieldOffsetVectorOffset: Int32
}
還有一點(diǎn)需要注意的是鼻由,ClassMetadata是在堆中的數(shù)據(jù)蕉世,類似OC中的元數(shù)據(jù)。如剛開始所比對的奸例,結(jié)構(gòu)體和類實(shí)例在內(nèi)存地址的不同表現(xiàn)向楼。類對象在棧中只是一個(gè)指針湖蜕,數(shù)據(jù)存儲(chǔ)在堆中,而ClassMetadata就是堆中數(shù)據(jù)頭部的指針,所謂的指向類型信息戈鲁。
所以以下我們通過創(chuàng)建對象的源碼來探索對象實(shí)例的本質(zhì)嘹叫。swift_allocObject的源碼如下:
// requiredSize是分配的實(shí)際內(nèi)存大小,為40
// requiredAlignmentMask是swift中的字節(jié)對齊方式罩扇,這個(gè)和OC中是一樣的喂饥,必須是8的倍數(shù),不足的會(huì)自動(dòng)補(bǔ)齊或粮,目的是以空間換時(shí)間捞高,來提高內(nèi)存操作效率袋毙。
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
size_t requiredSize,
size_t requiredAlignmentMask) {
assert(isAlignmentMask(requiredAlignmentMask));
// 通過swift_slowAlloc分配內(nèi)存听盖,并進(jìn)行內(nèi)存字節(jié)對齊
auto object = reinterpret_cast<HeapObject *>(
swift_slowAlloc(requiredSize, requiredAlignmentMask));//分配內(nèi)存+字節(jié)對齊
// NOTE: this relies on the C++17 guaranteed semantics of no null-pointer
// check on the placement new allocator which we have observed on Windows,
// Linux, and macOS.
new (object) HeapObject(metadata);//初始化一個(gè)實(shí)例對象
// If leak tracking is enabled, start tracking this object.
SWIFT_LEAKS_START_TRACKING_OBJECT(object);
SWIFT_RT_TRACK_INVOCATION(object, swift_allocObject);
return object;
}
通過new + HeapObject + metadata初始化一個(gè)實(shí)例對象生闲。函數(shù)的返回值是HeapObject類型,所以當(dāng)前對象的內(nèi)存結(jié)構(gòu)就是HeapObject的內(nèi)存結(jié)構(gòu)悬蔽。
進(jìn)入swift_slowAlloc函數(shù)捉兴,其內(nèi)部主要是通過malloc在堆中分配size大小的內(nèi)存空間倍啥,并返回內(nèi)存地址虽缕,主要是用于存儲(chǔ)實(shí)例變量。
void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
void *p;
// This check also forces "default" alignment to use AlignedAlloc.
if (alignMask <= MALLOC_ALIGN_MASK) {
#if defined(__APPLE__)
p = malloc_zone_malloc(DEFAULT_ZONE(), size);
#else
p = malloc(size);// 堆中創(chuàng)建size大小的內(nèi)存空間伍派,用于存儲(chǔ)實(shí)例變量
#endif
} else {
size_t alignment = (alignMask == ~(size_t(0)))
? _swift_MinAllocationAlignment
: alignMask + 1;
p = AlignedAlloc(size, alignment);
}
if (!p) swift::crash("Could not allocate memory.");
return p;
}
通過查看HeapObject初始化方法诉植,我們可以看到需要兩個(gè)參數(shù):metadata昵观、refCounts。其中metadata類型是HeapMetadata灼擂,是一個(gè)指針類型缤至,占8字節(jié),后面我會(huì)講它怎么和ClassMetadata扯上關(guān)系嫉到。
refCounts(引用計(jì)數(shù)何恶,類型是InlineRefCounts嚼黔,而InlineRefCounts是一個(gè)類RefCounts的別名唬涧,占8個(gè)字節(jié))疫赎,swift采用arc引用計(jì)數(shù)。
所以總結(jié)起來碎节,實(shí)例對象在OC和Swift的區(qū)別是:
- OC中實(shí)例對象的本質(zhì)是結(jié)構(gòu)體捧搞,是以objc_object為模板繼承的,其中有一個(gè)isa指針狮荔,占8字節(jié)胎撇。
- Swift中實(shí)例對象,本質(zhì)也是結(jié)構(gòu)體(HeapObject)殖氏,默認(rèn)的比OC中多了一個(gè)refCounted引用計(jì)數(shù)大小,默認(rèn)屬性占16字節(jié)(metadata 8字節(jié) + refCounts 8字節(jié))雅采。
再接著上面的研究HeapMetadata和ClassMetadata的關(guān)系爵憎。
HeapMetadata中有個(gè)Kind,在getClassObject中去匹配kind返回值是TargetClassMetadata類型婚瓜。如果是Class宝鼓,則直接對this(當(dāng)前指針,即metadata)強(qiáng)轉(zhuǎn)為ClassMetadata闰渔。
const TargetClassMetadata<Runtime> *getClassObject() const;
//******** 具體實(shí)現(xiàn) ********
template<> inline const ClassMetadata *
Metadata::getClassObject() const {
//匹配kind
switch (getKind()) {
//如果kind是class
case MetadataKind::Class: {
// Native Swift class metadata is also the class object.
//將當(dāng)前指針強(qiáng)轉(zhuǎn)為ClassMetadata類型
return static_cast<const ClassMetadata *>(this);
}
case MetadataKind::ObjCClassWrapper: {
// Objective-C class objects are referenced by their Swift metadata wrapper.
auto wrapper = static_cast<const ObjCClassWrapperMetadata *>(this);
return wrapper->Class;
}
// Other kinds of types don't have class objects.
default:
return nullptr;
}
}
參考資料或博客
1.《Swift中進(jìn)階合集》
2.《初探Swift底層Metadata》