JHHK

欢迎来到我的个人网站
行者常至 为者常成

自动引用计数(一)

目录

什么是自动引用计数

自动引用计数 ARC (Automatic Reference Counting)是指内存管理中对引用采取自动计数的技术。既让编译器来进行内存管理。
在LLVM编译器中设置ARC为有效状态,就无需再次键入retain和release代码了。

满足以下条件就无需手工输入retain和release代码了。

  • Xcode4.2或以上版本
  • LLVM3.0或以上版本
  • 编译器选项中设置ARC有效

内存管理/引用计数

一、概要

生成对象 持有对象 释放对象 废弃对象

img

二、内存管理的思考方式

1.重要的概念
生成并持有对象 alloc/new/copy/mutableCopy等方法
持有对象 retain
释放对象 release
废弃对象 delloc

负责内存管理的类 img

2.思考方式

  • 自己生成的对象,自己所持有;alloc/new/copy/mutableCopy

alloc

//生成并持有该对象
//指向 生成并持有对象的指针 被赋值给变量 obj 
id obj = [[NSObject alloc] init];

new

//生成并持有该对象
id obj = [NSObject new];

copy/mutableCopy

id obj = [NSObjce new];

//生成并持有对象的副本,mutableCopy本质相同
id obj2 = [obj copy];
  • 非自己生成的对象,自己也能持有;retain
//取得的对象存在,但自己并不持有
id obj = [NSArray array];

//持有该对象
//通过retain方法,非自己生成的对象跟使用alloc/new/copy/mutableCopy方法生成并持有的对象一样
[obj retain];
  • 不再需要自己持有的对象时释放;release
//生成并持有对象的指针 赋值给变量obj
id obj = [[NSObject alloc] init];

//释放持有的对象
//指向对象的指针仍然被保留在obj变量内,貌似能够访问,但一经释放绝不能访问
[obj release];

//清空
obj = nil;
//取得的对象存在,但自己并不持有
id obj = [NSArray array];

//持有该对象
[obj retain];

//释放对象
[obj release];
obj = nil;

如果要用某个方法生成对象,并返回给方法的调用方,源代码该如何实现呢?

-(id)allocObject{
    //生成并持有对象
    id object = [[NSObject alloc] init];
    return object;
}

//调用
//obj 持有对象
id obj = [self allocObject];

类似[NSArray array]方法使得对象存在,但自己不持有对象,又是如何实现的?

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

//调用
id obj = [self object];

//要想持有调用retain方法
[obj retain];

release 与 autorelease 的区别 img

  • 无法释放非自己持有的对象
//生成并持有对象
id obj = [[NSObject alloc] init];
//释放对象
[obj release];

//释放之后再次释放已非自己持有的对象,应用程序崩溃
//访问已经废弃的对象时也同样会崩溃
[obj release];
//取得的对象存在,但并不持有
id obj = [NSArray array];

//释放了非自己持有的对象,程序崩溃
[obj release];

三、alloc/retain/release/delloc 实现(GNUstep实现)

1.alloc实现

img

alloc返回对象的内存图 img

2.retain的实现

获取对象的retainCount img

代码实现 img

3.release代码实现 img

4.delloc方法实现 img

实现总结:
在Object-C的对象中存有引用计数这一整数值。
调用alloc或retain方法后,引用计数加1。
调用release后,引用计数减1。
引用计数为0时,调用delloc方法废弃掉对象。

四、苹果的实现

通过引用计数表管理,该引用计数表是一个散列表,表键值是内存块地址的散列值。

GNU通过内存块头部管理引用计数的好处:
少量代码即可完成;
能够统一管理引用计数用内存块和对象用内存块;

苹果通过引用计数表管理引用计数的好处:
对象用内存块的分布无需考虑内存块头部;
引用计数表各记录中存有内存块地址,可从各个记录追溯到各对象的内存地址。

五、autorelease

autorelease的具体使用方法如下:

  • 生成并持有NSAutoreleasePool对象
  • 调用已分配对象的autorelease实例方法
  • 废弃NSAutoreleasePool对象

示意图如下:

img

代码如下:

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
id obj = [[NSObject alloc] init];
[obj autorelease];

//等同于 [obj release];
[pool drain];               

在cocoa框架中相当于程序的主循环NSRunLoop或者在其它程序可运行的地方,对NSAutoreleasePool对象进行生成、持有、废弃处理。

img

六、autorelease的实现(GNUstep)

autorelease的伪代码实现

//对象调用autorelease
[obj autorelease];
-(void)autorelease{
    [NSAutoreleasePool addObject:self];
}


//NSAutoreleasePool 类内部实现
+(void)addObject:(id)anObj{
    NSAutoreleasePool * pool = 取得正在使用的NSAutoreleasePool对象;
    if (pool != nil) {
        [pool addObject:anObj];
    }else{
        NSLog(@"NSAutoreleasePool对象非存在状态下调用 autorelease");
    }
}

-(void)addObject:(id)anObj{
    NSMutableArray * array = NSAutoreleasePool对象的内部数组;
    [array addObject:anObject];
}

drain的伪代码实现

//废弃自动释放池
[pool drain];

//NSAutoreleasePool内部实现
-(void)drain{
    [self dealloc];
}

-(void)dealloc{
    [self emptyPool];
    [array release];
}

-(void)emptyPool{
    for (id obj in array) {
        [obj release];
    }
}

七、苹果的实现

苹果的实现原理与GNU类似。
在iOS中可以调用以下方法在控制台输出已被autorelease的对象的状态。

[NSAutoreleasePool showPools];

以下代码会发生异常

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

//运行时出错
//在Foundation框架时,无论调用哪个对象的 autorelease方法实际上调用的都是NSObject对象的autorelease方法。但对于NSAutoreleasePool类,autorelease方法已被该类重载,因此运行时会出错。
[pool autorelease];

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.