ios 内存管理

本文转自:http://hi.baidu.com/new/yunhuaikong

不管是做c 还是c++,都涉及到内存管理问题,尤其是小内存设备,内存显的弥足珍贵!所以内存管理显的非常重要。只可惜我们的类c 语言objc 没有自动回收机制。虽然现在加了arc 来管理内存,但是个人认为,永远也没有手动管理来的方便快捷有效。在计算机领域,堆栈是一个不容忽视的概念,但是很多人甚至是计算机专业的人也没有明确堆栈其实是两种数据结构。堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。


要点:
堆,顺序随意。
栈,后进先出(Last-In/First-Out)。
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。


栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。扯的有点远了,但是懂得上述理论后,对我们为什么要进行内存管理就有一定的帮助。


切入正题:ios 开发,内存管理!!!
一般我们初始化某一实例都需要手动alloc 方法,这样我们可以从内存中申请到我们需要用到的大小的内存!通过相应的初始化方法,一般以init 开头命名,我们就可以得到一个实例,这个实例就指向了这块内存!方便了我们在这块内存上的工作。但是当我们用完这块内存后,我们不能对其视而不见,任其存在在内存中。这样会产生很多的碎块区,垃圾内存等。严重降低了应用的运行和质量!在ios 开发中,一般没有像c 中的那free 或者是类似的方法立即释放内存!

苹果引入了新的内存管理机制——-引用计数器retainCount!
- (NSUInteger)retainCount
当一个实例被引用时,对应的计数器就会加+1,表明此块内存有几个指针指向它,当没有指针指向它的时候,就说明这块内存不再被使用,应该被回收了。系统将得到命令将此处内存释放!alloc 一retainCount+1,当我们使用完后,就需要手动的release!release 一次,retainCount 将-1,这样,就成了配合的关系。就很好的管理好了此内存。当retainCount 为0 时,此内存被释放!
- (oneway void)release;
所以有人现在总结出来了,很幽默的口诀:谁申请谁结束,谁污染,谁治理。
举一手动管理的例子:
NSMutableArray *follow_array = [[NSMutableArray alloc] initWithCapacity:0];
//使用
[follow_array release];
以上便为手动释放的例子。
再来看看
- (id)autorelease
从其名称中就能看到这是自动释放的意思,如果一个实例被标明为autorelease 方式,那就严禁再在后面调用release,这样会导致过度释放,会导致什么样的后果可想而知。
NSMutableArray *follow_array = [[[NSMutableArray alloc] initWithCapacity:0] autorelease];
那么autorelease究竟是什么时候被释放?首先可以告诉你,没有时间点!可能是立即,也可能是过了几s,也可能是过了几个小时!从这里就可以看出,
如果一个实例被标识为autorelease多危险!其实,这个的释放时间,要看此时的runloop执行多久,当然没有几个小时那么夸张!但是,只要是内存不能被立即
释放,肯定会或多或少的导致内存使用不合理,内存累积严重!所以,个人推荐不这么用!

看看下面的代码,你发现了什么问题:
NSMutableArray *follow_array = [[NSMutableArray alloc] initWithCapacity:0];
int index = -20000;
while (index < 20000)
{
NSString *text = [[[NSString alloc] init] autorelease];
[follow_array addObject:text];
index++;
}
//这里做其他的事情,并且做的事情很多。
/**/
//做完事情后,我们不用follow_array了,要释放它了。
[follow_array release];
好,我们现在来分析一下这些代码!假设此代码在一个线程中,表明退出while的时候runloop 还没有结束。我们当然希望在从while 中出来的时候2*20000个NSString 对应的内存应该已经被正式的释放!可是我告诉你,没有,它竟然还在内存中驻留!这个问题很严重!如果项目中线程过多,应用牵扯到的功能很复杂,那就要“恭喜”你了,你的应用迟早会挂掉,而你竟然只收到了内存警告,却不知道是哪里出错了!这就是autorelease 给我们带来的弊端!当然如果不是20000×2 个,而只是一个,那影响可以忽略不计!针对如上情况,如何解决?


方法一:
NSMutableArray *follow_array = [[NSMutableArray alloc] initWithCapacity:0];
int index = -20000;
while (index < 20000)
{
NSString *text = [[NSString alloc] init];
[follow_array addObject:text];
[text release];
index++;
}
//这里做其他的事情,并且做的事情很多。
/**/
//做完事情后,我们不用follow_array了,要释放它了。
[follow_array release];

