在iOS开发中了,我们每天都会使用+ alloc
和- init
这两个方进行对象的初始化。我们也这知道整个对象的初始化过程其实就是开辟一块内存空间,并且初始化isa_t结构体的过程。
alloc的实现
1 | + (id)alloc { |
整个过程其实就是NSObject
对callAlloc
方法的实现。
callAlloc
1 | /* |
在__OBJC2__
下当前类有没有默认的allocWithZone
方法是通过hasCustomAWZ()
函数判断的。YES
代表有则会调用[cls allocWithZone:nil]
方法。NO
代表没有,这时候会根据当前类是否可以快速分配,NO
的话调用class_createInstance
函数;YES
则分配内存并初始化isa。
allocWithZone
1 | + (id)allocWithZone:(struct _NSZone *)zone { |
allocWithZone
函数的本质是调用_objc_rootAllocWithZone
函数。
_objc_rootAllocWithZone
的逻辑分为两种情况:
- 先判断是否是
__OBJC2__
,如果是则调用class_createInstance
; - 判断
zone
是否为空,如果为空调用class_createInstance
,如果不为空,调用class_createInstanceFromZone
。
1 | //class_createInstance |
class_createInstance
和class_createInstanceFromZone
的本质都是调用_class_createInstanceFromZone
。
另外通过前面的源代码我们可以发现:用alloc方式创建,只要当前类有allocWithZone方法,最终一定是调用class_createInstance。
_class_createInstanceFromZone
1 | static __attribute__((always_inline)) |
初始化isa
_class_createInstanceFromZone
中不光开辟了内存空间,还初始化了isa。初始化isa的方法有initInstanceIsa
和initIsa
,但是本质都是调用initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
。
1 | inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) |
根据《OC引用计数器的原理》,现在再看一下初始化isa的方法。这个方法的意思是首先判断是否开启指针优化。
没有开启指针优化的话访问 objc_object
的isa
会直接返回isa_t
结构中的cls
变量,cls
变量会指向对象所属的类的结构。
开启指针优化的话通过newisa(0)
函数初始化一个isa,并根据SUPPORT_INDEXED_ISA
分别设置对应的值。iOS设备的话这个值是0,所以执行else
的代码。
到这里alloc的实现过程已经结束了,根据上面的源码分析,用一张图表示上述过程:
这里可能会有个疑问,既然alloc
将分配内存空间和初始化isa的事情都做了,那么init
的作用是什么呢?
init
1 | - (id)init { |
init
的作用就是返回当前对象。这里有个问题既然init
只是返回当前对象,为什么要多此一举呢?
Apple给出的注释:
In practice, it will be hard to rely on this function. Many classes do not properly chain -init calls.
意思是在实践中,很难依靠这个功能。许多类没有正确链接init
调用。所以这个函数很可能不被调用。也许是历史遗留问题吧。
new
1 | + (id)new { |
所以说UIView *view = [UIView new];
和UIView *view = [[UIView alloc]init];
是一样的。
dealloc
分析了对象的生成,我们现在看一下对象是如何被销毁的。dealloc
的实现如下:
1 | - (void)dealloc { |
rootDealloc
分为三种情况:
- 如果是TaggedPointer,直接return;
- 进行一些关于isa的条件判断,如果满足就释放分配的内存控件;
- 调用
object_dispose
函数,这是最重要的;
objc_destructInstance
我们先看object_dispose
函数的源码:
1 | id object_dispose(id obj) { |
做了两件事情:
- 调用
objc_destructInstance
函数 - 释放分配的内存空间
objc_destructInstance
的实现如下:
1 | /*********************************************************************** |
objc_destructInstance
做了三件事情:
- 执行
object_cxxDestruct
调用析构函数 - 执行
_object_remove_assocations
删除关联对象 - 执行
clearDeallocating
清空引用计数表并清除弱引用表,将所有weak引用指nil(这也解释了为什么使用weak能自动置空)
object_cxxDestruct
在源码中object_cxxDestruct
的实现由object_cxxDestructFromClass
完成。
1 | static void object_cxxDestructFromClass(id obj, Class cls) |
这段代码的意思就是沿着继承链逐层向上搜寻SEL_cxx_destruct
这个selector
,找到函数实现(void (*)(id)(函数指针)并执行。说白了就是找析构函数,并执行析构函数。
析构函数中书如何处理成员变量的?
- 对于strong来说执行
objc_storeStrong(&ivar, nil)
release旧对象,ivar赋新值nil; - 对于weak来说执行
objc_destroyWeak(&ivar)
消除对象weak表中的ivar地址。
关于这个函数Sunnyxx ARC下dealloc过程及.cxx_destruct的探究中也有提到。
用一张图表示dealloc
的流程:
至于dealloc
的调用时机,是跟引用计数器相关的。