概要
OC中的内存管理,也就是引用计数。可以用开关灯房间的案例来说明:
假设办公室照明设备只有一个。上班的人进入办公室需要照明,所以把灯打开。而队对于下班离开办公室的人来说,已经不需要照明了,所以把等关掉。若是很多上下班,每个人都开灯或者关灯,那么办公室的情况又将如何呢?最早下班的人关灯,那么办公室岂不是一片黑暗。
解决这个问题的办法是使办公室还有至少一个人的情况下保持开灯状态,无人的时候保持关状态。
1. 最早进入办公室开灯
2. 之后进入办公室的人,需要照明
3. 下班离开办公室的人,不需要照明
4. 最后离开办公室的人关灯(此时已无人需要照明)
根据计数功能来计算 ”需要照明的人数”
- 第一个人进入办公室,办公室的计数为1。计数:1
- 之后进入办公室的人,叠加1。计数:2
- 每当人有人下班离开,减去1。计数:1
- 最后离开办公室,减去1。计数:0,因此要关灯。
在OC中,”对象” 相当于办公室的照明设备。
- 开灯 –> 生成对象
- 需要照明 –> 持有对象
- 不再需要照明 –> 释放对象
- 关灯 –> 废弃对象
内存管理的思考方式
- 自己生成并持有对象。
- 持有非自己生成的对象。
- 不再需要自己持有的对象时释放。
- 非自己持有的对象无法释放。
对象操作 | Objective-C方法 |
---|---|
生成并持有对象 | alloc/new/copy/muetableCopy等方法 |
持有对象 | retain 方法 |
释放对象 | release 方法 |
废弃对象 | dealloc 方法 |
自己生成并持有对象
下面写出了自己生成并持有对象,我们使用alloc方法:
<!--自己生成并持有对象-->
id obj = [[NSObject alloc] init];
copy方法利用基于NSCopying方法约定,由各类实现copyWithZone:方法生成并持有对象的副本。与Copy方法类似,mutableCopy方法利用基于NSMutableCopying方法约定,由各类实现mutableCopyWhitZone:方法生成持有对象的副本。
这些方法生成的对象,虽然是对象的副本,但同alloc、new方法一样,在 “自己生成并持有对象” 这点上没有改变。
所谓自己生成,这个自己可以看做当前环境
或者编程人员
本身也没错。或者可以这样理解,当前这个对象是在当前的环境生成,那么这个对象就属于这个当前环境
生成的,所以对象被当前环境
持有。
结论:
通过alloc/new/copy/mutableCopy
生成的对象都是 自己生成并持有对象
。
持有非自己生成的对象
<!--取得非自己生成并持有的对象-->
id obj = [NSMutableArray array];
上述源码中,NSMutableArray类对象被赋给变量obj,但变量obj自己并不持有改对象。使用retain方法可以持有对象。
//持有对象
[obj retainl];
后续会说明为什么这种方式(非alloc/new/copy/mutableCopy
)生成的就是非自己生成的。
不再需要自己持有的对象时释放
自己持有的对象,一旦不再需要,持有者有义务释放该对象。释放使用release
方法。
//自己生成并持有对象
id obj = [[NSObject alloc] init];
//释放
[obj release];
自己生成并持有 和 非自己生成也能持有 的原理。
用
alloc/new/copy/mutableCopy
方法生成自己生成并持有的对象
,或者用retain
方法持有的对象,一旦不再需要,务必要用release
方法进行释放。
现在我们通过源码来分析:为什么会有自己生成并持有
和非自己生成也能持有
:
- 通过模拟
alloc/new/copy/mutableCopy
方法, 理解自己生成并持有对象
+ (NSObject *) allocObject {
//自己生成并持有对象
id obj = [[NSObject alloc] init];
//自己持有对象
retain obj;
}
原封不动地返回用 alloc
方法生成并持有对象,就能让调用方也持有该对象。
<!--取得非自己生成并持有的对象-->
id obj1 = [NSObject allocObject];
allocObject
与用 alloc
方法生成并持有对象的情况完全相同,所有使用allocObject方法也就意味着 自己生成并持有对象
。
- 通过模拟
[NSMutableArray array]
方法, 理解非自己生成并持有对象
前面就有说到调用 [NSMutableArray array]
方法使取得的对象存在,但是自己又不持有对象,又是怎么实现的呢?
- (id)object {
//自己生成并持有对象
id obj = [[NSObject alloc] init];
//使对象在超出指定的生成范围时能够自动并正确地的释放
[obj autorelease];
//返回对象
return obj;
}
上例中,我们使用了 autorelease 方法。用该方法,可以使取得的对象存在,但是自己不持有对象。
这样的好处就是,我们可以不用去手动去管理通过此方法 生成的对象 的释放。
autorelease 提供这样的功能,是对象在超出指定的生成范围时能够自动并正确地释放(调用release方法)
看完这张 release 和 autorelease
图:
相信大家已经明白为什么通过object
、array
方法生成的是非自己生成
,是因为生成在生成这个对象的方法里面,添加延迟释放处理,而不需要我们再去调用release
释放了。
那么我们要如何去区分到底谁需要调用release
,谁不需要调用release
。所以引入了命名规则:
- 使用
NSMutableArray
类的array
类方法等可以取得谁都不持有的对象,这些方法都是通过autorelease
而实现的 alloc/new/copy/mutableCopy
方面名开头的,就是自己生成并持有的对象
无法释放非自己持有的对象
用
alloc/new/copy/mutableCopy
方法生成自己生成并持有的对象
,或者用retain
方法持有的对象,一旦不再需要,务必要用release
方法进行释放。
而由此以外所得到的对象绝对不能释放。倘若在应用程序中释放了非自己所持有的对象会造成崩溃。
例如自己生成并持有对象后,在释放完后,再次释放。(重复调用release);
或者是在 “取得对象的存在,但是自己不持有对象” 时释放。
都会到导致应用程序崩溃!因此绝对不要释放非自己持有的对象!
以上五项内容,就是 “引用计数式内存管理” 的思考方式。