背景
iOS类对象、实例对象本质我们应该都很清楚,但是swift类、对象底层又是怎么样的呢?,这篇文章我们一起研究下。
目标
- 明白实例对象的创建过程以及内存布局
- 明白类对象的内部布局以及在oc和swift上的差异
探究
1. 探究对象
初始化过程
示例代码
swift
class ClassAndInstence {
var age: Int = 18
var name: String = "小李"
}
func main() {
//类和对象
let classAndIntence = ClassAndInstence()
}
main()
在创建classAndIntence对象的那一行下一个断点
运行... 可以发现创建ClassAndInstence示例调用了__allocating_init函数
因为__allocating_init在swift源码里是看不到的,所以我们需要再往下看看有什么证据可寻
swift源码编译可以参考我的这篇文章
我们可以在xcode下一个符号断点进入__allocating_init的内部实现
再次运行,__allocating_init内部逻辑如下
- 第一步:执行swift_allocObject创建对象
- 第二步:执行ClassAndInstence的init方法
swift_allocObject方法在swift源码中的定义如下:
字段 | 含义 |
---|---|
metadata | 包名.类名 |
requiredSize | 需要大小 |
requiredAlignmentMask | requiredAlignmentMask |
这里的CALL_IMPL的定义主要是用于分发事件,后续可以看到swift源码里的方法调用基本都是通过它进行的
断点继续走就走到CALL_IMPL派发的swift_allocObject 方法里了
- 第一步:通过swift_slowAlloc分配内存,分配给
c++
// Apple malloc is always 16-byte aligned.
#define MALLOC_ALIGN_MASK 15
// When alignMask == ~(size_t(0)), allocation uses the "default"
// _swift_MinAllocationAlignment. This is different than calling swift_slowAlloc
// with `alignMask == _swift_MinAllocationAlignment - 1` because it forces
// the use of AlignedAlloc. This allows manually allocated to memory to always
// be deallocated with AlignedFree without knowledge of its original allocation
// alignment.
//
// For alignMask > (_minAllocationAlignment-1)
// i.e. alignment == 0 || alignment > _minAllocationAlignment:
// The runtime must use AlignedAlloc, and the standard library must
// deallocate using an alignment that meets the same condition.
//
// For alignMask <= (_minAllocationAlignment-1)
// i.e. 0 < alignment <= _minAllocationAlignment:
// The runtime may use either malloc or AlignedAlloc, and the standard library
// must deallocate using an identical alignment.
//堆中开辟空间(空间大小,内存对齐大小)
void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
void *p;
// This check also forces "default" alignment to use AlignedAlloc.
// 内存对其大小 <= 15 :MAlloc使用默认的内存空间开辟空间,也是16字节对齐
if (alignMask <= MALLOC_ALIGN_MASK) {
#if defined(__APPLE__) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC
p = malloc_zone_malloc(DEFAULT_ZONE(), size);
#else
p = malloc(size);
#endif
} else { // 内存对齐大小 > 15 : MAlloc使用传入的内存大小进行开辟,也是外部传入的字节对齐
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;
}
- 通过判断内存对齐掩码是否大于15 判断不同的开辟内存方式,mac工程传入内存对齐掩码是7,通过以下方式创建内存 如果大于16字节位数,那么使用AlignedAlloc方法
-
第二步:通过HeapObject方法构造一个HeapObject对象,并且绑定到object上
-
第三步:最后返回一个HeapObject类型的对象
到这里我们知道,swift的类示例对象在底层都是创建一个heapObject类型的对象,我们需要深入研究下heapObject...
HeapObject定义如下:
成员 | 类型 | 含义 |
---|---|---|
metadata | HeapMetadata | 指向元类的指针 占8个字节 |
refCounts | InlineRefCounts | 引用计数相关 占8个字节 |
HeapObject带参数构造 | -- | -- |
HeapObject不带参数构造 | -- | -- |
看代码是通过HeapObject构造函数调用refCounts初始化引用计数
refCounts 看如下调用栈是调用到了RefCountBitsT类的构造函数设置 后续研究内存管理会重点介绍这里
总结:
- 对象在底层都是HeapObject类型
- HeapObject包含信息:
- metadata指向类对象信息信息
- refCounts保存引用计数相关
一个对象内存分局
js
class ClassAndInstence {
var age: Int = 18
var name: String = "葛高召"
}
let classAndIntence = ClassAndInstence()
print(MemoryLayout<Int>.stride)
print(MemoryLayout<String>.stride)
print(class_getInstanceSize(ClassAndInstence.self))
- metadata占8个字节
- refCounts占8个字节
- 再加上age的8个字节
- name占16个字节
- 所以总共是40个字节
2. 探究类对象
对象在底层中的结构是HeapObject结构体,其第一个属性为metadata,因此从这个属性出发来查看类的结构
查找HeapMetadata
js
template <typename Target> struct TargetHeapMetadata;
using HeapMetadata = TargetHeapMetadata<InProcess>;
- 上文可知对象结构体HeapObject包含有HeapMetadata结构体,对象通过它来查找对应的类信息
- 点击进入HeapMetadata的定义,发现它是TargetHeapMetaData类型的别名
- 并且接收了一个参数Inprocess
在HeapObject构造时对HeapMetadata进行定义,传入TargetHeapMetaData的参数Inprocess
TargetHeapMetaData
js
template <typename Runtime>
struct TargetHeapMetadata : TargetMetadata<Runtime> {
using HeaderType = TargetHeapMetadataHeader<Runtime>;
TargetHeapMetadata() = default;
constexpr TargetHeapMetadata(MetadataKind kind)
: TargetMetadata<Runtime>(kind) {}
constexpr TargetHeapMetadata(TargetAnyClassMetadataObjCInterop<Runtime> *isa)
: TargetMetadata<Runtime>(isa) {}
};
using HeapMetadata = TargetHeapMetadata<InProcess>;
说明:
- TargetHeapMetaData其本质是一个模板类型,其中定义了一些所需的数据结构
- 这个结构体中没有属性,只有初始化方法
- 初始化方法中传入了一个MetadataKind类型的参数,之后就可以返回TargetMetaData对象
- 同时可以看到这里传入的kind也就是上面的inprocess了
- 最后把传入kind传入基础结构体TargetMetadata的初始化方法
TargetMetaData
- 在TargetMetaData中可以看到有一个Kind属性,这是在构建对象时传入的那个参数
查看MetadataKind
js
/// Kinds of Swift metadata records. Some of these are types, some
/// aren't.
enum class MetadataKind : uint32_t {
#define METADATAKIND(name, value) name = value,
#define ABSTRACTMETADATAKIND(name, start, end) \
name##_Start = start, name##_End = end,
#include "MetadataKind.def" //包含所有类型的定义
/// The largest possible non-isa-pointer metadata kind value.
///
/// This is included in the enumeration to prevent against attempts to
/// exhaustively match metadata kinds. Future Swift runtimes or compilers
/// may introduce new metadata kinds, so for forward compatibility, the
/// runtime must tolerate metadata with unknown kinds.
/// This specific value is not mapped to a valid metadata kind at this time,
/// however.
LastEnumerated = 0x7FF,
};
- 可以看到它是uint32_t类型
- MetadataKind文件定义所有类型
查看MetadataKind
TargetMetaData结构体定义中有一个方法getClassObject,它就可以用来获取类对象
js
template <>
inline const ClassMetadata *Metadata::getClassObject() const {
switch (getKind()) {
case MetadataKind::Class: {//如果kind是class
// Native Swift class metadata is also the class object.
//将当前指针强转为ClassMetadata类型
return static_cast<const ClassMetadata *>(this);
}
#if SWIFT_OBJC_INTEROP
case MetadataKind::ObjCClassWrapper: {
// Objective-C class objects are referenced by their Swift metadata wrapper.
auto wrapper = static_cast<const ObjCClassWrapperMetadata *>(this);
return wrapper->Class;
}
#endif
// Other kinds of types don't have class objects.
default:
return nullptr;
}
}
- 在方法中的核心逻辑是通过kind来判断当前是哪种类型
- 里我们需要的是类类型,因此判断为MetadataKind::Class,就会返回ClassMetadata类型
验证:
命令:
- po metadata->getKind()
- 得到其kind是Class
- po metadata->getClassObject() + x/8g 0x0000000110efdc70
- 这个地址中存储的是元数据信息!
说明:
- 传递进来的Kind发现可以判断为类
- 通过方法调用最后得到的是一个类对象,也就是类
- 通过x/8g查看类信息,里面就是存储的元数据信息
注意:
- TargetMetadata 和 TargetClassMetadata 本质上是一样的
- 因为在内存结构中,可以直接进行指针的转换,所以可以说,我们认为的结构体,其实就是TargetClassMetadata
TargetClassMetadata
js
template <typename Runtime>
struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
...
//swift特有的标志
ClassFlags Flags;
//实力对象内存大小
uint32_t InstanceSize;
//实例对象内存对齐方式
uint16_t InstanceAlignMask;
//运行时保留字段
uint16_t Reserved;
//类的内存大小
uint32_t ClassSize;
//类的内存首地址
uint32_t ClassAddressPoint;
...
}
说明:
- 如果运行时支持Objective-C互操作性,这个类就会继承TargetAnyClassMetadataObjCInterop,否则它继承自TargetAnyClassMetadata。
- 包含了很多属性,这些都属于类结构信息
TargetAnyClassMetadataObjCInterop
js
struct TargetAnyClassMetadataObjCInterop
: public TargetAnyClassMetadata<Runtime> {
constexpr TargetAnyClassMetadataObjCInterop(
TargetAnyClassMetadataObjCInterop<Runtime> *isa, //isa信息
TargetClassMetadataObjCInterop *superclass) //superclass信息
: TargetAnyClassMetadata<Runtime>(isa, superclass),
CacheData{nullptr, nullptr}, //cache信息
Data(SWIFT_CLASS_IS_SWIFT_MASK) {} //data信息
...
}
- 如果支持oc则TargetClassMetadata继承TargetAnyClassMetadataObjCInterop
- 类中也会存在isa、superclass、cache、data,和OC的底层类结构完全一样
而TargetClassMetadata继承TargetAnyClassMetadata再看下TargetAnyClassMetadata
TargetAnyClassMetadata
js
/// The portion of a class metadata object that is compatible with
/// all classes, even non-Swift ones.
template <typename Runtime>
struct TargetAnyClassMetadata : public TargetHeapMetadata<Runtime> {
using StoredPointer = typename Runtime::StoredPointer;
using StoredSize = typename Runtime::StoredSize;
using TargetClassMetadata = TargetClassMetadataType<Runtime>;
protected:
constexpr TargetAnyClassMetadata(
//isa信息
TargetAnyClassMetadataObjCInterop<Runtime> *isa,
//superclass信息
TargetClassMetadata *superclass)
: TargetHeapMetadata<Runtime>(isa), Superclass(superclass) {}
public:
constexpr TargetAnyClassMetadata(TargetClassMetadata *superclass)
: TargetHeapMetadata<Runtime>(MetadataKind::Class),
Superclass(superclass) {}
// Note that ObjC classes do not have a metadata header.
/// The metadata for the superclass. This is null for the root class.
TargetSignedPointer<Runtime, const TargetClassMetadata *
__ptrauth_swift_objc_superclass>
Superclass;
/// Is this object a valid swift type metadata? That is, can it be
/// safely downcast to ClassMetadata?
bool isTypeMetadata() const {
return true;
}
/// A different perspective on the same bit.
bool isPureObjC() const {
return !isTypeMetadata();
}
};
- TargetAnyClassMetadata是所有的类结构,不单单是给Swift用的
- 继承自TargetHeapMetadata,这也证明类本身也是对象
总结:
- 类对象在底层都是属于TargetAnyClassMetadata
- oc时底层最基础类型是TargetAnyClassMetadataObjCInterop,用有和oc一样的成员
- swift时底层最基础类型是TargetAnyClassMetadata
到这里就完毕,感谢您的阅读,欢迎阅读我的其他文章