【iOS】引用计数

引用计数

每个OC对象都拥有自己的引用计数,是一个整数,可以理解为该对象被多少人使用,一个对象被创建默认引用计数为1。当引用计数为0时,认为没有人使用该对象,该对象被销毁。

使用引用计数,对象就能得到很好的管理,这就是Objective-C的内存管理,如下图所示:

在这里插入图片描述

图中过程对应的oc方法:

  1. 生成对象:通过alloc/new/copy/mutableCopy等方法生成对象;
  2. 持有对象:通过retain方法持有对象,对象引用计数加1;
  3. 释放对象:通过release方法释放对象,对象引用计数减1;
  4. 废弃对象:当对象引用计数归0,Objective-C給对象发送dealloc消息销毁对象。

自动引用计数

自动引用计数是指,在内存管理中对引用采取自动计数的技术。以下摘自苹果官方说明。

在Objective-C中采用ARC(Automatic Reference Counting)机制,让编译器来进行内存管理。在新一代Apple LLYM编译器中设置ARC为有效状态,就无需再次键入retain或者release代码,这在降低程序崩溃、内存泄漏等风险的同时,很大程度上减少了开发程序的工作量。编译器完全清楚目标对象,并能立刻释放那些不再被使用的对象。如此一来,应用程序将具有可预测性,且能流畅运行,速度也将大幅度提升。

这些优点无疑极具吸引力,但关于ARC技术,最重要的还是下面这一点:

“在LLVM编译器中设置ARC为有效状态,就无需再次键入retain或者是release代码。”

手动内存管理

自动引用计数是极具吸引力的,但在此之前,我们先了解一下程序员在代码中是如何手工进行内存管理的。

引用计数式内存管理思考方式:

  • 自己生成的对象,自己所持有。
  • 非自己生成的对像,自己也能持有。
  • 不在需要自己所持有的对象时释放。
  • 非自己持有的对象无法释放。

不要过分关注“计数”本身,因为当某个对象的引用计数归0,系统会自动销毁该对象。因此,我们只需要保证及时释放不需要自己持有的对象,就可以很好的管理内存。过分关注“计数”本身对我们并无助益。

对象操作与Objective-C的方法对应:

对象操作Objective-C方法
生成并持有对象alloc/new/copy/mutableCopy等方法
持有对象retain方法
释放对象release方法
废弃对象dealloc方法

这些OC内存管理的方法其实并不包括在OC语言中,而是包含在Cocoa框架中,Cocoa框架中Foundation框架类库的NSObject类负担内存管理的职责。
在这里插入图片描述

自己生成的对象,自己所持有

**注:本文中的自己指”对象的使用环境“。

alloc与new

NSObject类的alloc方法能自己生成并持有对象。指向自己生成并持有的对象的指针被赋给变量object。

         // 自己生成并持有对象
        id object = [[NSObject alloc] init];
        
        // 自己持有对象

使用new方法也能自己生成并持有对象。[NSObject new][[NSObject alloc] init]是完全一致的。

		// 自己生成并持有对象
        id object = [NSObject new];
        
        // 自己持有对象

copy与mutableCopy

copy与mutableCopy方法生成并持有对象的副本。区别在于,copy方法生成的是对象的不可变副本,而mutableCopy方法生成的是对象的可变副本。

虽然是对象的副本,但在“自己生成并持有对象”这点上与alloc与new方法一致。

		// 自己生成并持有对象的不可变副本
        id objectCopy = [object copy];
        
        // 自己生成并持有对象的可变副本
        id objectMutableCopy = [object mutableCopy];

方法名

另外根据上述“使用以下名称开头的方法名”,下列名称也意味着自己生成并持有对象:

  • allocMyObject
  • newThatObject
  • copyThis
  • mutableCopyYourObject

但是对于以下名称,即使使用了alloc/new/copy/mutableCopy名称开头,并不属于同一类别的方法。

  • allocate
  • newer
  • copying
  • mutableCopyed

