背景
iOS类方针、实例方针实质咱们应该都很清楚,但是swift类、方针底层又是怎么样的呢?,这篇文章咱们一同研讨下。
方针
- 理解实例方针的创立过程以及内存布局
- 理解类方针的内部布局以及在oc和swift上的差异
探求
1. 探求方针
初始化过程
示例代码
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分配内存,分配给
// 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保存引用计数相关
一个方针内存分局
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
template <typename Target> struct TargetHeapMetadata;
using HeapMetadata = TargetHeapMetadata<InProcess>;
- 上文可知方针结构体HeapObject包括有HeapMetadata结构体,方针经过它来查找对应的类信息
- 点击进入HeapMetadata的界说,发现它是TargetHeapMetaData类型的别号
- 而且接收了一个参数Inprocess
在HeapObject结构时对HeapMetadata进行界说,传入TargetHeapMetaData的参数Inprocess
TargetHeapMetaData
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
/// 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,它就能够用来获取类方针
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
template<typenameRuntime>
structTargetClassMetadata:publicTargetAnyClassMetadata<Runtime>{
...
//swift特有的标志
ClassFlagsFlags;
//实力方针内存大小
uint32_tInstanceSize;
//实例方针内存对齐方式
uint16_tInstanceAlignMask;
//运转时保留字段
uint16_tReserved;
//类的内存大小
uint32_tClassSize;
//类的内存首地址
uint32_tClassAddressPoint;
...
}
阐明:
- 假如运转时支持Objective-C互操作性,这个类就会承继TargetAnyClassMetadataObjCInterop,不然它承继自TargetAnyClassMetadata。
- 包括了许多特点,这些都归于类结构信息
TargetAnyClassMetadataObjCInterop
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
/// 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
到这儿就完毕,感谢您的阅读,欢迎阅读我的其他文章