本文由快学吧个人写作,以任何形式转载请表明原文出处。
一、需要的资料
objc4-876,地址 : github.com/apple-oss-d...
二、探索的思路
1、要探索alloc和init和new具体做了什么,就需要知道什么时候我们用alloc和new,这个很容易,OC中一个对象的初始化最常用的就是 [[Obj alloc] init]和[Obj new],所以,第一步我们需要一个类,并且初始化一个对象。
2、要看alloc、init和new分别做了什么,就需要把它们分开来看。
3、我们最常用也是最基本最常见的类就是NSObject,我们创建的一些基本的类大多都继承于它,所以我们以NSObject的alloc、init和new方法来探索。
3、找到NSObject的源码,进行探索。
三、需要的基本知识储备
字节对齐 :
1、为什么要字节对齐
因为苹果官方要求的。
2、为什么要这么要求
不止是苹果官方,几乎所有的语言,都会要求内存对齐。
如下图 :
首先,我们存储数据的目的不是单纯的存储,而是要在需要的时候可以调用,而cpu是不是以字节为单位来读取数据的,而是以"块"为单位。
如果所有申请内存空间的数据都要这样去申请,并且拿到不同大小的内存空间去使用。
那么在我们再调用这些数据的时候,cpu要不断的变换读取数据的内存长度大小,来精准的获取数据,这是很浪费资源的行为。
所以,字节对齐,实际上是一种以空间换取时间的方式,让cpu更高效的读取内存中的数据。
3、iOS的字节对齐
iOS的字节对齐是以16字节为基本数,开辟内存空间的大小需要是16的整数倍。
为什么iOS要以16字节进行字节对齐?
因为OC对象中,第一位一定有一个isa指针
,而isa指针
的大小就是8字节,如果仅仅以8字节进行字节对齐,如果两个对象都是没有其他任何属性的对象(仅仅有isa指针
),那么这两个对象的内存空间就会挨在一起,就容易造成数据的混乱。
iOS也有对8字节对齐的要求,具体的详情在下面的苹果官方字节对齐算法代码那里有解释。
4、苹果官方的字节对齐算法
c
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
四、alloc
1、alloc方法的跳转
1、打开准备的资料 : objc4-876的文件。
2、在Xcode的Navigators搜索alloc {
,因为方法的实现都是以 {
来开始的,所以带上 {
可以省略很多麻烦。
3、在NSObject.mm文件找到 alloc
的实现方法。.mm格式文件是为了支持C++和OC的混编。
4、进入_objc_rootAlloc
方法。
5、进入callAlloc
方法。
到这里我们可以发现,alloc的实现方法,一定是在callAlloc
中提到的_objc_rootAllocWithZone
或者allocWithZone
方法。
如果可以断点调试的话,可以发现,实际上JDPerson
的alloc
方法进入的是第二个if,也就是fastpath
的判断里面,进入了_objc_rootAllocWithZone
方法。
2、alloc的核心实现
1、在搜索栏搜索_objc_rootAllocWithZone(
找到方法的实现。
2、进入_class_createInstanceFromZone
方法。
这里保留一下传入的参数,以便探索。这里可以进入一下OBJECT_CONSTRUCT_CALL_BADALLOC
,这个参数看一下,发现是一个枚举值,是2。所以传入的参数就是(cls, 0, nil, 2)。
3、_class_createInstanceFromZone
方法的实现
3、alloc申请内存中的一些细节
1、如何计算申请内存的对象所需内存空间的大小?
这一步在_class_createInstanceFromZone
方法中进行了实现,看上图中的
ini
size = cls->instanceSize(extraBytes);
这里的size就是所需内存空间的大小,计算所用到的方法是instanceSize
。直接进入instanceSize
方法。
2、计算内存大小
重点在fastInstanceSize
和alignedInstanceSize
。
fastInstanceSize重点在align16
:
alignedInstanceSize重点在word_align
:
3、align16
和word_align
的源码
就是字节对齐的算法代码。
这里有一点,两个字节对齐的算法,一个是8字节对齐,一个是16字节对齐。
原因是8字节对齐的受众是对象内部的属性,对象内部的属性需要遵循8字节对齐的原则,而16字节对齐的受众则是对象。
比如 : obj1和obj2之间需要满足16字节对齐,而obj1的内部属性attr1和attr2需要满足8字节对齐。
五、init
和alloc同样,在objc4-876源码中找init的实现,在Xcode的Navigators搜索init {
,找到NSObject.mm文件中的init实现。
类方法直接返回self,没有做任何的操作。实例方法调用_objc_rootInit
,看一下 :
一样直接返回self。
其实init的意义在官方的注释中已经表达清楚了,init就是工厂模式,为了给开发者在实际的应用中提供一个自己可以自定义的接口。
六、new
源码查找方法同上。
new直接调用了
callAlloc
方法,和alloc的区别就是alloc调用的是_objc_rootAlloc
方法,然后又_objc_rootAlloc
调用的callAlloc
,再就是传递的最后一个形参不一样。
new会直接调用init方法,如果我们自己定义一些init的方法,类似于initWith之类的,new只会调用init,而不会调用我们的init方法,所以用的比较少。
七、总结
1、alloc申请内存,字节对齐,绑定isa,核心方法是callAlloc(实际入口),instanceSize(计算开辟内存的大小),calloc(申请内存,返回地址指针),initIsa(绑定isa)。
2、init提供重写的机会,给开发者根据自身情况做自定义。
3、new直接调用了alloc的实际入口callAlloc方法,然后调用了init方法。不会调用我们自定义的一些类似于initWith的方法。