您的位置:首页 > 移动开发 > Objective-C

Objective-C内存管理之引用计数

2016-01-07 15:48 549 查看
Apple在iOS5之后引入了自动ARC技术,让编译器来自动来管理内存,开发者从此不用再去写很多retain、release代码了。ARC下,编译器完全清楚目标对象,并且能够自动释放那些需要释放的对象,因此,程序内存泄漏的风险大大降低了,但!ARC!不!是!万!能!的!

在实际开发中,内存泄漏还是比较常见的,比如使用block时候的不注意,导致的循环引用;比如在使用NSTimer实例的时候,timer的action事件执行者也就是Target会强引用Self,如果timer没有正确的销毁,就会造成内存泄漏等等;这个时候我们就需要来手动管理内存了。因此,很有必要知道Objective -C的内存管理机制。

二、Objective-C内存管理是机制,就是引用计数。

正确客观的思考方式:

1)自己生成的对象,自己持有。

2)非自己生成的对象,自己也能持有。

3)自己持有的对象,一旦不再需要,就需要立即释放。

4)非自己持有的对象,自己无法释放。

这里的“自己”,知道是当前的对象的使用环境,也可以理解为程序猿自己啦。

问题一:

什么情况下自己生成的对象,自己持有?

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

a)alloc

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


使用类的alloc方法就能生成一个对象,在这里,指向生成并持有的对象的指针被赋值给变量obj。

b ) new

//    自己生成并持有对象
id obj2 = [NSObject new];


在Objective-C中,[[NSObject alloc] init]和[NSObject new]是完全一致的。

c)copy

d )mutableCopy

copy和mutableCopy都是生成并持有对象的一份副本,虽然持有的是副本,但是,副本是自己生成的。

另外,除以上的几个,以下名称开头的方法也意味着自己生成并持有。

(1)allocMyObject

(2)newThatObject

(3)copyThis

(4)mutableCopyYourObject

但是对于一下名称,即使用alloc/new/copy/mutableCopy名称开头,并不属于同一类方法。

(1)allocate

(2)newer

(3)copying

(4)mutableCopy

问题二:

什么情况下自己非自己生成的对象,自己也能持有?

用alloc/new/copy/mutableCopy等开头以外的方法取得的对象,因为不是自己生成的对象,所以并不持有这个对象。比如一些类方法。

//    取得非自己生成的对象,自己并不持有

id array = [NSMutableArray array];

//    如果要持有对象,那么就向对象发送retain消息
[array retain];


取到一个非自己生成的对象,但是自己可以持有对象的实现如下,向对象发送autorelease消息,告诉系统这个对象需要被引用,延缓释放:

- (id) object{

id obj = [[NSObject alloc] init];
[obj autorelease]; //obj 放入到NSAutoreleasePool中,延迟释放。
//取得的对象存在,但是自己不持有对象
return obj;
}


其实,由类方法创建的对象都会放入到NSAutoreleasePool中。后面会继续讲。

问题三:

如何释放自己持有的对象?

向自己持有的对象发送release消息。

//      自己生成并持有
NSArray *arr1 = @[@"11",@"22"];

// 释放自己生成且持有的对象
[arr1 release];

// 取得非自己生成的对象,自己并不持有 id array = [NSMutableArray array]; // 如果要持有对象,那么就向对象发送retain消息 [array retain];

// 释放自己持有的对象
[array release];


这里有个需要弄明白的:

1)释放 !== 销毁

一个对象可以有多个持有者,且持有者只能向自己持有的对象发送release消息,由引用计数器原则,当一个对象的retainCount == 0,这个对象才会被销毁。多个持有者时,每次release都会使对象的retainCount - 1。

实例如下代码:

//    创建并持有一个对象。
id obj = [NSObject new];
NSLog(@" create new obj  === %lu ===",[obj retainCount]);//retainCount=1

NSMutableArray *mutableArray = [NSMutableArray array];
//    数组对象持有 对象 obj
[mutableArray addObject:obj];
NSLog(@" mutableArray have obj === %lu ===",[obj retainCount]); //retainCount=2

id dict = [NSMutableDictionary dictionary];
//    字典对象持有 NSObject对象 obj
[dict setObject:obj forKey:@"oc"];
NSLog(@" dict have obj === %lu ===",[obj retainCount]);//retainCount=3

//    字典对象释放
[dict release];
NSLog(@" dic release === %lu ===",[obj retainCount]);//retainCount=2

//    数组对象释放
[mutableArray release];
NSLog(@" mutableArray release === %lu ===",[obj retainCount]);//retainCount=1

//    obj对象释放
[obj release];
NSLog(@" obj release === %lu ===",[obj retainCount]);//retainCount=1


所以,释放跟销毁不是一个概念,它们并不等价。

2)赋值 !== 持有

下面这句代码,NSMutableArray类对象被赋给变量obj,但变量obj自己并不持有改对象。所以,赋值并不等价于就是持有。

id array = [NSMutableArray array];


问题四:

对于“非自己持有的对象,自己无法释放“这句话的解释。

其实就是被释放或被销毁的对象不能重复被引用。

//    创建并持有一个对象。
id obj = [NSObject new];
NSLog(@" create new obj  === %lu ===",[obj retainCount]);

//    obj对象释放
[obj release];
NSLog(@" obj release === %lu ===",[obj retainCount]);

//    obj对象释放
[obj release];
NSLog(@" obj release === %lu ===",[obj retainCount]);


第二次释放的时候程序会崩溃。

二、alloc/retain/release/dealloc

关于alloc/retain/release/dealloc的实现可参看GNUstep,亦可以查阅 《Objective - C高级编程》这本书上的讲解。这里就不赘述了。

三、引用计数的实现

这一块儿理解的不是很好,大致的是这么回事儿:alloc类方法首先调用allocWithZone:类方法,然后调用create_Instance函数,然后通过调用calloc方法来分配内存。Apple内部使用一个引用计数表来管理所有对象的引用计数,同时,引用计数表还记录着每个对象的内存块地址。每次调用release方法都会去技术表中查询对象的引用计数的值。

具体总结如下:

(1)在Objective-C这一对象中存在引用计数器这一整数值。

(2)调用alloc 或者retain方法后,引用计数值会加1.

(3)调用release后,引用计数值减1.

(4)引用计数值为0时,调用dealloc方法废弃对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息