KM的博客.

Swift内存窥探之Class

字数统计: 3.3k阅读时长: 18 min
2022/04/15

类的内存结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct ClassMetadata {
uintptr_t Kind;
struct ClassMetadata *super;
void *data[2];
uintptr_t data2;
uint32_t InstanceAddressPoint;
uint32_t InstanceSize;
uint16_t InstanceAlignMask;
uint16_t Reserved;
uint32_t ClassSize;
uint32_t ClassAddressPoint;
NominalTypeDescriptor *Descriptor;
} ClassMetadata;

typedef struct NominalTypeDescriptor {
unsigned long kind;
const char *name;
unsigned long numFields;
unsigned long fieldOffsetVectorOffset;
const char *fieldNames;
} NominalTypeDescriptor;

首先看一个问题

Box类实际分配内存和实际占用内存分别是多少? 48字节 33 字节

Class 中有 Int、Bool类型

1
2
3
4
5
6
7
8
9
10
11
12
class Size {
// metadata 8 类型信息
// RefCounts 8 引用计数
var width = 1 // int 8
var height = 2 // int 8
var isBig = false // bool 1
}
// 变量 size 接收类Size
var size = Size()
print(class_getInstanceSize(type(of: size))) // 32 + 8 = 40
print(class_getInstanceSize(Size.self)) // 32 + 8 = 40
print(Mems.size(ofRef: size)) // size实际分配:32 + 16 = 48

Class 中有String、Int 类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Man {
// metadata 8 类型信息
// RefCounts 8 引用计数
var name = "Jack" // 16
var year = 18 // 8
}
var m = Man()

print(MemoryLayout<Man>.alignment) // 8
print(MemoryLayout<Man>.size) //8
print(MemoryLayout<Man>.stride) // 8
print(class_getInstanceSize(Man.self)) // 32 + 8对齐 = 40
print(class_getInstanceSize(type(of: m))) // 32 + 8对齐 = 40
print(Mems.size(ofVal: &m)) // 8
print(Mems.size(ofRef: m)) // 40

class Woman {
// metadata 8 类型信息
// RefCounts 8 引用计数
var name = "Rose" // 16
var year = 20 // 8
var isMan = false // 1
}
var woman = Woman()

print(MemoryLayout<Woman>.alignment) // 8
print(MemoryLayout<Woman>.size) //8
print(MemoryLayout<Woman>.stride) // 8
print(class_getInstanceSize(Woman.self)) // 32 + 16对齐 = 48
print(class_getInstanceSize(type(of: woman))) // 32 + 16对齐 = 48
print(Mems.size(ofVal: &woman)) // 8
print(Mems.size(ofRef: woman)) // 48

问题:Swift中最小的class内存对齐是8还是16?

分情况

  • 对于Man不需要分配内存8对齐
  • iOS/macOS中malloc函数分配内存大小总是16的倍数,所以对于woman需要分配内存16对齐

malloc内存对齐原则

原则一:所有Apple平台的 malloc函数总是 16 字节对齐。

原则 二:Linux和 Windows平台在 64 位系统上是 16 字节对齐,在 32位系统上是 8 字节对齐。

Class初始化过程分析

将 Box转成汇编sil, swiftc -emit-sil main.swift > main.sil

分析sil文件, 可以看到如下代码, 是初始化方法 __allocating_init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// box
sil_global hidden @$s4main3boxAA3BoxCvp : $Box

// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
alloc_global @$s4main3boxAA3BoxCvp // id: %2
%3 = global_addr @$s4main3boxAA3BoxCvp : $*Box // users: %7, %14
%4 = metatype $@thick Box.Type // user: %6
// function_ref Box.__allocating_init()
%5 = function_ref @$s4main3BoxCACycfC : $@convention(method) (@thick Box.Type) -> @owned Box // user: %6
%6 = apply %5(%4) : $@convention(method) (@thick Box.Type) -> @owned Box // user: %7
store %6 to %3 : $*Box // id: %7
%8 = integer_literal $Builtin.Word, 1 // user: %10
// function_ref _allocateUninitializedArray<A>(_:)
%9 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %10
%10 = apply %9<Any>(%8) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // users: %12, %11

