欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

iOS:对象的alloc&init、ARC中dealloc使用

程序员文章站 2022-07-01 17:59:40
大家好,我是OB!今天来聊聊alloc&init和dealloc!一、对象的创建:alloc&init执行Cat *cat = [[Cat alloc]init];时,发生了什么?alloc开辟一块内存给对象,让它不释放,并且把地址返回给指针。init对这块内存进行初始化有下面的继承关系:Cat : Animal Animal : NSObject@interface Animal : NSObject@property (nonatomic, strong)NSString...

大家好,我是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初始化方法 去初始化成员变量,并把父类的成员变量放到子类开辟的内存中。

iOS:对象的alloc&init、ARC中dealloc使用

结论:所以对象的创建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会自动帮我们在代码的适当位置插入[obj release];,对象引用计数为0就会自动释放,其实release()底层也是调用对象的dealloc()方法。如果我们调用了[super dealloc];方法,那么由上可知,最终会执行到C++的objc_object::rootDealloc()方法,这时对象就free(this)释放了,然后ARC自动释放机制又会调用我们对象的dealloc()方法,最后又到了C++的objc_object::rootDealloc()方法,就会重复free(this)释放,导致未知错误

所以ARC环境下不能调用[super dealloc];,不然会导致该实例对象重复释放

本文地址:https://blog.csdn.net/pk_sir/article/details/107384255

相关标签: iOS进阶