dealloc用法

使用场景

MRC

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

ARC

1
2
3
4
5
6
- (void)dealloc {
// 移除观察者
// 移除通知
// 非Objc对象内存的释放,如CFRelease(...)
// ... //
}

对比发现:

1.对象的实例变量释放不见了
2.没有显示的调用[super dealloc],上层的析构不见了

解释:

ARC官方文档

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下对象的实例变量在根类[NSObject dealloc]中释放(通常root class都是NSObject),变量释放顺序各种不确定(一个类内的不确定,子类和父类间也不确定,也就是说不用care释放顺序)
  • 所以ARC下的实例变量在根类NSObject析构是析构

NSObject 析构过程

  • dealloc
  • _objc_rootDealloc
  • objc_dispose
  • objc_destructInstance

objc_destructInstance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void *objc_destructInstance(id obj)
{
if (obj) {
Class isa_gen = _object_getClass(obj);
class_t *isa = newcls(isa_gen);
// Read all of the flags at once for performance.
bool cxx = hasCxxStructors(isa);
bool assoc = !UseGC && _class_instancesHaveAssociatedObjects(isa_gen);
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
if (!UseGC) objc_clear_deallocating(obj);
}
return obj;
}

方法干了三件事

  • object_cxxDestruct
  • _object_remove_assocations除去和这个对象assocate的对象(category)
  • objc_clear_deallocating清空引用计数病清除弱引用表,将所有的weak引用置为nil

object_cxxDestruct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void object_cxxDestructFromClass(id obj, Class cls)
{
void (*dtor)(id);
// Call cls's dtor first, then superclasses's dtors.
for ( ; cls != NULL; cls = _class_getSuperclass(cls)) {
if (!_class_hasCxxStructors(cls)) return;
dtor = (void(*)(id))
lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
if (dtor != (void(*)(id))_objc_msgForward_internal) {
if (PrintCxxCtors) {
_objc_inform("CXX: calling C++ destructors for class %s",
_class_getName(cls));
}
(*dtor)(obj);
}
}
}

沿着继承链往上搜索SEL_cxx_destruct,直到找到函数(void(*)(id))实现后返回

.cxx_destruct这个方法是C++对象析构的,ARC借用这个方法插入代码实现了自动释放的工作
这个方法最终会调用objc_storeStrong,遍历当前对象的所有实例变量(Ivars)进行形如objc_storeStrong(&ivar, null),实例对象就被release和置为nil

objc_storeStrong

1
2
3
4
5
6
7
id objc_storeStrong(id *object, id value) {
value = [value retain];
id oldValue = *object;
*object = value;
[oldValue release];
return value;
}

容易出现的问题

在某些情况下如果使用self.name=nil这种写法会使程序造成错误甚至崩溃。
假设在父类的dealloc方法中使用读写方法(self.name=nil)进行置空操作,如果子类重写了其读写方法,当所创建的子类对象销毁时进而调用父类的dealloc方法,就会造成访问已释放对象的情况,从而发生崩溃

父类

1
2
3
4
5
6
#import
@interface BaseClass : NSObject
@property(nonatomic) NSString *baseObj;
@end
1
2
3
4
5
6
7
8
9
#import "BaseClass.h"
@implementation BaseClass
- (void)dealloc {
self.baseObj = nil;//读写方法置空
}
@end

子类

1
2
3
4
5
6
7
#import "BaseClass.h"
@interface SubClass : BaseClass
@property (nonatomic) NSString *subObj;
@end
1
2
3
4
5
6
7
8
9
10
11
#import "SubClass.h"
@implementation SubClass
- (void)setBaseObj:(NSString *)baseObj {
NSLog(@"%@",[NSString stringWithString:_subObj]);
}
- (void)dealloc {
_subObj = nil;
}

主VC

1
2
3
4
5
6
- (void)viewDidLoad {
[super viewDidLoad];
SubClass *s = [[SubClass alloc]init];
s.subObj = @"子类属性";
}

试试

参考

不要在init和dealloc函数中使用accessor | 唐巧的博客

ARC下dealloc过程

这个人很帅<br>他什么都不想说<br>