接下来在Xcode打上符号断点 __allocating_init,调用的是 swift_allocObject 这个方法, 而如果 Human继承自NSObject, 会调用objc的 objc_allocWithZone 方法, 走OC的初始化流程.

分析Swift源码, 搜索 swift_allocObject, 定位到 HeapObject.cpp 文件,发现函数swift_allocObject内部调用 swift_slowAlloc,

swift_slowAlloc内部调用的malloc或者AlignedAlloc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
namespace swift {

// FIXME: Use C11 aligned_alloc if available.
inline void *AlignedAlloc(size_t size, size_t align) {
// posix_memalign only accepts alignments greater than sizeof(void*).
//
if (align < sizeof(void*))
align = sizeof(void*);

#if defined(_WIN32)
void *r = _aligned_malloc(size, align);
assert(r && "_aligned_malloc failed");
#else
void *r = nullptr;
int res = posix_memalign(&r, align, size);
assert(res == 0 && "posix_memalign failed");
(void)res; // Silence the unused variable warning.
#endif
return r;
}

inline void AlignedFree(void *p) {
#if defined(_WIN32)
_aligned_free(p);
#else
free(p);
#endif
}

} // end namespace swift

综上:我们通过汇编和源代码分析得知道 Swift 对象初始化过程如下:

__allocating_init -> swift_allocObject -> swift_allocObject -> swift_slowAlloc -> Malloc

Class的内存结构

通过上面的源码, 发现__allocating_init初始化方法返回的是一个 HeapObject类型的指针, 所以Swift对象的内存结构就是 HeapObject, 它有2个属性 metadatarefCounts, 它的定义如下:

1
2
3
4
5
6
7
struct HeapObject {
/// This is always a valid pointer to a metadata object.
HeapMetadata const *__ptrauth_objc_isa_pointer metadata;
//64位的位域信息, 8字节, 引用计数;
// metadata 和 refCounts 一起构成默认16字节实例对象的内存大小
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
};

metadata是一个HeapMetadata类型, 本质上是 TargetHeapMetadata, 我们可以在源码中找到这个定义

1
2
3
4
5
6
7
8
9
10
11
template <typename Runtime>
struct TargetHeapMetadata : TargetMetadata<Runtime> {
using HeaderType = TargetHeapMetadataHeader<Runtime>;

TargetHeapMetadata() = default;
constexpr TargetHeapMetadata(MetadataKind kind) // 纯 Swift
: TargetMetadata<Runtime>(kind) {} // 传入 kind
constexpr TargetHeapMetadata(TargetAnyClassMetadataObjCInterop<Runtime> *isa) // Objc交互
: TargetMetadata<Runtime>(isa) {} // 传入isa
};
using HeapMetadata = TargetHeapMetadata<InProcess>;

再继续点击跳转分析 TargetHeapMetadata 的父类 TargetMetadata

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
struct TargetMetadata {
using StoredPointer = typename Runtime::StoredPointer;

/// The basic header type.
typedef TargetTypeMetadataHeader<Runtime> HeaderType;

constexpr TargetMetadata()
: Kind(static_cast<StoredPointer>(MetadataKind::Class)) {}
constexpr TargetMetadata(MetadataKind Kind)
: Kind(static_cast<StoredPointer>(Kind)) {}

#if SWIFT_OBJC_INTEROP
protected:
constexpr TargetMetadata(TargetAnyClassMetadataObjCInterop<Runtime> *isa)
: Kind(reinterpret_cast<StoredPointer>(isa)) {}
#endif

private:
/// The kind. Only valid for non-class metadata; getKind() must be used to get
/// the kind value.
StoredPointer Kind;
public:
/// Get the metadata kind.
MetadataKind getKind() const {
return getEnumeratedMetadataKind(Kind);
}

/// Set the metadata kind.
void setKind(MetadataKind kind) {
Kind = static_cast<StoredPointer>(kind);
}

protected:
const TargetAnyClassMetadata<Runtime> *getClassISA() const {
return reinterpret_cast<const TargetAnyClassMetadata<Runtime> *>(Kind);
}
void setClassISA(const TargetAnyClassMetadata<Runtime> *isa) {
Kind = reinterpret_cast<StoredPointer>(isa);
}

public:
/// Is this a class object--the metadata record for a Swift class (which also
/// serves as the class object), or the class object for an ObjC class (which
/// is not metadata)?
bool isClassObject() const {
return static_cast<MetadataKind>(getKind()) == MetadataKind::Class;
}

/// Does the given metadata kind represent metadata for some kind of class?
static bool isAnyKindOfClass(MetadataKind k) {
switch (k) {
case MetadataKind::Class:
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass:
return true;

default:
return false;
}
}

/// Is this metadata for an existential type?
bool isAnyExistentialType() const {
switch (getKind()) {
case MetadataKind::ExistentialMetatype:
case MetadataKind::Existential:
return true;

default:
return false;
}
}

/// Is this either type metadata or a class object for any kind of class?
bool isAnyClass() const {
return isAnyKindOfClass(getKind());
}

const ValueWitnessTable *getValueWitnesses() const {
return asFullMetadata(this)->ValueWitnesses;
}

const TypeLayout *getTypeLayout() const {
return getValueWitnesses()->getTypeLayout();
}

void setValueWitnesses(const ValueWitnessTable *table) {
asFullMetadata(this)->ValueWitnesses = table;
}

// Define forwarders for value witnesses. These invoke this metadata's value
// witness table with itself as the 'self' parameter.
#define WANT_ONLY_REQUIRED_VALUE_WITNESSES
#define FUNCTION_VALUE_WITNESS(WITNESS, UPPER, RET_TYPE, PARAM_TYPES) \
template<typename...A> \
_ResultOf<ValueWitnessTypes::WITNESS ## Unsigned>::type \
vw_##WITNESS(A &&...args) const { \
return getValueWitnesses()->WITNESS(std::forward<A>(args)..., this); \
}
#define DATA_VALUE_WITNESS(LOWER, UPPER, TYPE)
#include "swift/ABI/ValueWitness.def"

unsigned vw_getEnumTag(const OpaqueValue *value) const {
return getValueWitnesses()->_asEVWT()->getEnumTag(const_cast<OpaqueValue*>(value), this);
}
void vw_destructiveProjectEnumData(OpaqueValue *value) const {
getValueWitnesses()->_asEVWT()->destructiveProjectEnumData(value, this);
}
void vw_destructiveInjectEnumTag(OpaqueValue *value, unsigned tag) const {
getValueWitnesses()->_asEVWT()->destructiveInjectEnumTag(value, tag, this);
}

size_t vw_size() const {
return getValueWitnesses()->getSize();
}

size_t vw_alignment() const {
return getValueWitnesses()->getAlignment();
}

size_t vw_stride() const {
return getValueWitnesses()->getStride();
}

unsigned vw_getNumExtraInhabitants() const {
return getValueWitnesses()->getNumExtraInhabitants();
}

/// Allocate an out-of-line buffer if values of this type don't fit in the
/// ValueBuffer.
/// NOTE: This is not a box for copy-on-write existentials.
OpaqueValue *allocateBufferIn(ValueBuffer *buffer) const;

/// Get the address of the memory previously allocated in the ValueBuffer.
/// NOTE: This is not a box for copy-on-write existentials.
OpaqueValue *projectBufferFrom(ValueBuffer *buffer) const;

/// Deallocate an out-of-line buffer stored in 'buffer' if values of this type
/// are not stored inline in the ValueBuffer.
void deallocateBufferIn(ValueBuffer *buffer) const;

