什么是自动引用计数
自动引用计数(ARC, Automatic Reference Counting)是指内存管理中对引用采用自动计数的技术
ARC苹果官方说明
在Objective-C采用Automatic Reference Counting (ARC)机制,让编译器来进行内存管理。在新一代Apple LLVM编译器中设置ARC为有效状态,就无需再次键入retain或者release代码,这在降低程序崩溃、内存泄漏等风险的同时,很大程度上减少了开发程序的工作量。编译器完全清楚目标对象,并能立刻释放那些不再被使用的对象。如此一来,应用程序将具有可预测性,且能流畅运行,速度也将大幅提升。
其中关键是
- 使用Xcode 4.2以上版本
- 使用LLVM编译器3.0或以上版本
- 编译器选项中设置ARC为有效
在以上条件编译源代码时,编译器将自动进行引用计数
那么先了解一下在此之前程序猿在代码是如何手动进行内存管理的
内存管理/引用计数
内存管理的思考方式
看到"引用计数"这个名称,会不自觉将注意力放在"计数"上,但其实客观正确的思考方式时:
- 自己生成的对象自己持有
- 非自己生成的对象自己也可以持有
- 不再需要自己持有的对象时释放
各个词表示的Objective-C方法如下
对象操作 | Objective-C方法 |
---|---|
生成并持有对象 | alloc/new/copy/mutableCopy等方法 |
持有对象 | retain方法 |
释放对象 | release方法 |
废弃对象 | dealloc方法 |
接下来详细了解"内存管理的思考方式"中出现的各个项目
自己生成的对象,自己持有
使用以下名称开头的方法名意味着自己生成的对象只有自己持有:
- alloc
- new
- copy
- mutableCopy
id obj = [[NSObject alloc] init];
使用NSObject
类的alloc
类方法就能自己生成并持有对象。指向生成并持有对象的指针被赋给变量obj
。另外,[NSObject new]
与[[NSObject alloc] init]
是完全一致的。
copy
方法利用基于NSCopying
方法约定,又各类实现的copyWithZone:
方法生成并持有对象的副本。mutableCopy
方法与copy
方法类似。两者的区别在于,copy
方法生成不可变更的对象,而mutableCopy
方法生成可变更的对象。这类似于NSArray
类对象与NSMutableCopy
类对象的差异。用这些方法生成的对象,虽然是对象的副本,但同alloc、new
方法一样,在"自己生成并持有对象"这点上没有改变。
下列名称也意味着自己生成并持有对象
- allocMyObject
- newThatObject
- copyThis
- mutableCopyYourObject
非自己生成的对象,自己也能持有
使用NSMutableArray
类的array
类方法
id obj = [NSMutableArray array];
NSMutableArray
类对象被赋给变量obj
,但变量自己不持有该对象,使用retain
方法可以持有对象
[obj retain];
通过retain
方法,非自己生成的对象成为了自己所持有的
不再需要自己持有的对象时释放
自己持有的对象一旦不再需要,持有者有义务释放该对象。释放使用release
方法
id obj = [[NSObject] init]; //自己生成并持有对象
[obj release]; //释放对象
如此,使用alloc
方法自己生成并持有的对象就释放了。自己生成而非自己所持有的对象,若用retain
方法变为自己持有,也同样可以用release
方法释放。
无法释放非自己持有的对象
// 1. 释放一个已经释放的对象
id obj = [[NSObject alloc] init];
// 已经释放对象
[obj release];
// 释放了对象还进行释放
[obj release];
// 2. 释放一个不属于自己的对象
id obj1 = [obj object];
// obj1没有进行retain操作而进行release操作,使得obj持有对象释放,造成了野指针错误
[obj1 release];
引用计数器讨论
苹果对于引用计数的管理是通过一张引用计数表进行管理的
我们平常在操作对象的引用计数器时,其实就是对这个引用计数表进行操作,在获取到该表的地址以及相应对象的内存地址,就可以通过对象的内存从该表中进行索引获取到相应的引用计数值,然后根据用户的操作来返回计时器、计时器加1、计时器减1,下面就深入讨论retain、release、alloc、dealloc具体怎么操作该引用计数表
alloc
当我们调用alloc函数时我们进一步会调用allocWithZone方法
id obj = [[NSObject alloc] init];
+ (id)alloc {