第三讲:内存管理


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、程序代码区—存放函数体的二进制代码。 栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常 提示栈溢出。 堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的 申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结 点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,由于找到的堆 结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入 空闲链表中。 http://hi.baidu.com/new/yunhuaikong 扯的有点远了,但是懂得上述理论后,对我们为什么要进行内存管理就有一定的 帮助。 切入正题: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,这样会导致过度释放,会导致什么样的后果可想而知。 http://hi.baidu.com/new/yunhuaikong 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]; http://hi.baidu.com/new/yunhuaikong 方法二: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 来释放!否则 会导致内存泄露! http://hi.baidu.com/new/yunhuaikong 容器加载引用: 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 了! Ok,内存方面的问题,基本就这些,关于线程中的内存管理,后续再补充。
还剩4页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 5 金币 [ 分享pdf获得金币 ] 1 人已下载

下载pdf

pdf贡献者

xiaoqi150

贡献于2012-10-17

下载需要 5 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf