ios一点内存管理.doc_第1页
ios一点内存管理.doc_第2页
ios一点内存管理.doc_第3页
ios一点内存管理.doc_第4页
ios一点内存管理.doc_第5页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

iPhone相机和内存警告现在我们越来越习惯于在程序中使用相机。但是,几乎在程序每次打开相机的瞬间,我们都会收到一个“Received memory warning. Level=1”内存警告 。对于iOS来说,内存永远是稀缺资源 ,因此,在你使用iPhone的高分辨率相机时,尤其需要小心。程序员应当重视内存警告并对之进行处理,包括:一、在viewDidUnload方法中释放内存从iOS3.0开始, 释放内存的代码didReceiveMemoryWarning 迁移到了viewDidUnload中,我们不用覆盖didReceiveMemoryWarning方法。 事实上有不止地方会收到内存警告,因此程序中会有两个地方存在 didReceiveMemoryWarning方法:AppDelegate 和ViewController。一般,我们选择在ViewController而不是AppDelegate中处理内存警告。当程序收到内存警告时,程序员们就必须注意了。iOS随后会自动清理当前“无用的”内存,比如内存中那些不处于顶层的ViewController和视图。我们一般需要在viewDidUnload方法中,释放视图中无用的对象,比如UILabel、UIButton、NSArray等:-(void)viewDidUnloadsuper viewDidUnload;if(ivBg)ivBg release,ivBg=nil;if(btSend)btSendrelease,btSend=nil;if(btUpload)btUploadrelease,btUpload=nil;if(vwBody)vwBodyrelease,vwBody=nil;if(backButton)backButtonrelease,backButton=nil;if(indicator)indicatorrelease,indicator=nil;if(imagePicker)imagePickerrelease,imagePicker=nil;if(receiverVC)receiverVCrelease,receiverVC=nil;注意,我们释放的对象必须是“无用的”。这些东西可能是任何对象,比如成员对象和UI对象。关键在于怎样认识一个对象是“有用的”还是“无用的”。实际上,对于iOS来说,任何在viewDidUnload方法中释放的东西都是“无用的”。如果你有任何对象在恢复视图时会用到,那么就不要在viewDidUnload方法中释放。例如,用户在视图中的输入一封邮件的正文,或者用户正在编辑的图片这些东西将在当相机使用完毕,iOS准备恢复视图时显示给用户。如果你确实不得不节省出更多的内存,那么你可以在viewDidUnload中把一些有用的东西也释放掉,前提是,当你收到内存警告时,把这些对象持久化(保存到文件)。而有的对象,它们本来就在xib文件中存在。在iOS恢复视图时,这些东西会从xib中恢复到内存(initWithNibName-viewDidLoad)。比如按钮、图片、静态标签等UI对象。这些对象我们就可以看作是“无用的”,在viewDidUnload方法中可以毫不客气地把它们统统释放。二、在 didReceiveMemoryWarning 方法中设置内存警告标志现在,虽然我们已经在viewDidUnload方法中,而不用在 didReceiveMemoryWarning方法中释放对象,但didReceiveMemoryWarning方法仍然有一个用途,就是设置内存警告变量,以让程序员知道何时收到内存警告。首先声明一个BOOL成员作为是否收到过内存警告的标志:BOOLmaybeSetViewNil;然后在didReceiveMemoryWarning方法中:maybeSetViewNil=YES;三、在viewDidUnload方法中,保存视图数据以便恢复在第一步中,我们提到为了“尽可能地”为iOS腾出内存,我们可以把所有对象释放,但对于“有用的”的对象,我们应该采用必要的保存策略,比如保存到文件缓存中。仍然在viewDidUnload方法中,加入以下代码:NSMutableDictionary*d=NSMutableDictionary allocinit;/in use objectsif(tfTitle)if(tfTitle.text) d setObject:tfTitle.text forKey:tfTitle.text;self.tfTitle=nil;if(lbAttach)if(lbAttach.text) d setObject:lbAttach.text forKey:lbAttach.text;self.lbAttach=nil;if(imageView)if(imageView.image) NSData* data=UIImageJPEGRepresentation(imageView.image,0.7);if (data)d setObject:data forKey:imageView.image;self.imageView=nil;if(receivers)dsetObject:receivers forKey:receivers;receivers release,receivers=nil;if(selectedPeople)dsetObject:selectedPeople forKey:selectedPeople;selectedPeople release,selectedPeople=nil;VCCachesaveToCache:d toVC:self;d release;可以看到,我们把所有“有用的”对象放到了Dictionary集合中。CCache是一个自定义类,我用它把Dictionary保存到指定文件。四、恢复视图状态接下来我们可以在viewDidLoad方法中恢复视图状态了:selectedPeople=NSMutableArrayallocinit;receivers=NSMutableStringallocinit;if (maybeSetViewNil) NSLog(maybe set view nil!);NSDictionary* d=VCCache loadCache:self;if(d!=nil) / status restoreid obj=d objectForKey:tfTitle.text;if(obj!=nil)tfTitle.text=(NSString*)obj;obj=d objectForKey:lbAttach.text;if(obj!=nil)lbAttach.text=(NSString*)obj;obj=d objectForKey:imageView.image;if(obj!=nil)imageView.image=UIImage imageWithData:(NSData*)obj;obj=d objectForKey:receivers;if(obj!=nil)receivers setString:(NSString*)obj;obj=d objectForKey:selectedPeople;if(obj!=nil)selectedPeople setArray:(NSArray*)obj;maybeSetViewNil=NO;注意,receivers和selectedPeople对象要在viewDidLoad方法中初始化,而不能在initWithNibName方法中初始化,否则对一个nil对象赋值是无效的。因为iOS在恢复视图时从loadView方法开始调用,而不是从initWithNibName方法(参考前面的图)。OC内存管理在视图控制器之间传递参数时尤其需要注意“野指针”的问题。因为程序总是在一个个视图控制器之间跳动,一些视图控制器所拥有的变量(跟UI相关),当视图跳转后,会被释放(出于节约iphone内存的考虑?)。比如这样的代码:在一个ViewController中:RecipientRoll* controller=RecipientRoll allocinitWithNibName:RecipientRoll msgText:tvBill.text expenseid:expense_id; self presentModalViewController:controller animated:YES;注意红色字体部分,向另一个ViewController传递了一个字符串参数。但这个参数引用了本ViewController的UI控件属性。随后以模式窗体的方式弹出另一个ViewController,此时第一个ViewController的UI会被释放。于是你传递的那个字符串内存被释放了,它的生命周期只有构造时候的短暂一小段时间,当模式窗体显示后它被释放了。如果你在此后还想用它做些什么,程序崩溃了,Xcode报告EXEC BAD。因此我们需要利用它短暂的生命周期时间,对字符串做一个复制。在构造函数中这样写:-(id)initWithNibName:(NSString *)nibNameOrNil msgText:(NSString*)d expenseid:(int)idx / NSLog(initWithNibName); if (self=super initWithNibName:nibNameOrNil bundle:nil) expense_id=idx; msgText=NSString allocinitWithString:d;/而不要这样写:msgText=d return self;这样,你可以放心地在这个ViewController被释放之前使用msgText变量了。iPhone开发中内存的合理使用 iPhone 开发过程中,内存的使用至关重要。不但要合理分配使用内存,还要注意内存泄露的问题, 因为内存泄露会导致程序由于内存不足而崩溃。根据个人开发的经验来看,在开发iPhone程序的过程中,关于内存的问题需要注意以下几点:1 内存分配、释放成对出现使用 alloc 分配的内存对象需要在用完后 调用release释放2 注意copy,retain,assign操作符的区别copy, retain操作符赋值的对象和alloc一样,需要release释放,否则会导致内存泄露assign 操作符的含义是将对象指向另一对象, 两者指向的是同一内存对象,无需调用release释放3 NSArray, NSDictionary, NSMutableArray, NSMutableDictionary等容器类, 在使用这些容器类的时候要注意, 在添加对象到这些类对象时,容器类会自动调用一次retain,比如NSString* string = NSString alloc initWithString:”test string”; / refCount = 1NSArray* array = NSArray array;array addObject:string; / refCount = 2string release; / refCount = 1这种情况, 即便string已经调用release,但是在加入 array中时已经调用了一次retain,注意refCount的变化简单介绍一下iPhone 或者说Objective C对对象的管理机制。 OC中采用一种引用计数refCount的方式来管理内存对象,当refCount等于0的时候就会释放对象所占的内存, 操作符alloc,copy, retain都会将refCount加1表示引用计数增加, 而调用release使 refCount自动减1, 当refCount=0时表示该对象已经没有被引用,可以将其释放, 之后该对象便不可用4 连续重复分配内存的过程最好创建自己的自动释放池 NSAutoreleasePool,通常是在for、while等循环操作过程中,比如for( int i=0; i 100; i+ )NSString* str = NSString alloc initWithString:”some string”;/ 针对str的操作str release;在这种情况下,有2点需要注意,首先如果可能,就把str的分配、释放放在for循环外面, 从而减少内存的分配、释放导致程序效率低下,也利于内存回收,如上例应该为NSString* str = NSString alloc initWithString:”some string”;for( int i=0; i 100; i+ )/ 针对str的操作str release;如果实际情况复杂,不能像例子中那样抽离出循环外,需要创建自己的内存管理池, 同样适用于需要大量autorelease对象的过程NSAutoreleasePool * pool = NSAutoreleasePool alloc init;for(int i=0; i 100; i+ )/ actionspool release;之所以要这样做,是因为apple处理iPhone的内存管理机制问题, 通常情况下,系统会在需要的时候释放整理所有的autorelease对象,这就是为什么有时候autorelease对象在作用域范围外还有可能是有效的5 避免不常用对象驻留内存, 桌面开发的tx很多喜欢在程序初始化的时候将某些资源比如小图片加载进内存,从而提高程序运行效率。 但这种方式在iPhone以及其它mobile移动设备开发时需要避免,因为对于这些设备来说,内存永远显得不足(当然普通pc内存也是越大越好:) )。 按照apple的官方说法, Load resources lazily . 就是在需要的时候再从硬盘上读取,而避免常驻内存。iOS开发是否应该使用ARC?和身边做iOS开发的同事组建了一个QQ群,每隔一段时间,大家就会讨论是否应该使用ARC。所以我觉得有必要将这些讨论分享出来,让大家消除对于ARC的疑虑。关于ARC的介绍文章网上已经很多,苹果的官方文档也不少。担心使用ARC会带来问题的同学主要的理由有以下5点:1.担心这个技术方案不靠谱。苹果大多数时候的技术方案都是比较靠谱的,但也有一些技术方案有很多坑,例如storyboard。关于storyboard的问题可以参看我的这篇文章。2.原有的项目在非ARC环境下运行良好,担心迁移成本或引入新的问题。3.苹果以前手工管理内存需要非常小心,稍微不注意应用程序就崩溃了。有过这段经历的iOS开发老手,心里上还是觉得自己手工管理内存更踏实一些。4.使用ARC需要了解ARC的一些细节,还需要引入_bridge等新的关键字,学习成本还是有的。5.以为ARC只能支持iOS5.0以上(这是非常大的误解)。对于上面提到5点问题,我认为相应的回答如下:1.ARC是WWDC2011大会时提出的技术,离现在已经快2年了,而且苹果现在将MacOS上的垃圾回收机制废弃(Deprecated),采用ARC替代,无疑证明了ARC是成熟的了。2.确实有一些迁移成本,但苹果在Xcode中专门集成了迁移工具,成本已经非常小了。如下图就是Xcode集成的将非ARC工程转换成ARC工程的工具。另外,为了兼容第三方的非ARC开源库,你也可以在工程中随意使用编译参数:-fno-objc-arc ,这个参数允许对部分文件关闭ARC。3.手工管理内存虽然踏实,但是泄露很容易发生。常常开发完成后,需要使用Instruments来检测泄露。但用了ARC后,基本不会出现泄露了,我在开发粉笔网iPhone客户端时,由于使用了ARC,花三个月开发完的应用,用instruments检测后,没有发现任何内存泄漏问题。这在没有使用ARC的工程中是不可想象的。4.确实有学习成本。但是非常值得学习,能省不少开发精力。5.虽然ARC是与iOS5一同推出,但是由于ARC的实现机制是在编译期完成,所以使用ARC之后App仍然可以支持iOS4.3。稍微需要注意的是,如果要在ARC开启的情况下支持iOS4.3,需要将weak关键字换成 _unsafe_unretained,另外还有一些细节需要处理,在这里我就不展开说了。所以,希望大家都能在项目中使用ARC,一旦你感受到它带来的好处,你就离不开它了。它也能让你从繁琐的内存管理代码中解放出来,将精力更多关注于代码结构、设计模式而不是底层的内存管理。Objective-C 内存管理的几点总结在 172 页,对 Objective-C 的内存管理做了以下说明:1 当你使用 new、alloc 或 copy 创建对象时,对象的 count retain 到 1。你一定要负责把这个对象 release 或 autolease 掉。这样当它的生命周期结束时,它才能清空。When you create an object using new, alloc, or copy, the object has a retain count of 1. You are responsible for sending the object a release or autorelease message when youre done with it. That way, it gets cleaned up when its useful life is over.2 当你使用其他方法获得一个对象时,你可以认为它已经 retain 了一个 count,并且 autolease 掉了。你不用考虑和它相关的清理问题。但是如果你想保留这个对象,那么你需要 retain 它,并且要确保之后你 release 了这个对象。When you get hold of an object via any other mechanism, assume it has a retain count of 1 and that it has already been autoreleased. You dont need to do any fur- ther work to make sure it gets cleaned up. If youre going to hang on to the object for any length of time, retain it and make sure to release it when youre done.3 如果你 retain 一个对象,你最终总是需要 release 或者 autolease 它。If you retain an object, you need to (eventually) release or autorelease it. Balance these retains and releases. 这三条规则在写代码的时候一定要遵守,一旦遵守了一般也就不会有内存泄露的问题。创建对象 Objective-C 中创建对象分为 alloc 和 init 两步,alloc 是在堆(heap)上初始化内存给对象变量,把变量(指针)设为 nil。每个类可以很多 init 方法,且每个方法都以 init 开头,但每个类只有一个特定(designated)的 init 方法,NSObject 是 init;,UIView 是 - (id)initWithFrame:(CGRect)aRect;。在子类的 designated 方法中一定要调用父类的 designated 方法,子类其他的 init 方法只能调用子类自己的 designated 方法,不能调用父类的(即使用 self 而不是 super)。下面是一些小知识点:4 当你想暂时保留对象时,使用 autolease - (Money *)showMeTheMoney:(double)amount Money *theMoney = Money alloc init:amount; theMoney autorelease; return theMoney; 5 集合类的 autolease,一种方法是像对象一样调用 autolease,另外也可以调用 NSMutableArray array,最好的方式 return NSArray arrayWithObjects:“Steve”, “Ankush”, “Sean”, nil;,其他类似的方法返回的对象都是 autolease 的对象。 NSString stringWithFormat:“Meaning of % is %d”, “life”, 42; NSDictionary dictionaryWithObjectsAndKeys:ankush, “TA”, janestudent, “Student”, nil; NSArray arrayWithContentsOfFile:(NSString *)path;6 集合类对新增的对象拥有 ownership7 string 是 autorelease 的8 NSString 一般是 copye 而不是 retain9 你应该尽快 release 你拥有的对象,越快越好。建议创建完对象后就写好 release 的代码10 当最后一个对象 owner release 后,会自动调用 dealloc 函数,在类中需要重载 dealloc,但永远都不要自己去调用 dealloc11 property 一般直接返回对象变量,我们可以把它理解为返回的是 autolease 的对象12 使用 synthesize 实现时,property 可以指定 setter 函数使用 retain,copy 或 assign。assign 一般用在属性一定会随着对象的消亡而消亡的,比如 controller 的view,view 的 delegate13 Protocols 可以理解为抽象接口,delegat 和 dataSource 基本都是用 protocol 定义的浅析Objective-C的内存管理机制最近iphone平台比较火,大家都一窝蜂的上了。不过貌似好多都是从Java转过来的程序员,用惯了Java的,对Objective- C可能会不大适应,特别是Objective-C的内存管理机制。手机平台不同于计算机,虽然开发的应用都是小巧的应用,但是由于手机硬件上的局限性,如果不对内存好好管理的话,还是会出很多问题的,特别是习惯了垃圾回收机制的Java程序员,最容易忽视这个问题。希望下面这篇文章能对大家有所帮助。Objective-C使用了一种叫做持有计数(Retain Count)的机制来管理内存中的对象。 在Objective-C中每个对象都对应着他们自己的持有计数(Retain Count),持有计数可以理解为一个整数计数器,当使用alloc方法创建对象的时候,持有计数会自动设置为1。当你向一个对象发送retain消息时,持有计数数值会增加。相反,当你像一个对象发送release消息时,持有计数数值会减小。当对象的持有计数变为0的时候,对象会释放自己所占用的内存。 为什么要使用持有计数这个概念呢? 想象一下,有时候你会在多个不同对象

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论