1、LLVM优化
在源码中,alloc的类方法实现为_objc_rootAlloc(self),但是运行起来后发现,在_objc_rootAlloc之前,系统会执行一个objc_alloc方法,这是为什么呢?
原来,在LLVM编译阶段,要对SEL和imp进行绑定,在objc-runtime-new.mm中,在_read_images方法中static size_t UnfixedSelectors以前进行了SEL和imp的绑定,对有更改的在static void fixupMessageRef(message_ref_t *msg) 方法中进行了修复重定向,原因是苹果认为一些方法比如alloc很特殊,因为会对内存进行一些操作,所以为了安全,所以alloc之前,在LLVM编译中,用苹果的方法objc_alloc方法进行替换,调用完成后就走到了上图的callAlloc方法中
/************
* fixupMessageRef
* Repairs an old vtable dispatch call site. 修复旧的 vtable 调度调用站点。
* vtable dispatch itself is not supported. 不支持 vtable 调度本身。
************/
2、对象的内存
3、结构体内存对齐
内存对齐原则
- 数据成员对齐规则:
结构体(struct)或联合体(union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或成员的子成员大小(如成员为数组或结构体等)的整数倍开始(如int为4字节,则要从4的整数倍地址开始存储) - 结构体作为成员:如果一个结构体里有某些结构体成员,则
结构体成员要从其内部最大元素的大小的整数倍地址开始存储(如struct A中含有struct B,struct B中有char、int、double等元素,则struct B应该从8的整数倍开始存储) - 结构体的总大小:也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐
4、malloc
首先我们先看一下获取一个类的各种内存大小的几个方法
我们获取了
指针大小、类实例大小和堆空间【实际】分配给对象的内存大小大小,但是malloc_size()是怎么得来的呢?我们运行Malloc源码,简单书写一个Demo,内存分析一下:
再打印一下zone_calloc得到上图中nano_calloc,继续查找
slot_bytes的大小是通过上边
segregated_size_to_fit方法获取,跳入查看:
- 对象内存对齐
因此得知,对象会
16字节对齐到16的倍数
5、运算符
sizeof:是一个运算符,获取的是类型的大小(int、size_t、结构体、指针变量等),这些数值在程序编译时就转成常数,程序运行时是直接获取的class_getInstanceSize:是一个函数(调用时需要开辟额外的内存空间),程序运行时才获取,计算的是类的大小(至少需要的大小)- 创建的对象【至少】需要的内存大小
- 不考虑malloc函数的话,内存对齐一般是以【8】对齐
#import <objc/runtime.h>
malloc_size:堆空间【实际】分配给对象的内存大小- 在Mac、iOS中的malloc函数分配的内存大小总是【16】的倍数
总结
-
alloc底层方法会被hook, 然后被方法重定向
-
结构体对齐:- 数据成员位置要
从其类型长度的整数倍(比如int就是4)开始 - 结构体作为数据成员,则以该
子结构体中最大的类型长度作为该子结构体的类型长度 - 总大小以结构体中最大类型成员数据的整数倍对齐(注意:1、
子结构体以其内部最大类型长度作为其在父结构体中的长度,而非其总大小,即使总大小很大如40,但计算总大小对齐时也是按其最大类型char:1、int:4、NSString:8这些对齐。2、甚至成员数据都是BOOL类型则以1字节对齐)
- 数据成员位置要
-
成员变量为8字节内存对齐 -
OC对象为16字节内存对齐(对象一般都具有成员变量,(NSObject:8 + 成员变量:n) > 8,因此不适合8字节对齐,而且空间更安全) -
segregated_next_block:整个流程大概意思就是不断循环查找能够容纳需要的大小的空间,如果找到直接返回空间地址,如果找不到返回0