// Allocate an out-of-line buffer box (reference counted) if values of this
// type don't fit in the ValueBuffer.
// NOTE: This *is* a box for copy-on-write existentials.
OpaqueValue *allocateBoxForExistentialIn(ValueBuffer *Buffer) const;

// Deallocate an out-of-line buffer box if one is present.
void deallocateBoxForExistentialIn(ValueBuffer *Buffer) const;

/// Get the nominal type descriptor if this metadata describes a nominal type,
/// or return null if it does not.
ConstTargetMetadataPointer<Runtime, TargetTypeContextDescriptor>
getTypeContextDescriptor() const {
switch (getKind()) {
case MetadataKind::Class: {
const auto cls =
static_cast<const TargetClassMetadataType<Runtime> *>(this);
if (!cls->isTypeMetadata())
return nullptr;
if (cls->isArtificialSubclass())
return nullptr;
return cls->getDescription();
}
case MetadataKind::Struct:
case MetadataKind::Enum:
case MetadataKind::Optional:
return static_cast<const TargetValueMetadata<Runtime> *>(this)
->Description;
case MetadataKind::ForeignClass:
return static_cast<const TargetForeignClassMetadata<Runtime> *>(this)
->Description;
default:
return nullptr;
}
}

TargetMetadata 就是最终的基类, 其中有个 Kind 的成员变量, 不同的 kind 有不同的固定值:

TargetMetadata 中根据 kind 种类强转成其它类型, 所以 这个 TargetMetadata 就是所有元类类型的最终基类.

在强转成类的时候, 强转类型是 TargetClassMetadata, TargetClassMetadata是所有类的元类的基类, 点击跳转然后分析它的继承连如下:

TargetClassMetadata : TargetAnyClassMetadata : TargetHeapMetadata : TargetMetadata

通过分析得到关系图:

所以综合继承链上的成员变量, 可以得出类的内存结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct ClassMetadata {
uintptr_t Kind;
struct ClassMetadata *super;
void *data[2];
uintptr_t data2;
uint32_t InstanceAddressPoint;
uint32_t InstanceSize;
uint16_t InstanceAlignMask;
uint16_t Reserved;
uint32_t ClassSize;
uint32_t ClassAddressPoint;
NominalTypeDescriptor *Descriptor;
} ClassMetadata;

typedef struct NominalTypeDescriptor {
unsigned long kind;
const char *name;
unsigned long numFields;
unsigned long fieldOffsetVectorOffset;
const char *fieldNames;
} NominalTypeDescriptor;

类的描述

根据上面的分析,的结构 TargetClassMetadata 有个属性 Description

1
ConstTargetMetadataPointer<Runtime, TargetClassDescriptor> Description;

这个 TargetClassDescriptor 是 Swift 类的描述 ,它有个别名 ClassDescriptor

1
using ClassDescriptor = TargetClassDescriptor<InProcess>;

