iOS内存储器管理之:引用计数、ARC、自动释放池autoreleasepool和便捷方法之间的关系

iOS内存管理之:引用计数、ARC、自动释放池autoreleasepool和便捷方法之间的关系

部分内容摘自《Objective-C基础教程》和互联网

引用计数

       Cocoa采用了引用计数(reference counting)机制,每一个对象有一个关联的“整数retainCount”用于记录对象的使用情况。对象被引用时retaincount+1,外部环境结束对象的使用后retainCount-1.当retaincount为0的时候,该对象被销毁。

        当我们使用alloc、new或者copy的我们需要销毁这个对象。release函数,只是将对象的retainCount值减1,并不是删除对象。当retainCount==0的时候,系统会发给对象一个dealloc消息,另外:千万不要手动调用dealloc,因为我们不知道何时,何地,何人还会使用该对象。应该老老实实依赖引用计数机制完成内存管理。

        释放对象所有权的函数除了release还有autorelease,这是一种延迟操作,下面会详细介绍。


        当我们看到下面代码,

   第一个问题:dateformatter的内存管理,应该很好理解,因为[NSDateFormatter alloc]所以,我们要release它。

-(NSString*) date2String:(NSString*)str

{

    NSString* dateString;

    NSDate *  currentTime=[NSDate date];

    NSDateFormatter  *dateformatter=[[NSDateFormatter alloc] init];

    [dateformatter setDateFormat:str];

    dateString =[dateformatter stringFromDate:currentTime];

    [dateformatter release];

    return dateString;

}

那么,这个函数中的NSString* dateStringNSDate *  currentTime这两个变量,在怎样进行内存管理。还有,局部dateString作为返回值,内存管理又是如何?这是个好问题。

        我们发现dateString的赋值方法是 [ dateformatter stringFromDate:current ] ,显然,它并没有使用alloc、new或copy任何一种。《Objective-C基础教程》上说:假设dateString对象被返回时保留引用计数值为1。呵呵,“假设”俩字。这本书还真是基础教程!

        刚才《Objective-C基础教程》说过,OC里没有栈上对象,没有临时对象。那么这个dateString算是什么?

        那么,现在将他们放到自动释放的范畴,可以这么理解:[ dateformatter stringFromDate:current ] 里面alloc新的对象。这个对象就是autorelease的。

       下面将详细介绍自动释放。

自动释放池autoreleasepool

        自动释放池是NSAutoreleasePool的实例,其中包含了收到autorelease消息的对象。当一个自动释放池自身被销毁(dealloc)时,它会给池中每一个对象发送一个release消息(如果你给一个对象多次发送autorelease消息,那么当自动释放池销毁时,这个对象也会收到同样数目的release消息)。可以看出,一个自动释放的对象,它至少能够存活到自动释放池销毁的时候。

        

        简单的说一个例子,返回局部堆上变量的指针(用c++的口吻说的),那么这个对象如何释放?Objective-C发明了自动释放机制。

-(obj*) foo

{

obj* temp = [[obj alloc]init];

return [ obj autorelease];//只是在返回的时候加上关键字autorelease

}

《Objective-C基础教程》上说:自动释放(autorelease)是一种延迟释放机制,这样保证局部堆上的变量能够被外部正常使用。

但是,系统又是什么时候释放的呢?在每一个事件周期(event cycle)的开始,系统会自动创建一个自动释放池;

        在每一个事件周期的结尾,系统会自动销毁这个自动释放池。一般情况下,你可以理解为:当你的代码在持续运行时,自动释放池是不会被销毁的,这段时间内你也可以安全地使用自动释放的对象;当你的代码运行告一段落,开始等待用户输入(或者其它事件)时,自动释放池就会被释放掉,池中的对象都会收到一个release消息,有的可能会因此被销毁。

        这是很难确定的时间,如果自动释放池的销毁时间过早,那么程序就很危险,这个恐怕很难满足程序员的要求吧。

        自动释放池的缺点:它延缓了对象的释放,在有大量自动释放的对象时,会占用大量内存资源。因此,你需要避免将大量对象自动释放。并且,在以下两种情况下,你需要手动建立并手动销毁掉自动释放池:

1.当你在主线程外开启其它线程时:系统只会在主线程中自动生成并销毁掉自动释放池。

2.当你在短时间内制造了大量自动释放对象时:及时地销毁有助于有效利用iPad上有限地内存资源。


     所以,本人不建议使用autorelease的机制,如果遇到上面例子的情况,使用典型的解决方法吧,外部一个对象负责删除obj对象,防止内存泄露。

Convenience method的内存管理

        与自动释放相关的,有一大类构造方法(constructor method),由它们构造的对象直接就是自动释放的对象;这一类构造方法叫做便捷方法。比如下面这句的字符串就是一个自动释放的对象,stringWithFormat:就是一个便捷方法。

NSString* string = [NSString stringWithFormat:@”autoreleaseString”];

再举几个便捷方法的例子,方便读者以后的开发。

1.NSArrayarrayWithObjects:和arrayWithArray:。

2.UIImageimageNamed:。

3.NSNumbernumberWithBool等。

        也就是说这些方法返回的对象,我们可以用,但是还是不能确定得知道她什么时候dealloc,那么我们能不能主动删除这些“便捷函数”返回的对象呢?如果,程序中有大量的“便捷函数”,这样无疑占用了大量内存空间。

        难道只能避免循环调用这种“便捷函数”?

        现在我们已经解释了,autorelease方法会在一段时间以后释放掉一个对象,在这段时间内我们可以安全地使用该对象。那么这段时间究竟是多久呢?

        上面已经介绍了自动释放的机制,“便捷函数”产生的对象至少能够存活到自动释放池销毁的时候。

ARC(自动引用计数Auto Reference counting)

        上面的文字介绍了“引用计数”这里又来个更高级的自动引用计数。

请参考这篇文章 http://blog.csdn.net/zkdemon/article/details/7446385


/****************************************下面说以下典型的应用****************************?

self.xxx的作用。

    NSInteger i =0;

第一行    _extraMessage = [[FtExportMessage alloc]init];

第二行    //self.extraMessage = [[FtExportMessage alloc]init];

    i = [self.extraMessage retainCount];

    [self.extraMessage release];


你会发现:运行第一行时,retainCount是1,这个好理解。但是不要使用第二行代码,retaincount是2,及时这个时候你调用release也不会删除对象。

初学者容易犯错,什么地方都用self.XXX.


NSArray和NSDictionary的添加元素,内存管理

这种集合类,只是让“元素”的retainCount加1.同样,当NSDictionary release的时候,会将“元素”

- (void)prepareData

{

    _buddyMsg   = [[ExportMsgEntity alloc] init];

    _pgMsg      = [[ExportMsgEntity alloc] init];;

    _dgMsg      = [[ExportMsgEntity alloc] init];

    _msgGroup = [[NSMutableDictionary alloc] initWithObjectsAndKeys:

                     self.buddyMsg,KMsgBuddy,

                     self.pgMsg,KMsgPGGroup,

                     self.dgMsg,KMsgDGGroup,nil];

    int i=0;

    i = self.buddyMsg.retainCount;//此时 i=2

}

上面的代码使self.buddyMsg的retainCount从1加1成为2.那么,当NSDictionary析构后呢,请看下面的情况

- (void)deleteDictionary

{

    [self.msgGroup release];

    int i=0;

    i = self.buddyMsg.retainCount;//此时 i=1

}

上面代码使得self.buddyMsg的retainCount从2减1成为1

总结:使用这些“集合”的时候,不要妄想着“集合”release的时候会自动删除里面的元素。最后还是元素自己release资源。