大家好,我是OB!今天来聊聊alloc&init和dealloc!
一、对象的创建:alloc&init
执行Cat *cat = [[Cat alloc]init];
时,发生了什么?
alloc
开辟一块内存给对象,让它不释放,并且把地址返回给指针。
init
对这块内存进行初始化
有下面的继承关系:Cat : Animal
Animal : NSObject
@interface Animal : NSObject
@property (nonatomic, strong)NSString *name;
@end
@implementation Animal
- (instancetype)init {
self = [super init];
if (self) {
self.name = @"我是动物";
}
return self;
}
@end
@interface Cat : Animal
@end
@implementation Cat
- (instancetype)init {
self = [super init];
if (self) {
self.name = @"ob";
}
return self;
}
@end
注意:子类
Cat
中的[super init]
并没有给父类Animal
开辟内存,子类的[super init]
也不会创建父类的实例对象!要不然内存中全是NSObject的实例对象,因为都继承自NSObject。
1、那么[super init]
干了些什么呢?
由于[[Cat alloc]init];
开辟了一块内存,那么就需要对这块内存进行初始化,不然访问这块内存没有意义。因为继承的原因,需要先到父类那里把成员变量给继承过来,所以需要到父类的init
初始化方法 去初始化成员变量,并把父类的成员变量放到子类开辟的内存中。
结论:所以对象的创建Cat *cat = [[Cat alloc]init];
,子类调用[super init]
并不会创建父类的实例对象!只是将父类的成员变量搬到子类的实例对象中。所以对象释放时,只需要释放当前对象,父类不需要释放,因为父类没有执行alloc
也没有开辟内存,根本不需要释放。
二、对象的销毁:dealloc
首先来看看dealloc
源码的实现
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);
if (this) {
// Read all of the flags at once for performance.
bool cxx = this->hasCxxDtor();
bool assoc = this->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(this);
if (assoc) _object_remove_assocations(this);
this->clearDeallocating();
}
free(this);
}
}
可以发现,执行
hasCxxDtor
(清除成员变量),_object_remove_assocations
(清除管理对象)之后,就会free(this)
,对象的释放就此完成。
结论:对象要释放,必须执行根类NSObject中-(void)dealloc()
方法,因为该方法最终会去执行C++的objc_object::rootDealloc()
方法。对象的销毁最终就是在rootDealloc()
中销毁
1、MRC 为什么要调用[super dealloc];
首先在MRC环境下,系统不会自动释放对象。
由于对象的释放必须调用c++的rootDealloc()
方法。因此一旦我们重写了对象的dealloc方法,如果不调用[super dealloc];
那么就不会调用到根类NSObject中-(void)dealloc()
方法。对象也就释放不掉,只是单单执行了该类-(void)dealloc()
方法,并没有释放对象
举个例子,如下代码:运行在MRC,注释掉[super dealloc];
看看对象是否被释放。
@implementation Cat
- (void)dealloc {
NSLog(@"self:[%@]-%s",self, __func__);
// [super dealloc];
}
@end
调用
- (void)viewDidLoad {
[super viewDidLoad];
Cat *cat = [[Cat alloc]init];
cat.name = @"ob";
NSLog(@"---%@",cat.name);
[cat release];
NSLog(@"---%@",cat.name);
}
打印如下:发现执行dealloc()
方法后,对象没有被释放
Test[38251:2112405] ---ob
Test[38251:2112405] self:[<Cat: 0x600002b24440>]--[Cat dealloc]
Test[38251:2112405] ---ob
执行
[cat release];
后,确实来到了[Cat dealloc]
方法,但是对象的属性cat.name
依然可以使用。也不会crash。就是因为没有调用[super dealloc];
导致没有执行根类NSObject中-(void)dealloc()
方法,也就没有执行C++中的objc_object::rootDealloc()
方法去释放对象。对象依然存在。
所以MRC一旦重写了-(void)dealloc()
方法一定要调用[super dealloc];
,这样才能释放该实例对象。
2、ARC 为什么不能调用[super dealloc];
?
ARC如何自动释放?
首先ARC会自动帮我们在代码的适当位置插入[obj release];
[obj release];
最终会来到根类NSObject
的release()
,此时,会检查对象引用计数是否为0,来判断否调用根类的dealloc()
方法。如果为0,那么最后又到了C++的objc_object::rootDealloc()
方法,对象就会释放。
我们什么也不做,ARC就调用了一次C++的objc_object::rootDealloc()
方法。此时,
如果我们再次调用了[super dealloc];
方法,那么最终会调用根类NSObject
的release()
方法,就会会执行到C++的objc_object::rootDealloc()
方法,由于ARC已经释放该对象了,这时再次free(this)
释放该对象,发生错误。
所以ARC环境下不能调用[super dealloc];
,不然会导致该实例对象重复释放