每个OC对象都拥有自己的引用计数,是一个整数,可以理解为该对象被多少人使用,一个对象被创建默认引用计数为1。当引用计数为0时,认为没有人使用该对象,该对象被销毁。
使用引用计数,对象就能得到很好的管理,这就是Objective-C的内存管理,如下图所示:
图中过程对应的oc方法:
自动引用计数是指,在内存管理中对引用采取自动计数的技术。以下摘自苹果官方说明。
在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类负担内存管理的职责。
**注:本文中的自己指”对象的使用环境“。
NSObject类的alloc方法能自己生成并持有对象。指向自己生成并持有的对象的指针被赋给变量object。
// 自己生成并持有对象
id object = [[NSObject alloc] init];
// 自己持有对象
使用new方法也能自己生成并持有对象。[NSObject new]
与[[NSObject alloc] init]
是完全一致的。
// 自己生成并持有对象
id object = [NSObject new];
// 自己持有对象
copy与mutableCopy方法生成并持有对象的副本。区别在于,copy方法生成的是对象的不可变副本,而mutableCopy方法生成的是对象的可变副本。
虽然是对象的副本,但在“自己生成并持有对象”这点上与alloc与new方法一致。
// 自己生成并持有对象的不可变副本
id objectCopy = [object copy];
// 自己生成并持有对象的可变副本
id objectMutableCopy = [object mutableCopy];
另外根据上述“使用以下名称开头的方法名”,下列名称也意味着自己生成并持有对象:
但是对于以下名称,即使使用了alloc/new/copy/mutableCopy名称开头,并不属于同一类别的方法。
使用上述方法以外的方法取得的对象,因为非自己生成并持有,所以自己不是该对象的持有者。
以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提供这样的功能,是对象在超出指定的生存范围时能够自动并正确地释放(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 或者其它未知行为