内存管理(一) - 引用计数式的理解

CSDN地址

有道地址

概要


OC中的内存管理,也就是引用计数。可以用开关灯房间的案例来说明:

假设办公室照明设备只有一个。上班的人进入办公室需要照明,所以把灯打开。而队对于下班离开办公室的人来说,已经不需要照明了,所以把等关掉。若是很多上下班,每个人都开灯或者关灯,那么办公室的情况又将如何呢?最早下班的人关灯,那么办公室岂不是一片黑暗。

解决这个问题的办法是使办公室还有至少一个人的情况下保持开灯状态,无人的时候保持关状态。
1. 最早进入办公室开灯
2. 之后进入办公室的人,需要照明
3. 下班离开办公室的人,不需要照明
4. 最后离开办公室的人关灯(此时已无人需要照明)

根据计数功能来计算 ”需要照明的人数”

  1. 第一个人进入办公室,办公室的计数为1。计数:1
  2. 之后进入办公室的人,叠加1。计数:2
  3. 每当人有人下班离开,减去1。计数:1
  4. 最后离开办公室,减去1。计数:0,因此要关灯。

在OC中,”对象” 相当于办公室的照明设备。

  1. 开灯 –> 生成对象
  2. 需要照明 –> 持有对象
  3. 不再需要照明 –> 释放对象
  4. 关灯 –> 废弃对象

内存管理的思考方式


  • 自己生成并持有对象。
  • 持有非自己生成的对象。
  • 不再需要自己持有的对象时释放。
  • 非自己持有的对象无法释放。
对象操作 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 方法进行释放。

现在我们通过源码来分析:为什么会有自己生成并持有非自己生成也能持有 :

  1. 通过模拟 alloc/new/copy/mutableCopy 方法, 理解 自己生成并持有对象
+ (NSObject *) allocObject {
    //自己生成并持有对象
    id obj = [[NSObject alloc] init];
    //自己持有对象
    retain obj;
}

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

<!--取得非自己生成并持有的对象-->
id obj1 = [NSObject allocObject];

allocObject 与用 alloc方法生成并持有对象的情况完全相同,所有使用allocObject方法也就意味着 自己生成并持有对象

  1. 通过模拟 [NSMutableArray array] 方法, 理解 非自己生成并持有对象

前面就有说到调用 [NSMutableArray array] 方法使取得的对象存在,但是自己又不持有对象,又是怎么实现的呢?

- (id)object {

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

    //使对象在超出指定的生成范围时能够自动并正确地的释放
    [obj autorelease];

    //返回对象
    return obj;
}

上例中,我们使用了 autorelease 方法。用该方法,可以使取得的对象存在,但是自己不持有对象。

这样的好处就是,我们可以不用去手动去管理通过此方法 生成的对象 的释放。

autorelease 提供这样的功能,是对象在超出指定的生成范围时能够自动并正确地释放(调用release方法)

release 和 autorelease 的区别

看完这张 release 和 autorelease 图:

相信大家已经明白为什么通过objectarray方法生成的是非自己生成,是因为生成在生成这个对象的方法里面,添加延迟释放处理,而不需要我们再去调用release释放了。

那么我们要如何去区分到底谁需要调用release,谁不需要调用release。所以引入了命名规则:

  • 使用NSMutableArray类的array类方法等可以取得谁都不持有的对象,这些方法都是通过 autorelease 而实现的
  • alloc/new/copy/mutableCopy 方面名开头的,就是 自己生成并持有的对象

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

alloc/new/copy/mutableCopy 方法生成 自己生成并持有的对象,或者用 retain 方法持有的对象,一旦不再需要,务必要用 release 方法进行释放。

而由此以外所得到的对象绝对不能释放。倘若在应用程序中释放了非自己所持有的对象会造成崩溃。

例如自己生成并持有对象后,在释放完后,再次释放。(重复调用release);

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

都会到导致应用程序崩溃!因此绝对不要释放非自己持有的对象!

以上五项内容,就是 “引用计数式内存管理” 的思考方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章