方法二:NSAutoreleasePool
NSMutableArray *follow_array = [[NSMutableArray alloc] initWithCapacity:0];
int index = -20000;
while (index < 20000)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *text = [[[NSString alloc] init] autorelease];
[follow_array addObject:text];
[pool release];
index++;
}
下面我们讲讲NSAutoreleasePool:自动释放内存池
@interface NSAutoreleasePool : NSObject
{
@private
void *_token;
void *_reserved3;
void *_reserved2;
void *_reserved;
}
+ (void)addObject:(id)anObject;
- (void)addObject:(id)anObject;
- (void)drain;
@end
如果在NSAutoreleasePool 实例初始化和release 的代码段间有autorelease的对象,那么这个对象将被加到NSAutoreleasePool 实例的数组中,当NSAutoreleasePool 实例被release 或者是drain 的时候,此数组中的实例同时也会被遍历一次,将存在的实例的引用计数器-1!类似于手动管理了!当被遍历的实例的引用计数器为0,此实例将会被释放 ,内存将会被回收!注意,我说的是代码段间的autorelease。如果你的标识autorelease 在外面,那么不好意思,它没有这个管理权限!所以个人推荐在出现for,while 或者是线程的地方,最好放置一个NSAutoreleasePool,这样你将不会为垃圾内存的存在而影响应用的性能和内存暴增的问题。


那么哪些方法可以导致引用计数器增加呢?
• alloc –为对象分配内存, 对象引用计数器加一。
• copy – 拷贝一个对象,返回新对象,新对象引用计数器加一。原对象引用计数引用计数器不变。
• retain – 引用计数器器加一, 获得对象的所有权。只要是为某一个实例进行了如上操作,那你就必须调用release 来释放!否则会导致内存泄露!

容器加载引用:
NSArray, NSDictionary, NSSet等容器,将一个实例加入其中,获取它的引用权限,都会将原对象的引用计数器加1!如下:
NSMutableArray *object_array = [[NSMutableArray alloc] initWithCapacity:0];
UserDesObject *user_object = [[UserDesObject alloc] init];
[user_object setUser_id:comment_user_id];
[user_object setUser_ture_name:user_ture_name];
[user_object setUser_nick_name:user_nick_name];
[user_object setUser_description:comment];
[user_object setUser_url:user_url];
[object_array addObject:user_object];// user_object引用计数器会被加1
[user_object release];
如果后面不进行[user_object release];这一步,那么将会导致内存泄露!因为此时已经被加1了,如果创建的时候没有对应的release,那么当数组[object_array release];被释放的时候此实例的引用计数器为2-1=1,不会被释放!但是网上有人说是[user_object release];不用,应该注释掉,纯粹是扯淡!

还有一个问题需要注意一下,就是调用某一个方法返回一个实例,以获取此实例的使用权!
- (NSString*)get_time_with_timeinterval:(double)time_interval
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSDate *date = [NSDate dateWithTimeIntervalSince1970:time_interval/1000.0];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:kCFDateFormatterMediumStyle];
[formatter setTimeStyle:kCFDateFormatterShortStyle];
[formatter setDateFormat:@"YYYY-MM-dd-hh-mm-ss"];
NSString *string_time = [formatter stringFromDate:date];
[formatter release];
NSArray *time = [string_time componentsSeparatedByString:@"-"];
int value_year = [[time objectAtIndex:0]intValue];
int value_month = [[time objectAtIndex:1]intValue];
int value_day = [[time objectAtIndex:2]intValue];
NSString *system_time = [[NSString alloc]
initWithFormat:@"%d-%d-%d",value_year,value_month,value_day];
[pool release];
return [system_time autorelease];
}
此方法返回了一个autorelease 对象,在外部调用的时候,它也是autorelease 的,不需要release!


外面调用:
NSString *time = [self get_time_with_timeinterval:[create_time doubleValue]];只要用就行了。但是如果写成这样的:
NSString *time = [[self get_time_with_timeinterval:[create_time doubleValue]]retain];那使用完成后就需要release 了!

人生只有走出来的美丽,没有登出来的辉煌!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值