引用计数

引用计数规则

自己生成的对象 自己持有

使用以下名称开头的方法名,意味自己生成的对象只有自己持有:

  • alloc
  • new
  • copy
  • mutableCopy
id obj = [[NSObject alloc] init]
id obj = [NSObject new]

自己生成并持有对象,指向生成并持有对象的指针被复制给变量obj

非自己生成的对象 自己也能持有

用上面👆所述的几个方法之外取得的对象,因为是非自己生成并持有的,所以自己并不是对象的持有者

//取得的对象存在 但是自己并不持有该对象
id obj = [NSMutableArray array]

//自己持有该对象
[obj  retain]

通过retain方法 非自己生成的对象跟用alloc/new/copy/mutablecopy一样,称为自己持有的对象

不再需要自己持有的对象时释放

自己持有的对象,一旦不再需要,持有者有义务释放该对象,释放使用release方法

  1. 自己生成并持有对象 自己释放

    //自己生成并持有对象
    id obj = [[NSObject alloc] init]
    //释放对象
    [obj release]
    /*
    * 释放对象
    * 指向对象的指针仍然被保留在变量obj中,貌似能够访问
    * 但对象一经释放绝对不可访问
    */
  2. 非自己生成并持有的对象 也可以通过release释放

    //取的对象 但是自己并不持有
    id obj = [[NSObject alloc] init]
    //自己持有对象
    [obj retain]
    //释放对象
    [obj release]

    用retian方法持有的对象 一旦不再寻妖 务必用release方法进行释放

  3. 调用alloc/new/copy/mutablecopy开头方法生成对象,并将其返回该方法调用方

    -(id)allocObject {
        //自己生成并且持有对象
    id obj = [[NSObject alloc] init]
    return obj
    }
    //自己持有对象
    id obj1 = [obj0 allocObject]

    原封不动的返回alloc方法生成并且持有的对象 就能让调用方也持有该对象
    allocObject是符合前面的命名规则的 因此调用该方法也能自己生成并且持有该对象

  4. 调用非alloc/new/copy/mutablecopy开头方法生成对象

    此类型的方法 就是我们常用的[NSArray array]方法类似

    -(id)object {
    //自己生成并持有对象
    id obj = [[NSObject alloc] init]
    //取得对象存在 但自己不持有对象
    [obj autorelease]
    //返回对象
    return obj
    }
    //取得的对象存在 但是自己不持有对象
    id obj1 = [obj0 object]
    //自己持有对象
    [obj1 retain]

    使用autorelease方法,可以取得对象存在,但自己不持有对象。
    autorelease,使对象在超出指定生存范围时 能自动并且正确释放

    注意

    这些谁都不持有的对象的方法名不能以alloc/new/copy/mutablecopy开头

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

alloc/new/copy/mutablecopy开头获得的对象 或者 retain方法持有的对象 持有者是自己
当持有者是自己时 在不需要的时候将其释放 除此之外,得到的而对象绝对不能释放

alloc/retain/release/dealloc实现过程

GUNStep中的实现方式

用alloc类方法初始化对象时,初始化开辟内存大小为对象大小加上一个 记录引用计数的结构体大小的空间。

用该结构体中的retained整数来保存引用计数,并将其写到对象内存头部。通过对该结构体中的数值进行操作,来获取、增加、减少引用数值

  • Object-C对象存有引用计数这一数值
  • 调用alloc方法或retain方法 引用计数加1
  • 调用release后 引用计数减1
  • 引用计数为0 调用dealloc方法废弃该对象

苹果的实现

NSObject类方法上设置断点,列出alloc方法执行的函数

+alloc
+allocWithZone
class_creatInstance
calloc

alloc方法先调用+allocWithZone方法,然后调用运行时中的class_createInstance函数,最后调用calloc来分配内存块

retaincount/retain/release操作的实现 apple是维护了以个散列表(引用计数表)来管理引用计数
散列表中 键值为内存块地址的散列值

autorelease

autorelease像C语言的自动变量那样来对待对象实例,当超出作用域,对象实例release方法被调用,编程人员可以设定变量的作用域

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]
id obj = [[NSObject alloc] init]
[obj autorelease]
//等效于执行了 [obj release]
[pool drain]

在Cocoa框架中,相当于程序主循环的NSRunLoop或者在其他程序可运行的地方,对NSAutoreleasePool对象进行生成,持有和废弃处理。因此,程序开发者不一定非要使用NSAutoreleasePool对象进行开发处理工作

注意:

虽然runloop会自动废弃NSRunloop对象,但是在大量产生对象时 也会产生内存不足的现象,需要在使用完后,手动及时释放对象

在cocoa框架中,很多类方法 用于返回autorelease对象

id array = [NSMutableArray arrayWithCapacity:1];
//相当于以下源代码
id array = [[NSMutableArray arrayWithCapacity:1] autorelease];

注意

在OC中 也就是Foundation框架时,无论调用那一个对象的autorelease方法,实现上是调用NSObject类的autorelease实例方法。但是对于NSAutoreleasePool类,该方法已经被重载,因此运行时会报错

autorelease的实现