根据 ClassDescriptor 全局搜索源码, 可以定位到GenMeta.cpp中一个 类 ClassContextDescriptorBuilder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
class ClassContextDescriptorBuilder
: public TypeContextDescriptorBuilderBase<ClassContextDescriptorBuilder,
ClassDecl>,
public SILVTableVisitor<ClassContextDescriptorBuilder>
{
using super = TypeContextDescriptorBuilderBase;

ClassDecl *getType() {
return cast<ClassDecl>(Type);
}

// Non-null unless the type is foreign.
ClassMetadataLayout *MetadataLayout = nullptr;

Optional<TypeEntityReference> ResilientSuperClassRef;

SILVTable *VTable; // VTable函数表
bool Resilient;
bool HasNonoverriddenMethods = false;

SmallVector<SILDeclRef, 8> VTableEntries;
SmallVector<std::pair<SILDeclRef, SILDeclRef>, 8> OverrideTableEntries;
SmallVector<std::pair<Size, SILDeclRef>, 8> VTableEntriesForVFE;

// ...

void addMethod(SILDeclRef fn) {
if (!VTable || methodRequiresReifiedVTableEntry(IGM, VTable, fn)) {
VTableEntries.push_back(fn);
} else {
// Emit a stub method descriptor and lookup function for nonoverridden
// methods so that resilient code sequences can still use them.
emitNonoverriddenMethod(fn);
}
}

void addMethodOverride(SILDeclRef baseRef, SILDeclRef declRef) {
OverrideTableEntries.emplace_back(baseRef, declRef);
}
// 内存布局赋值
void layout() {
assert(!getType()->isForeignReferenceType());
super::layout();
addVTable(); // 添加函数表
addOverrideTable();
addObjCResilientClassStubInfo();
maybeAddCanonicalMetadataPrespecializations();
}
// ...

//
void addVTable() {
LLVM_DEBUG(
llvm::dbgs() << "VTable entries for " << getType()->getName() << ":\n";
for (auto entry : VTableEntries) {
llvm::dbgs() << " ";
entry.print(llvm::dbgs());
llvm::dbgs() << '\n';
}
);

// Only emit a method lookup function if the class is resilient
// and has a non-empty vtable, as well as no elided methods.
if (IGM.hasResilientMetadata(getType(), ResilienceExpansion::Minimal)
&& (HasNonoverriddenMethods || !VTableEntries.empty()))
IGM.emitMethodLookupFunction(getType());

if (VTableEntries.empty())
return;

auto offset = MetadataLayout->hasResilientSuperclass()
? MetadataLayout->getRelativeVTableOffset()
: MetadataLayout->getStaticVTableOffset();
B.addInt32(offset / IGM.getPointerSize());
B.addInt32(VTableEntries.size());

for (auto fn : VTableEntries)
emitMethodDescriptor(fn);
}

void addVTableTypeMetadata(llvm::GlobalVariable *var) {
if (!IGM.getOptions().VirtualFunctionElimination)
return;
assert(VTable && "no vtable?!");

IGM.addVTableTypeMetadata(getType(), var, VTableEntriesForVFE);
}

void emitNonoverriddenMethod(SILDeclRef fn) {
// TODO: Derivative functions do not distinguish themselves in the mangled
// names of method descriptor symbols yet, causing symbol name collisions.
if (fn.getDerivativeFunctionIdentifier())
return;

HasNonoverriddenMethods = true;
// Although this method is non-overridden and therefore left out of the
// vtable, we still need to maintain the ABI of a potentially-overridden
// method for external clients.

// Emit method dispatch thunk.
if (hasPublicVisibility(fn.getLinkage(NotForDefinition)) ||
IGM.getOptions().VirtualFunctionElimination) {
IGM.emitDispatchThunk(fn);
}

if (IGM.getOptions().VirtualFunctionElimination) {
auto offset = B.getNextOffsetFromGlobal() +
// 1st field of MethodDescriptorStructTy
Size(IGM.DataLayout.getTypeAllocSize(IGM.Int32Ty));
VTableEntriesForVFE.push_back(std::pair<Size, SILDeclRef>(offset, fn));
}

// Emit a freestanding method descriptor structure. This doesn't have to
// exist in the table in the class's context descriptor since it isn't
// in the vtable, but external clients need to be able to link against the
// symbol.
IGM.emitNonoverriddenMethodDescriptor(VTable, fn);
}

void addOverrideTable() {
LLVM_DEBUG(
llvm::dbgs() << "Override Table entries for " << getType()->getName() << ":\n";
for (auto entry : OverrideTableEntries) {
llvm::dbgs() << " ";
entry.first.print(llvm::dbgs());
llvm::dbgs() << " -> ";
entry.second.print(llvm::dbgs());
llvm::dbgs() << '\n';
}
);

if (OverrideTableEntries.empty())
return;

B.addInt32(OverrideTableEntries.size());

for (auto pair : OverrideTableEntries)
emitMethodOverrideDescriptor(pair.first, pair.second);
}

void emitMethodOverrideDescriptor(SILDeclRef baseRef, SILDeclRef declRef) {
if (IGM.getOptions().VirtualFunctionElimination) {
auto offset =
B.getNextOffsetFromGlobal() +
// 1st field of MethodOverrideDescriptorStructTy
Size(IGM.DataLayout.getTypeAllocSize(IGM.RelativeAddressTy)) +
// 2nd field of MethodOverrideDescriptorStructTy
Size(IGM.DataLayout.getTypeAllocSize(IGM.RelativeAddressTy));
VTableEntriesForVFE.push_back(
std::pair<Size, SILDeclRef>(offset, baseRef));
}

auto descriptor = B.beginStruct(IGM.MethodOverrideDescriptorStructTy);

// The class containing the base method.
auto *baseClass = cast<ClassDecl>(baseRef.getDecl()->getDeclContext());
IGM.IRGen.noteUseOfTypeContextDescriptor(baseClass, DontRequireMetadata);
auto baseClassEntity = LinkEntity::forNominalTypeDescriptor(baseClass);
auto baseClassDescriptor =
IGM.getAddrOfLLVMVariableOrGOTEquivalent(baseClassEntity);
descriptor.addRelativeAddress(baseClassDescriptor);

// The base method.
auto baseMethodEntity = LinkEntity::forMethodDescriptor(baseRef);
auto baseMethodDescriptor =
IGM.getAddrOfLLVMVariableOrGOTEquivalent(baseMethodEntity);
descriptor.addRelativeAddress(baseMethodDescriptor);

// The implementation of the override.
if (auto entry = VTable->getEntry(IGM.getSILModule(), baseRef)) {
assert(entry->getKind() == SILVTable::Entry::Kind::Override);

auto *impl = entry->getImplementation();
if (impl->isAsync()) {
llvm::Constant *implFn = IGM.getAddrOfAsyncFunctionPointer(impl);
descriptor.addRelativeAddress(implFn);
} else {
llvm::Function *implFn = IGM.getAddrOfSILFunction(impl, NotForDefinition);
descriptor.addCompactFunctionReference(implFn);
}
} else {
// The method is removed by dead method elimination.
// It should be never called. We add a pointer to an error function.
descriptor.addRelativeAddressOrNull(nullptr);
}

descriptor.finishAndAddTo(B);
}

void addPlaceholder(MissingMemberDecl *MMD) {
llvm_unreachable("cannot generate metadata with placeholders in it");
}

void addLayoutInfo() {

// TargetRelativeDirectPointer<Runtime, const char> SuperclassType;
if (auto superclassType = getSuperclassForMetadata(IGM, getType())) {
GenericSignature genericSig = getType()->getGenericSignature();
B.addRelativeAddress(IGM.getTypeRef(superclassType, genericSig,
MangledTypeRefRole::Metadata)
.first);
} else {
B.addInt32(0);
}

// union {
// uint32_t MetadataNegativeSizeInWords;
// RelativeDirectPointer<StoredClassMetadataBounds>
// ResilientMetadataBounds;
// };
if (!MetadataLayout) {
// FIXME: do something meaningful for foreign classes?
B.addInt32(0);
} else if (!MetadataLayout->hasResilientSuperclass()) {
B.addInt32(MetadataLayout->getSize().AddressPoint
/ IGM.getPointerSize());
} else {
B.addRelativeAddress(
IGM.getAddrOfClassMetadataBounds(getType(), NotForDefinition));
}

// union {
// uint32_t MetadataPositiveSizeInWords;
// ExtraClassContextFlags ExtraClassFlags;
// };
if (!MetadataLayout) {
// FIXME: do something meaningful for foreign classes?
B.addInt32(0);
} else if (!MetadataLayout->hasResilientSuperclass()) {
B.addInt32(MetadataLayout->getSize().getOffsetToEnd()
/ IGM.getPointerSize());
} else {
ExtraClassDescriptorFlags flags;
if (IGM.hasObjCResilientClassStub(getType()))
flags.setObjCResilientClassStub(true);
B.addInt32(flags.getOpaqueValue());
}

// uint32_t NumImmediateMembers;
auto numImmediateMembers =
(MetadataLayout ? MetadataLayout->getNumImmediateMembers() : 0);
B.addInt32(numImmediateMembers);

// uint32_t NumFields;
B.addInt32(getNumFields(getType()));

// uint32_t FieldOffsetVectorOffset;
B.addInt32(getFieldVectorOffset() / IGM.getPointerSize());
}

void addObjCResilientClassStubInfo() {
if (IGM.getClassMetadataStrategy(getType()) !=
ClassMetadataStrategy::Resilient)
return;

if (!IGM.hasObjCResilientClassStub(getType()))
return;

B.addRelativeAddress(
IGM.getAddrOfObjCResilientClassStub(
getType(), NotForDefinition,
TypeMetadataAddress::AddressPoint));
}

void addCanonicalMetadataPrespecializations() {
super::addCanonicalMetadataPrespecializations();
auto specializations = IGM.IRGen.metadataPrespecializationsForType(Type);
for (auto pair : specializations) {
if (pair.second != TypeMetadataCanonicality::Canonical) {
continue;
}
auto specialization = pair.first;
auto *function = IGM.getAddrOfCanonicalSpecializedGenericTypeMetadataAccessFunction(specialization, NotForDefinition);
B.addCompactFunctionReference(function);
}
}
};

