什么是自动引用计数(ARC)
顾名思义,自动引用计数是指内存管理中对引用采用自动计数的计数,让编译器来进行内存管理。在新一代的Apple LLVM编译器中设置ARC为有效状态,就无需再输入retain或者release代码,这在降低程序崩溃,内存泄露等风险的同时,很大程度上减少了开发程序的工作量。而编译器完全清楚目标对象,并能立刻释放那些不再使用的对象,如此一来,应用程序将具有可预测性,且能流畅运行,速度也将大幅提升
手动引用计数(MRC)
说到自动引用计数,就不得不提手动引用计数。iOS内存管理中引用计数功能来标识使用一个对象的引用次数。引用计数的思考原则就是生成、持有、释放、废弃四个动作。这些有关NSObjective-C内存管理的方法,实际上不包含在改语言中,而是包含在Cocoa框架中用于OSX、iOS应用开发。Cocoa框架中的Foundation框架类库的NSObject类负担内存管理的职责。Objective-C内存管理中的alloc/retain/release/dealloc方法分别指代NSObject类中的alloc类方法、retain示例方法、release示例方法和dealloc示例方法、
自己生成的对象,自己持有
- 使用alloc、new、copy、mutableCopy开头的方法名生成的对象,意味着只有自己持有。
/*
* 自己生成并持有对象
*/
id obj = [[NSObject alloc] init];
/*
自己持有对象
*/
- 使用NSObject类的alloc方法就能自己生成并持有对象。指向生成并持有对象的指针赋给变量obj。另外,使用new类方法也能生成并持有对象。[NSObject new]与[[NSObject alloc] init]是完全一致的
/*
* 自己生成并持有对象
*/
id obj = [NSObject new];
/*
自己持有对象
*/
- copy 方法利用给予NSCopying方法约定,由各自类实现的CopyWithZone: 方法生成并持有对象的副本。与copy方法类似,mutableCopy 方法利用给予NSMutableCopying方法约定,由各类实现的mutableCopyWithZone:方法生成并持有对象的副本。两者的区别在于,copy方法生成不可变更的对象,而mutableCopy生成可变更的对象。这类似于NSArray类对象与NSMutableArray类对象的差异。用这些方法生成的地域性,虽然是对象的副本,但是同alloc、new方法一样,在“自己生成并持有对象”这点上并没有改变。
另外,根据上述“使用一下名称开头的方法名”,下列名称也意味着自己生成并持有对象- allocMyObject
- allocThatObject
- copyThis
- mutableCopyYourObject
但是对于以下名称,即使使用alloc/new/copy/mutableCopy名称开头,并不属于同一类别的方法 - allocate
- newer
- copying
- mutableCopying
非自己生成的对象,自己也能持有
用上述项目之外的方法取得的对象,即用alloc/new/copy/mutableCopy以外方法取得的对象,因为并非自己生成并持有,所以自己不是该对象的持有者。我们来使用alloc/new/copy/mutableCopy以外的方法看看。这里试用一下NSMutableArray类中的array类方法。
/*
* 取得非自己生成并持有的对象
*/
id obj = [NSMutableArray array];
/*
* 取得的对象存在,但是自己不持有对象
*/
源代码中,NSMutableArray类对象被赋值给变量obj,但是obj自己并不持有该对象。使用retain方法可以持有等一下。
/*
取得非自己生成并持有的对象
*/
id obj = [NSMutableArray array];
/*
* 取得的对象存在,但是自己不持有该对象
*/
[obj retain];
/*
自己持有该对象
*/
通过retain方法,非自己生成的对象跟用alloc/new/copy/mutableCopy方法生成并持有的对象一样,成为了自己所持有的。
疑问
通过[NSMutableArray array]生成的对象,并没有持有它,为何不会被释放?它与retain再release后的对象有何区别?
看代码实现,应该是类方法创建会自动将其加入AutoreleasePool,在超出作用域的时候再自动释放。
不在需要自己持有的对象时释放
自己持有的对象,一旦不再需要,持有者有义务释放该对象。释放对象用release方法。
/*
* 自己生成并持有对象
*/
id obj = [[NSObject alloc] init];
/*
* 自己持有对象
/*
[obj release];
/*
* 释放对象
* 指向对象的指针仍然被保留在变量obj中,貌似能够访问
* 但对象一经释放绝对不可访问,会产生野指针问题
/
- 如此,用alloc方法自己生成并持有的对象就通过release方法释放了,自己生成而非自己所持有的对象,若用retain方法变为自己持有,也同样可以用release方法释放
/*
* 取得非自己生成并持有的对象
*/
id obj = [NSMutableArray array];
/*
* 取得的对象存在,但自己不持有对象
*/
[obj retain];
/*
* 自己持有对象
/*
[obj release];
/*
* 释放对象
* 指向对象的指针仍然被保留在变量obj中,貌似能够访问
* 但对象一经释放绝对不可访问,会产生野指针问题
* release必须在持有后调用,没有持有调用或重复调用都会导致crash
/
- 用alloc/new/copy/mutableCopy方法生成并持有的对象,或者用retain方法持有的对象,一旦不再需要,务必要用release方法进行释放。
- 如果要用某个方法生成对象,并将其返还给该方法的调用者,那么它的源代码是怎样的呢?
-(id)allocObject {
/*
* 自己生成并持有对象
*/
id obj = [[NSObject alloc] init];
/*
* 自己持有对象
*/
return obj;
}
- 如上例所示,原封不动地返回用alloc方法生成并持有的对象,就能让调用方法也持有该对象,请注意allocObject这个名称是符合前文命名规则的。
/*
* 取得非自己生成并持有的对象
*/
id obj = [NSObject allocObject];
/*
* 自己持有对象
*/
- allocObject 名称符合前文的命名规则,因此它与用alloc生成并持有对象的情况完全相同,所以使用allocObject方法也就意味着“自己生成并持有对象”
那么,调用[NSMutableArray array]方法使取得的对象存在,但是自己又不持有对象,又是如何实现的呢?根据上文命名规则,不能使用以alloc/new/copy/mutableCopy开头的方法名,因此要使用object这个方法名。
-(id)object {
id obj = [[NSObject alloc] init];
/*
自己持有对象
*/
[obj autorelease];
/*
取得的对象存在,但是将对象放入自动释放池,自己不持有对象
*/
return obj;
}
- 上例中,我们使用了mutorelease方法,使用该方法,可以使取得的对象存在,但是自己不持有对象。autorelease提供这样的功能,使对象在超出指定的生存范围时能够自动并正确的释放(调用release方法)。调用release方法使立即使对象的引用计数-1,而使用autorelease方法是不立即释放,而是注册到autoreleasepool中,pool结束时会自动对注册到pool的对象调用一次release。
- 使用NSMutableArray的array类方法可以取得谁都不持有的对象,这些方法都是通过autorelease而实现的。此外,根据上文的命名规则,这些用来取得谁都不持有的对象的方法名不能以alloc/new/copy/mutableCopy开头,这点需要注意。
id obc1 = [NSObject object];
/*
* 取得的对象存在,但是自己不持有对象
*/
- 当然,也能够通过retain方法将调用autorelease方法取得的对象变为自己持有。
id obj1 = [NSObject object];
/*
* 取得的对象存在,但是自己不持有对象
*/
[obj1 retain];
/*
* 自己持有对象
* 注意,对象调用retain方法后必须调用release方法进行释放,否则会产生僵尸对象
/*
无法释放非自己持有的对象
- 对于用alloc/new/copy/mutableCopy方法生成并持有的对象,或是用retain方法持有的对象,由于持有者是自己,所以在不需要该对象的时候需要将其释放。而由此之外所得到的对象绝对不能释放。在程序中,释放了非自己持有的对象就会造成crash。而在自己生成并持有的对象,在释放完不再需要的对象后再次释放也会造成crash
/*
* 自己生成并持有对象
*/
id obj = [[NSObject alloc] init];
/*
* 自己持有对象
*/
[obj release];
/*
* 对象已释放
*/
[obj release];
// 释放之后再次释放已非自己持有的对象,应用程序将crash
// 再度废弃已经废弃了的对象和访问已经废弃的对象,都会造成crash
- 或者在“取得的对象存在,但是自己不持有对象”时释放
id obj = [NSObject object];
// 取得的对象存在,但自己不持有对象
[obj release];
// 释放了非自己持有的对象会导致crash
- 以上,“自己生成并持有对象”、“非自己生成的对象”,“自己也能持有、不再需要自己持有的对象时需要释放”、“非自己持有的对象无法释放” 就是“引用计数式内存管理”的思考方式。