iOS中内存管理
iOS内存管理简介
Objective-C的内存管理机制与.Net/Java不同,它没有提供全自动的垃圾回收机制,需要我们进行手动管理,其本质相当于在C语言的基础上稍微加了一些自动方法。iOS平台中的内存管理采用了引用计数的管理机制,当创建一个对象使用alloc或者allocWithZone方法时,引用计数就会+1;当释放对象使用release方法时,引用计数就会-1,这意味着每一个对象都会跟踪有多少对象引用它,一旦引用计数为0,该对象的内存就会被释放掉。另外,iOS还提供了一种延时释放机制AutoRelease,以这种方式申请的内存,开发者无需手动释放,系统会在某一时机释放该内存。因此,开发人员在开发应用的时候必须合理的控制何时创建对象、保存对象以及从内存中释放对象。如果使用太多的内存,iPhone会警告应用程序委托和UIViewController,委托收到applicationDidReceiveMemoryWarning:回调,视图控制器会收到didReceiveMemoryWarning,如果继续使用太多内存的话,iPhone将终止你的应用程序,使你的用户回到SpringBoard。很明显,这不是我们所希望得到的用户体验。
iOS内存使用原则:
1. 对象的所有权与销毁
①谁创建,谁释放:如果是以alloc,new或者copy创建的对象,则必须调用release或者autorelease方法释放内存,如果没有释放,则导致内存泄漏。除了alloc,new,或copy之外的方法创建的对象都被声明了autorelease。
②谁retain,谁释放:如果对一个对象发送retain消息,其引用计数会+1,则使用完必须发送release或autorelease方法释放内存或恢复引用计数,如果没有释放,同样导致内存泄漏。
③没创建且没retain,别释放:不要释放那些不是自己alloc或者retain的对象,否则程序会crash,不要释放autorelease的对象,否则程序会crash。
2. 对象的深拷贝与浅拷贝
①深拷贝:复制指针所引用的数据,并将其赋给副本的实例变量。其流程是先创建一个
新的对象且引用计数为1,并用就旧对象的值初始化这个新对象。
ClassA *objA=[[ClassA alloc] init];
ClassA *objB=[objA copy];
objB是一个新对象,引用计数为1,且objB的数据等同于objA的数据。
注:objB需要释放,否则会内存泄漏
②浅拷贝:将原始对象的指针复制到副本中,原始对象和副本共享引用数据。其流程是,无需引入新的对象,把原有对象的引用计数+1即可。
ClassA *objA=[[ClassA alloc] init];
ClassA *objB=[objA retain];
注:objB需要释放,恢复objA的引用计数,否则会引起内存泄漏。
3. 对象的存取方法
①属性的声明与实现
变量声明的常用属性类型包括:
readonly属性:只能读,不能写;
assign属性:是默认属性,直接赋值,没有任何保留与释放问题;
retain属性:会增加原有对象的引用计数,并且在赋值前会释放原有对象,然后再进行赋值。
copy属性:会复制原有对象,并在赋值前释放原有对象,然后再进行赋值。
②属性声明可能带来的问题:
当一个非指针变量使用retain(或者copy)这个属性时,尽量不要显性的release这个变量,直接给这个变量置空即可,否则容易产生过度释放,导致程序crash。
iOS中autorelease机制
1.一般地,在新建一个iPhone项目的时候,xcode会自动在mian函数中为你创建一个autorelease pool,其全名是NSAutoreleasePool,是Objective-C中的一个类。
2.在NSAutoreleasePool中包含了一个可变数组,用来存储被声明为autorelease的所有对象,如果一个对象被声明为autorelease,系统所做的工作就是把这个对象加入到这个数组中去。
3.当AutoreleasePool自身被销毁的时候,它会遍历这个数组,release数组中的每一个成员,如果此时数组中成员的retain count为1,那么release之后,retain count为0,对象正式被销毁。如果此时数组中成员的retain count大于1,那么release之后,retain count大于0,此对象依然没有被销毁,内存泄漏。
Main函数如下:
int main(int argc,char *argv[])
{
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
int retVal=UIApplicationMain(argc,argv,nil,nil);
[pool release];
return retVal;
}
4.自动释放池所涉及到的一些常见问题
在ios程序开发的时候,会经常遇到在滑动列表,频繁访问图片,频繁打开或关闭数据库的时候,内存会莫名其妙的增长,其实,这些都很有可能是autorelease机制的所导致的。
分析如下:
①滑动列表的时候,内存增长原因:没有使用UITableVIew的reuse机制,导致每显示一个cell都用autorelease的方式重新alloc一次,导致cell的内存不断增加。或者,每个cell会显示一个单独的UIView,在UIView发生内存泄漏,导致cell内存不断增长。
②频繁访问图片的时候,内存增长原因:频繁的访问网络图片,导致ios内部API,会不断的分配autorelease方式的buffer来处理图片的解码与显示。
③频繁打开和关闭SQLite数据库,内存增长原因:在进行sqlite频繁打开和关闭操作,而且读取的数据buffer较大,那么sqlite在每次打开关闭的时候,都会利用autorelease的方式分配51k的内存,如果访问次数多,内存马上会顶到几十兆,甚至上百兆,所以,对于频繁读写数据库且数据buffer较大的情况,可以设置sqlite长连接方式,避免频繁打开或关闭数据库。
iOS内存使用误区
.
2.循环引用:循环引用,容易产生野引用,内存无法回收,最终导致内存泄漏。可以通过弱引用的方式打破循环引用链。所谓的弱引用就是不需要retain,直接采取赋值的方式,这样的话可以避免循环引用,同时也要注意避免重复释放的问题。
IOS内存警报处理流程
1. app收到系统发过来的memory warning的notice;
2. app释放占用较大的内存;
3. 系统回收此app所创建的autorelease的对象;
4. app返回到已经打开的页面时,系统重新调用viewdidload方法,view重新加载页面数据,重新进行显示;
iOS内存检查工具
1. 编译分析工具Analyze
可以发现编译中的warning,内存泄漏隐患,甚至是逻辑上的问题,从而避免严重的由于内存引起的严重的bug。
内存泄漏隐患提示:Potential Leak of an object allocated on line…
数据赋值隐患提示:The left operand of…is a garbage value;
对象引用隐患提示:Reference-Counted object is used after it is release;
2. 内存检测工具
①内存泄漏检测工具——Leak
②内存猛增检测工具——Allocations
二者的详细使用,可以自己查阅相关信息进行了解。
总之,对于IOS中内存的管理,需要我们在实际的程序开发中通过编写相应代码,以及调试相关由内存引起的的问题,才可以得到比较好的体会。如果在开发过程中,不能按照分配原则进行内存的合理分配与释放,将会对整个应用程序的性能造成很严重的影响,甚至导致其崩溃。所以开发者必须在深刻理解ios内存管理机制的基础上,采用最佳的内存管理方式,才能获得优良的用户体验。
本文转自HDDevTeam 51CTO博客,原文链接:http://blog.51cto.com/hddev/1112121,如需转载请自行联系原作者