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;
}
我们发现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.NSArray的arrayWithObjects:和arrayWithArray:。
2.UIImage的imageNamed:。
3.NSNumber的numberWithBool等。
也就是说这些方法返回的对象,我们可以用,但是还是不能确定得知道她什么时候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资源。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。