iOS中内存管理

iOS内存管理简介

    Objective-C的内存管理机制与.Net/Java不同,它没有提供全自动的垃圾回收机制,需要我们进行手动管理,其本质相当于在C语言的基础上稍微加了一些自动方法。iOS平台中的内存管理采用了引用计数的管理机制,当创建一个对象使用alloc或者allocWithZone方法时,引用计数就会+1;当释放对象使用release方法时,引用计数就会-1,这意味着每一个对象都会跟踪有多少对象引用它,一旦引用计数为0,该对象的内存就会被释放掉。另外,iOS还提供了一种延时释放机制AutoRelease,以这种方式申请的内存,开发者无需手动释放,系统会在某一时机释放该内存。因此,开发人员在开发应用的时候必须合理的控制何时创建对象、保存对象以及从内存中释放对象。如果使用太多的内存,iPhone会警告应用程序委托和UIViewController,委托收到applicationDidReceiveMemoryWarning:回调,视图控制器会收到didReceiveMemoryWarning,如果继续使用太多内存的话,iPhone将终止你的应用程序,使你的用户回到SpringBoard。很明显,这不是我们所希望得到的用户体验。

iOS内存使用原则:

1.  对象的所有权与销毁

谁创建,谁释放:如果是以allocnew或者copy创建的对象,则必须调用release或者autorelease方法释放内存,如果没有释放,则导致内存泄漏。除了allocnew,或copy之外的方法创建的对象都被声明了autorelease

retain,谁释放:如果对一个对象发送retain消息,其引用计数会+1,则使用完必须发送releaseautorelease方法释放内存或恢复引用计数,如果没有释放,同样导致内存泄漏。

没创建且没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

iOSautorelease机制

1.一般地,在新建一个iPhone项目的时候,xcode会自动在mian函数中为你创建一个autorelease pool,其全名是NSAutoreleasePool,是Objective-C中的一个类。

2.在NSAutoreleasePool中包含了一个可变数组,用来存储被声明为autorelease的所有对象,如果一个对象被声明为autorelease,系统所做的工作就是把这个对象加入到这个数组中去。

3.当AutoreleasePool自身被销毁的时候,它会遍历这个数组,release数组中的每一个成员,如果此时数组中成员的retain count1,那么release之后,retain count0,对象正式被销毁。如果此时数组中成员的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机制的所导致的。

分析如下:

滑动列表的时候,内存增长原因:没有使用UITableVIewreuse机制,导致每显示一个cell都用autorelease的方式重新alloc一次,导致cell的内存不断增加。或者,每个cell会显示一个单独的UIView,在UIView发生内存泄漏,导致cell内存不断增长。

频繁访问图片的时候,内存增长原因:频繁的访问网络图片,导致ios内部API,会不断的分配autorelease方式的buffer来处理图片的解码与显示。

频繁打开和关闭SQLite数据库,内存增长原因:在进行sqlite频繁打开和关闭操作,而且读取的数据buffer较大,那么sqlite在每次打开关闭的时候,都会利用autorelease的方式分配51k的内存,如果访问次数多,内存马上会顶到几十兆,甚至上百兆,所以,对于频繁读写数据库且数据buffer较大的情况,可以设置sqlite长连接方式,避免频繁打开或关闭数据库。

iOS内存使用误区

1重复释放:不要释放不是自己创建的对象,释放自己或系统的autorelease对象,app都会crash

2循环引用:循环引用,容易产生野引用,内存无法回收,最终导致内存泄漏。可以通过弱引用的方式打破循环引用链。所谓的弱引用就是不需要retain,直接采取赋值的方式,这样的话可以避免循环引用,同时也要注意避免重复释放的问题。

IOS内存警报处理流程

1. app收到系统发过来的memory warningnotice

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内存管理机制的基础上,采用最佳的内存管理方式,才能获得优良的用户体验。