对象的dealloc调用后发生了什么?
本文对应的源码见:https://github.com/huangturen/objc-runtime
第一、最简单的类(没有任何成员变量,没有任何引用的类)其dealloc的过程为何?
我们创建一个类TestObject1,使其继承自NSObject,在main函数中创建一个TestObject1的临时变量,观察其dealloc过程:
static void testDeallocSimple(){
TestObject1 *obj1 = [TestObject1 new];
NSLog(@"%@",obj1);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
testDeallocSimple();
}
return 0;
}
当testDeallocSimple()执行结束时,obj1对象需要析构,在NSObject.mm的dealloc方法中,我们断点,会发现调用顺序为:
显然,调用顺序为:先调用当前类的dealloc,然后调用NSObject类的dealloc。这里我们可以推定,多次继承的情况下其dealloc调用顺序应该为:先子类然后父类的顺序,最后到了NSObject根类。这个结论也不难验证,再上面示例的基础上,我们再多继承一次后的调用情况如图所示:
所以,一个类的dealloc执行后,其会依次调用父类的dealloc方法,直到NSObject的dealloc为止。
那么NSObject的dealloc执行时发生了什么?
其实际的调用链为:
- (void)dealloc {
_objc_rootDealloc(self);
}
void
_objc_rootDealloc(id obj)
{
assert(obj);
obj->rootDealloc();
}
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
上述TestObject执行了free(this)这个分支,即dealloc后,直接释放了内存,然后结束。
第二、当类有一个成员变量时,其dealloc过程为何?
我们定义一个测试类TestObjectWithProperty:
@interface TestObjectWithProperty : NSObject
@property(nonatomic, strong)NSMutableArray *array;
@end
然后采用上述同样的方式测试,来观察整个dealloc流程。这里我们发现objc_object::rootDealloc()方法中执行了object_dispose((id)this);通过lld调试,发现TestObjectWithProperty的isa结构体成员变量值如下:
(lldb) po isa.nonpointer
1
(lldb) po isa.weakly_referenced
<nil>
(lldb) po isa.has_assoc
<nil>
(lldb) po isa.has_cxx_dtor
1
(lldb) po isa.has_sidetable_rc
<nil>
所以,显然当前类的属性has_cxx_dtor为true;这里has_cxx_dtor意思是当前类有方法[TestObjectWithProperty .cxx_destruct],此方法是编译期自动为类添加,具体的添加规则,我们可以在类的初始化流程中去分析。所以,以上逻辑可以看出,OC对象销毁有两种调用路径: 我们可以称之为快速销毁和一般销毁.为了解释方便理解这两种销毁路径,我们有必要解释一下isa的成员变量的意义,参考http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html.
名称 | 意义 | 长度 |
nonpointer |
0表示指针(指针只用来描述地址),1表示优化后的指针(除了描述地址以外还做了一些其他的事儿) | 1 |
has_assoc | Object has or once had an associated reference. Object with no associated references can deallocate faster. 有或曾经有过关联对象 | 1 |
has_cxx_dtor | Object has a C++ or ARC destructor. Objects with no destructor can deallocate faster. 有C++ 或 ARC 析构器 | 1 |
shiftcls | Class pointer's non-zero bits. 类指针的非空字节 | 44 |
magic | Equals 有固定的值,debugger用来区分未初始化的变量 | 6 |
weakly_referenced | Object is or once was pointed to by an ARC weak variable. Objects not weakly referenced can deallocate faster. 被或曾经被弱引用过 | 1 |
deallocating | Object is currently deallocating. 正在dealloc | 1 |
has_sidetable_rc | Object's retain count is too large to store inline. 对象的保留计数过大 | 1 |
extra_rc | Object's retain count above 1. (For example, if 此值+1则为对象的引用计数 | 8 |
从上述解释不难看出,对象快速销毁发生在:指针被优化过,没有被弱引用,没有关联对象,没有C++或ARC析构器,以及引用计数可以内连储存时------本质上是当前对象的所有信息都存在在这一个指针内时.
第三、一般销毁object_dispose的具体操作是什么?
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
从object_dispose的实现来看,其首先执行objc_destructInstance方法,然后释放内存。而objc_destructInstance具体的工作包括三部分:
1、如果当前实例有.cxx_Destruct方法,则执行之;
2、如果当前实例有关联对象,则移除之;
3、执行clearDeallocating,其内部所做的工作为从weak列表中移除(如果被弱引用)以及销毁引用计数的存储指针(如果引用计数很大不能内联存储),这里的weak列表和引用计数都保存在一个叫SideTable的全局结构体中,此结构体在runtime运行期内不会被析构;
完!