在进行内存布局的赋值操作时, 会调用父类的方法,然后就去调用 addVTable() 方法添加vtable。 再结合继承连,可以分析出 TargetClassDescriptor 的内存结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct TargetClassDescriptor { 
var flags: UInt32
var parent: UInt32
var name: Int32 // 类/结构体/枚举 的名称
var accessFunctionPointer: Int32
var fieldDescriptor: FieldDescriptor // 属性的描述,属性信息存在这里
var superClassType: Int32
var metadataNegativeSizeInWords: UInt32
var metadataPositiveSizeInWords: UInt32
var numImmediateMembers: UInt32
var numFields: UInt32
var fieldOffsetVectorOffset: UInt32
var Offset: UInt32 // 偏移量
var size: UInt32 // V-Table的size大小
var vtable: Array // 数组V-Table, 函数表
}

方法的描述

函数表 vtable 中存储着的是方法描述 TargetMethodDescriptor

1
2
3
4
5
6
7
8
9
10
struct TargetMethodDescriptor {
// 4字节, 标识方法的种类, 初始化/getter/setter等等
MethodDescriptorFlags Flags;

/// Impl方法实现.
union {
TargetCompactFunctionPointer<Runtime, void> Impl;
TargetRelativeDirectPointer<Runtime, void> AsyncImpl;
};
};

TargetMethodDescriptor 是对方法的描述;

Flags 表示方法的种类,占据 4 个字节;

Impl 里面并不是真正的方法imp,而是一个相对偏移量;

VTableSize

Class Metadata的用途?

  • 我理解 Class Metadata 解决的是 Swift 编译的 Class (其中的设计包含兼容 OC 交互)
  • **Objc Class Wrapper 解决的是直接与非 Swift 编译的 Objc(Native)类的交互 **

参考

Swift 源码

Swift 源码Heap.cpp

CATALOG
  1. 1. 首先看一个问题
    1. 1.1. Class 中有 Int、Bool类型
    2. 1.2. Class 中有String、Int 类型
  2. 2. malloc内存对齐原则
  3. 3. Class初始化过程分析
  4. 4. Class的内存结构
  5. 5. 类的描述
  6. 6. 方法的描述
  7. 7. VTableSize
    1. 7.1. Class Metadata的用途?
  8. 8. 参考