iOS爱上底层-alloc与init那些事

547 阅读4分钟

为什么要学习底层?

2020年,移动互联网似乎又到瓶颈,一些开发者们难以看到发展的空间,陷入迷茫,但只要拨开 迷雾,这个领域依然有广阔的天地可以施展。这就是我们学习底层的原因。

alloc流程图

探索alloc源码

由于alloc源码不能直接在运行,所以需要进行相应的配置才能跑起来,需要的读者可以看看这篇文章iOS_objc4-756.2 最新源码编译调试

  • 如果alloc源码可以编译之后,在main函数里面创建一个NSObject对象并且alloc一下,例:NSObject *objc = [NSObject alloc]。然后按住cmd+鼠标左键,选择"NSObject alloc"进入alloc源码,然后在按住cmd+左键依次进入_objc_rootAlloc->callAlloc,此时alloc初始化的详细源码由此开始。

首先一进来,在1733行代码处就进行判断,使用slowpath就代表一般不可能出现的情况,但是为了保证正常运行,还是要判断一下cls和checkNil,checkNil直接传值为false,如果cls为nil则直接返回Nil。

而1736这行代码,我们点进去看下hasCustomAWZ这个方法。我们可以看见他是调用了一个叫hasDefaultAWZ的方法。

然后我们再点进去就可以看见:

这个方法主要就是判断当前对象或者父类是否重写了allocWithZone:这个方法(这个值会存储在metaclass 中)。

  • 如果当前对象或者父类没有重写allocWithZone:,则会进行判断cls->canAllocFast()。
    我们点进去看下canAllocFast()方法:
    然后在点击canAllocFast()进去看看:

这里面直接返回false。也就是说cls->canAllocFast()直接返回false,永远都不会进来。所以这里忽略,看else分支里面1750行的代码:我们点击class_createInstance()进去,然后在点击_class_createInstanceFromZone,看看这里面到底做了什么:
我们一进来,就会发现对cls进行判断,cls为nil就返回nil。
前两个的声明,表示是否有C++的类构造函数和析构函数如果有,则需要进行额外的任务,而fast则表示对isa的类型的区分,如果一个类的实例不能使用 isa_t类型的isa的话,fast就为false。
接下来我们会看见instanceSize(),这个就是alloc的关键:给对象开辟内存空间。

一点进来我们会发现,当开辟的内存空间小于16字节时就赋值为16,所以系统开辟的最小的空间是16字节。那大于16字节呢?我们就点击alignedInstanceSize(),发现他走了上面那个方法(红色方框圈出来的地方),首先unalignedInstanceSize()这个是没有进行字节对齐的一个属性。我们点进去看一看。
当我们程序在进行dyld在进行映射文件加载的时候,会加载类的结构,而类的结构是我们整个data的数据段。data里面最重要的东西,就是ro。ro里面存储的就是编译进去的属性的大小。
然后在word_align()进行字节对齐,我们点进去看一看。

根据代码可以判断出这里面是根据对象的属性方法来开辟内存空间并且进行8字节对齐。当对象的空间知道大小之后,我们就要开始创建obj了。首先会创建一个id类型的obj,然后判断 if (!zone && fast) 如果为YES,也就是 没有zone并且fast为真,这是比较常见的情况,因为zone肯定是没有的,fast也几乎为真,一般都会走到这个分支来,根据calloc创建的大小,来调用initInstanceIsa()进行初始化。
如果if (!zone && fast),就会去判断zone,而zone肯定还是没有,此时还是会调用calloc方法。然后在调用initIsa()。
不管最后他是调用InstanceIsa()还是initIsa()这个方法,他们都会调用一个共同方法initIsa。根据代码,我们可以看出initIsa就是创建一个isa,然后关联关象存并储对象的一些信息。
到这里我们只是看完了hasCustomAWZ等于NO的流程,接下来是等于YES的流程。当hasCustomAWZ等于YES的时候,他会直接调用allocWithZone(),然后在调用_objc_rootAllocWithZone()申请内存空间

一进入_objc_rootAllocWithZone(),我们会发现他直接创建一个id类型的Obj,然后直接调用了class_createInstance()创建内存。然后返回obj,整个alloc流程结束。

探索init

我们直接点击cmd+单击,进入init源码,然后直接调用_objc_rootInit()。可以看见他直接返回了一个self。所以我们可以得出一个结论。init啥也没干,就是开放出一个api提供给我们调用,方便我们初始化(类似工厂模式)。

探索new

我们直接点进new的源码,可以发现他是调用了callAlloc(self, false/checkNil/) init,既alloc+init。所以效果和alloc init是一样的

总结

1、初始化,开辟空间都是由alloc来完成的。
2、init什么也没做,只是把自己给返回了回去。
3、new等于alloc+init。