非自己生成的对象,自己也能持有

使用上述方法以外的方法取得的对象,因为非自己生成并持有,所以自己不是该对象的持有者。
以NSMutableArray的array方法为例:

		// 取得非自己生成并持有的对象
        id object = [NSMutableArray array];
        
        // 取得的对象存在但自己不持有对象

源代码中,NSMutableArray类变量被赋给objetc,但变量object自己并不持有该对象,使用retain可以持有该对象。

		[object retain];
        
        // 自己持有对象

通过retain方法,非自己生成的对象成为了自己持有的。

不需要自己持有的对象时释放

自己持有的对象,一旦不在需要,持有者有义务释放对象。释放对象使用release方法。

        // 自己生成并持有对象
        id object = [[NSObject alloc] init];

        // 自己持有对象
        
        [object release];
        
        // 释放对象
        
        // 指向对象的指针貌似还被保留在object变量中,貌似还能访问
        // 对象一经释放,绝对不可访问

用alloc/new/copy/mutableCopy等方法创建并持有的对象或者通过retain持有的对象,一旦不在需要,务必使用release释放。

如果要用某个方法生成对象,并返还给方法的调用方,源代码是什么样:

+ (id) allocObject {
    // 自己生成并持有对象
    id object = [[NSObject alloc] init];
    
    // 自己持有对象
    
    return object;
}

原封不动的返回alloc生成并持有的对象,就能让方法的调用方也能持有该对象。

那么,调用某个方法使得取得的对象存在,但自己又不持有该对象,又是如何实现的:

+ (id) object {
    // 自己生成并持有对象
    id object = [[NSObject alloc] init];
    
    [object autorelease];
    
    // 取得的对象存在,但自己不持有对象
    
    return object;
}

上例中,我们使用了autorelease方法。

autorelease

autorelease方法可以使取得的对象存在,但自己不持有对象。autorelease提供这样的功能,是对象在超出指定的生存范围时能够自动并正确地释放(release方法)如图。
在这里插入图片描述

使用NSMutableArray类的array类方法等可以取得谁都不持有的对象,这些方法都是通过autorelease方法实现的。另外,根据上文的命名规则,这些方法名称不能以alloc/new/copy/mutableCopy开头。

当然,也能通过retain方法将调用autorelease方法取得的对象变为自己持有。

        id object = [MyObject object];
        // 取得对象存在,但自己不持有对象
        
        [object retain];
        // 自己持有对象

无法释放非自己持有的对象

对于自己持有的对象,在不需要对象时需要将其释放。而非自己持有的对象绝对不能释放。倘若在应用程序中释放了非自己持有的对象就会造成崩溃。例如自己生成并持有对象,在不需要对象释放后,再次释放该对象。

        // 自己生成并持有对象
        id object = [[NSObject alloc] init];

        // 自己持有对象

        [object release];
        // 释放对象
        
        [object release];
        // 再次释放以非自己持有的对象
        // 应用程序奔溃
        
        // 奔溃情况:
        //  再度废弃已经废弃的对象时奔溃
        //  访问已经废弃的对象时崩溃

补充:上例应该会导致程序崩溃,但实际运行时却没有。这是因为因为对象所占的内存在“解除分配”之后,只是放回了“可用内存池”。如果再次释放对象时尚未覆写对象内存,那么该对象仍然有效,这时程序不会崩溃,所以调试过程中可能结果不一样。为避免在不经意间使用了无效对象,一般调完release之后都会清空指针。这就能保证不会出现可能指向无效对象的指针,这种指针通常称为“悬挂指针”。

        [object release];
        object = NULL;

或者在“取得对象存在,但自己不持有对象”时释放。

        // 取得非自己生成并持有的对象
        id object = [NSMutableArray array];

        // 取得的对象存在但自己不持有对象
        
        [object release];
        // 释放了非自己持有的对象,可能会导致运行时 crash 或者其它未知行为
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值