OC对象底层原理之alloc分析

228 阅读4分钟

前言


今天探索的是对象的底层实现,创建对象就要使用到alloc,那么alloc在底层做了什么造就了一个对象

我们先来看一个小例子:

image.png

这里三个打印分别是:对象本身, 对象内存 , 指向对象的指针的地址,我们知道&p1 &p2 &p3 是在栈空间上,且栈是连续的(因为指针占用8字节,所以它们之间相差8字节) ,它们都指向了同一个内存地址。

如下图所示:

image.png

接下来就一起探索下,alloc是如何开辟内存空间的?init又做了什么,p2 p3对象的内存地址和p1 是一样的?

准备工作

  1. 由于我们平常使用的环境是没有办法探索到底层的实现的,所以这里我们需要下载一份objc4-818可编译源码的源码。

源码编译配置:

①. 选择你所创建的 targets

②. enable hardened runtime -> NO

③. build phase -> denpendenice -> objc

④. team 选择 None

  1. 掌握断点有助于底层探索,这里奉上连接

探索:

通过断点的方式探索到了alloc底层使用libobjc.A.dylib库的objc_alloc方法,如下图: image.png

Step into instruction(hold Control) 按住Control 进入用户说明

image.png

image.png

打开libobjc.A.dylib库,接下来分析alloc源码,首先通过汇编跟流程来到了objc_alloc方法,打下断点(再次强调,这里的断点并不是一直Enable,只有当断点断在外部我们研究的对象身上时,再把内部断点Enable,保证我们研究方向不会跑偏。)

image.png

callAlloc出现了分支,走的是objc_msgSend(DXJPerson, @selector(alloc)) 等同于[DXJPerson alloc](为什么会有这一步呢?) image.png

紧接着内部调用alloc image.png

这里我们发现又调回了callAlloc image.png

这次走的是_objc_rootAllocWithZone(cls,nil) (这里留个问题:objc_msgSend(DXJPerson, @selector(alloc)) 内部必然做了什么影响到了 cls->ISA()->hanCustomAWZ()的值) image.png

来到_objc_rootAllocWithZone image.png

alloc核心实现了

image.png

注释给到了这个方法主要目的是class_createInstance 通过class创建一个实例对象,那么这个对象必然是要丢出去给外部使用的,我们便清楚的定位到了要研究对象return obj,这个obj是怎么生成的呢?影响obj的参数有哪些?

  1. 计算开辟内存空间大小
  2. 开辟空间
  3. objcls通过isa进行关联

1. obj 开辟内存空间大小

cls->instanceSize 计算对象所占的内存空间大小

image.png

image.png

计算类的内存大小有两种方式

  1. 红框标注:编译器快速计算内存大小;以16字节对齐(16的倍数);现在使用的方式
  2. 绿框标注:8字节对齐(8的倍数);以前使用的方式

分别看下代码实现:

方式一绿框标注:8字节对齐(8的倍数) image.png

影响类的内存大小:类成员变量(类本身的成员变量;继承自父类的成员变量; isa) image.png

WORD_MASK = 7 image.png

8字节对齐算法分析
eg: x = 10  
(x + WORD_MASK) & ~WORD_MASK => (10 + 8 ) & ~7  => 18 & ~7

   0001 0100   18
 & 1111 1000   ~7
 = 0001 0000   16

方式二(红框标注):编译器快速计算内存大小;以16字节对齐(16的倍数); fastpath() 将最有可能的执行的分支告诉编译器,基本上行都支持cache.hasFastInstanceSize()快速计算内存大小

image.png

16字节对齐算法分析(和8字节相同)
eg: x  = 24 
(x + size_t(15)) & ~size_t(15) => 39 & ~15
  0010 0111   39
& 1111 0000   ~15
= 0010 0000   32


x= 16  16 + 15 = 31
   0001 1111
 & 1111 0000 
 = 0001 0000 = 16

现在是16字节对齐操作,也就是说,一个对象所占用的内存大小是至少16字节。为什么是16字节对齐呢?

  • 一个对象,必然会有一个属性,继承自NSObject类的Class isa (objc_class *Class)所以Class结构体指针,占8个字节,若无其他属性时,16字节对齐则会给该对象预留出8字节来,如果是8字节对齐没有预留出来8字节,相当于当前对象的isa紧挨着下一个对象的isa,会容易造成访问混乱。
  • 16字节对齐后,方便CPU读取速度,以空间换时间,访问也更安全,不会产生访问混乱的问题

2. 开辟内存空间

image.png

3. objcls通过isa进行关联

image.png

总结:

alloc流程图

image.png