dealloc的调用时机

文章目录
  1. 1. ARC文档中对dealloc过程的解释
  2. 2. delloc 调用栈分析
    1. 2.1. clearDeallocating 清空弱引用
    2. 2.2. object_cxxDestruct

一般想法 delloc 会在对象释放的时候调用。用户重载该方法释放自己分配的堆控件,移除订阅……

在 MRC 中

1
2
3
4
5
6
7
8
- (void)dealloc {
self.array = nil;
self.string = nil;
// ... //
// 非Objc对象内存的释放,如CFRelease(...)
// ... //
[super dealloc];
}

ARC 中

1
2
3
4
5
- (void)dealloc {
// ... //
// 非Objc对象内存的释放,如CFRelease(...)
// ... //
}

对比两端代码:

  1. 这个对象实例变量(Ivars)的释放去哪儿了?
  2. 没有显示的调用[super dealloc],上层的析构去哪儿了?

ARC文档中对dealloc过程的解释

A class may provide a method definition for an instance method named dealloc. This method will be called after the final release of the object but before it is deallocated or any of its instance variables are destroyed. The superclass’s implementation of dealloc will be called automatically when the method returns.

大概意思是:dealloc方法在最后一次release后被调用,但此时实例变量(Ivars)并未释放父类的dealloc的方法将在子类dealloc方法返回后自动调用

The instance variables for an ARC-compiled class will be destroyed at some point after control enters the dealloc method for the root class of the class. The ordering of the destruction of instance variables is unspecified, both within a single class and between subclasses and superclasses.

理解:ARC下对象的Ivars在根类[NSObject dealloc]中释放(通常root class都是NSObject),变量释放顺序各种不确定(一个类内的不确定,子类和父类间也不确定,也就是说不用care释放顺序)

问题:

  1. [super delloc] 为什么可以在子类的 delloc 返回后自动调用
  2. 对象的 Ivars 为什么会在根类的 delloc 中释放

delloc 调用栈分析

调用过程

dealloc -> _objc_rootDealloc -> object_dispose -> objc_destructInstance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
- (void)dealloc { _objc_rootDealloc(self); } 
void _objc_rootDealloc(id 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);
}
}

id object_dispose(id obj) {
objc_destructInstance(obj);
free(obj);
return nil;
}

/***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory.
* Calls C++ destructors.
* Calls ARC ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is 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);// 清理 ivars
if (assoc) _object_remove_assocations(obj);// 清理关联对象
obj->clearDeallocating();// 清理 weak 表
}

return obj;
}

主要是这个方法objc_destructInstance,根据他的注释

  1. 销毁实例对象,但没有释放内存
  2. 调用 C++ 的 destructor
  3. 掉用 ARC 将 ivar 清理
  4. 清理关联对象

object_cxxDestruct 做了什么?
clearDeallocating 做了什么?

clearDeallocating 清空弱引用

1
2
3
4
5
6
7
8
9
10
inline void objc_object::clearDeallocating() {
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
} else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}

ok clearDeallocating 用于清空引用计数表并清除弱引用表,将所有weak引用指nil

object_cxxDestruct

这篇文章中:

ARC actually creates a -.cxx_destruct method to handle freeing instance variables. This method was originally created for calling C++ destructors automatically when an object was destroyed.

object_cxxDestruct 方法用于释放实例的 ivars

和《Effective Objective-C 2.0》中提到的:

When the compiler saw that an object contained C++ objects, it would generate a method called .cxx_destruct. ARC piggybacks on this method and emits the required cleanup code within it.

可以了解到,.cxx_destruct方法原本是为了C++对象析构的,ARC借用了这个方法插入代码实现了自动内存释放的工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void object_cxxDestruct(id obj){
if (!obj) return;
if (obj->isTaggedPointer()) return;
object_cxxDestructFromClass(obj, obj->ISA());
}

/***********************************************************************
* object_cxxDestructFromClass.
* Call C++ destructors on obj, starting with cls's
* dtor method (if any) followed by superclasses' dtors (if any),
* stopping at cls's dtor (if any).
* Uses methodListLock and cacheUpdateLock. The caller must hold neither.
**********************************************************************/
static void object_cxxDestructFromClass(id obj, Class cls) {
void (*dtor)(id);
// Call cls's dtor first, then superclasses's dtors.
for ( ; cls; cls = cls->superclass) {
if (!cls->hasCxxDtor()) return;
dtor = (void(*)(id))
lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
if (dtor != (void(*)(id))_objc_msgForward_impcache) {
if (PrintCxxCtors) {
_objc_inform("CXX: calling C++ destructors for class %s",
cls->nameForLogging());
}
(*dtor)(obj);
}
}
}
  1. 在实例对象的最后一个 release 方法调用,引用计数 = 0 调用dealloc
  2. 实例对象是否是 isTaggedPointer
  3. 通过 C++ 的析构器释放 ivars 成员变量内存
  4. 移除关联对象
  5. 移除 weak 引用相关数据,weak 变量置为 nil

ARC下dealloc过程及.cxx_destruct的探究