Obj-C内存管理

     OC中,继承NSObject的类的对象在建立时,对象内部会自动建立一个内存计数器retainCount,当retainCount为0时,系统会自动回收该对象。retainCount是判断的惟一标记。 数据库

     进行retain操做时,返回的是指向本身的self指针。 数组

引用计数: 网络

       当代码须要访问一个对象时,该代码将对象的引用计数器的值+1,表示“我要访问该对象”。

      当代码结束时,对象的引用计数-1,表示“我再也不访问该对象”。 app

       对象的引用计数为零时,表示没有代码会再使用该对象。对象将会被销毁,其占用的内存被系统回收。
此时,将会调用dealloc方法。(此方法不要直接调用,而是由系统自动调用)


使用 alloc、 new 方法或者经过copy消息(生成接收对象的一个副本)建立对象时,对象的引用计数被设置为1。

得到当前引用计数的值:
    发送retainCount消息,消息原型:-(unsigned)retainCount;
-(id)retain;对象发送该消息使得引用计数+1
    嵌套使用 [ [ car retain] setTire:tire atIndex 2 ];

- (void) release; 对象发送该消息使得引用计数-1 函数

在使用@property 和 @synthesize时,若是方法前有retain参数,生成的setter方法中会有retain操做. 工具


@property(retain)Car *car;
-(void)setCar:(Car *)car //标准格式
{
    //1.先判断是否是新传进来的对象
    If(car!=_car)
    { 
    //2 对旧对象作一次release
        [_car release];//若没有旧对象,则没有影响

    //3.对新对象作一次retain
        _car=[car retain];
    }
}




对象的全部权问题:
    1.若是一个对象内部有指向其余对象的变量时,该对象就拥有这些对象。

    2.若是一个函数建立了一个对象,则该函数就拥有它所建立的对象。 测试

Cocoa内存管理的三条原则:
    1. 使用new、alloc和copy方法建立一个对象时,该对象的引用计数为1。不在使用时,对该对象发送release或者autorelease。
    2.当经过任何方法得到一个对象时,则假设该对象的引用计数为1,并且已经被设置为自动释放。若是打算长时间保留此对象,则确保操做完成时释放。

    3.保留某个对象时,最终须要释放或者自动释放该对象。必须保证retain和release的次数相同。 spa

牢记一条:

    若是使用new、alloc或者copy方法得到一个对象,则必须释放或者自动释放它。 命令行

基本的ARC使用规则  线程

     代码中不能使用retain, release, retain, autorelease

     不重载dealloc(若是是释放对象内存之外的处理,是能够重载该函数的,可是不能调用[super dealloc])

     不能使用NSAllocateObject, NSDeallocateObject

     不能在C结构体中使用对象指针

     id与void *间的若是cast时须要用特定的方法(__bridge关键字)

     不能使用NSAutoReleasePool、而须要@autoreleasepool块

     不能使用“new”开始的属性名称 (若是使用会有下面的编译错误”Property’s synthesized getter follows Cocoa naming convention for returning ‘owned’ objects”)


    iOS平台的内存使用引用计数的机制,而且引入了半自动释放机制;这种使用上的多样性,致使开发者在内存使用上很是容易出现内存泄漏和内存莫名的增 长状况; 本文会介绍iOS平台的内存使用原则与使用陷阱; 深度剖析autorelease机制;低内存报警后的处理流程;并结合自身实例介绍内存暴增的问题追查记录以及相关工具的使用状况;
    iOS平台内存常见问题
    做为iOS平台的开发者,是否曾经为内存问题而苦恼过?内存莫名的持续增加,程序莫名的 crash,难以发现的内存泄漏,这些都是iOS平台内存相关的常见问题;本文将会详细介绍iOS平台的内存管理机制,autorelease机制和内存 的使用陷阱,这些将会解决iOS平台内存上的大部分问题,提升了程序的稳定性;
    1 iOS平台内存管理介绍
    iOS平台的内存管理采用引用计数的机制;当建立一个对象时使用alloc或者allWithZone方法时,引用计数就会+1;当释放对象使用release方法时,引用计数就是-1; 这就意味着每个对象都会跟踪有多少其余对象引用它,一旦引用计数为0,该对象的内存就会被释放掉;另外,iOS也提供了一种延时释放的机制 AutoRelease,以这种方式申请的内存,开发者无需手动释放,系统会在某一时机释放该内存; 因为iOS平台的这种内存管理的多样性,致使开发者在内存使用上很容易出现内存泄漏或者程序莫名崩溃的状况,本文会详细介绍iOS平台内存的使用规范与技 巧以及如何利用工具避免或者发现问题;
    2 iOS平台内存使用原则
    2.1 对象的全部权与销毁
    2.1.1 谁建立,谁释放;
    若是是以alloc,new或者copy,mutableCopy建立的对象,则必须调用release或者autorelease方法释放内存;
    若是没有释放,则致使内存泄漏!
    2.1.2 谁retain,谁释放;
    若是对一个对象发送 retain消息,其引用计数会+1,则使用完必须发送release或者autorelease方法释放内存或恢复引用计数;
    若是没有释放,则致使内存泄漏!
    2.1.3 没建立且没retain,别释放;
    不要释放那些不是本身alloc或者retain的对象,不然程序会crash;
    不要释放autorelease的对象,不然程序会crash;
    2.2 对象的深拷贝与浅拷贝
    通常来讲,复制一个对象包括建立一个新的实例,并以原始对象中的值初始化这个新的实例。 复制非指针型实例变量的值很简单,好比布尔,整数和浮点数。复制指 针型实例变量有两种方法。一种方法称为浅拷贝,即将原始对象的指针值复制到副本中。所以,原始对象和副本共享引用数据。另外一种方法称为深拷贝,即复制指针 所引用的数据,并将其赋给副本的实例变量。
    2.2.1 深拷贝
    深拷贝的流程是 先建立一个新的对象且引用计数为1,并用旧对象的值初始化这个新对象;
    ClassA* objA = [[ClassA alloc] init];
    ClassA* objB = [objA copy];
    objB是一个新对象,引用计数为1,且objB的数据等同objA的数据;
    注意: objB须要释放,不然会引发内存泄漏!
    2.2.2 浅拷贝
    浅拷贝的流程是,无需引入新的对象,把原有对象的引用计数+1便可
    ClassA* objA = [[ClassA alloc] init];
    ClassA* objB = [objA retain];
    注意: objB须要释放,恢复objA的引用计数,不然会引发内存泄漏!
    2.3对象的存取方法

    2.3.1 属性声明和实现
    变量声明的经常使用属性类型包括readonly,assign,retain和copy;且系统会自动为声明了属性的变量生成set和get函数;
    readonly属性: 只能读,不能写;
    assign属性: 是默认属性,直接赋值,没有任何保留与释放问题;
    retain属性: 会增长原有对象的引用计数而且在赋值前会释放原有对象,而后在进行赋值;
    copy属性:  会复制原有对象,并在赋值前释放原有对象,而后在进行赋值;
    2.3.2 使用属性声明可能带来的隐患
    当一个非指针变量使用retain(或者copy)这个属性时,尽可能不要显性的release这个变量;直接给这个变量置空便可;不然容易产生过分释放,致使程序crash; 例如:
    ClassA类的strName是NSString* 类型的变量且声明的属性为retain;
    ClassA.strName = nil;  /* 释放原有对象且对此对象赋值为空 */
    [ClassA.strName release]; /* strName内存可能已经被释放过了,将致使程序crash */
    Assign这个属性通常是非指针变量(布尔类型,整形等)时用这个类型;属于直接赋值型,不须要考虑内存的保留与释放;
    若是一个指针类型的变量使用assign类型的属性,有可能引用已经释放的变量;致使程序crash; 例如:
    ClassB* obj =[[[ClassB alloc] init] autorelease];
    ……
    ClassA.strName = obj; /* strName 指向obj的内存地址*/
    后续在使用ClassA.strName的时候, 由于obj是autorelease的,可能obj的内存已经被回收;致使引用无效内存,程序crash;
    3iOS平台AutoRelease机制

    3.1 自动释放池的常见问题
    你们在开发iOS程序的时候,是否遇到过在列表滑动的状况内存莫名的增加,频繁访问图片的时候内存莫名的增加,频繁的打开和关闭数据库的时候内存莫名的增加…… 这些都是拜iOS的autorelease机制所赐;具体分析以下:
    1: 滑动列表的时候,内存出现莫名的增加,缘由可能有以下可能:
    没有使用UITableView的reuse机制;

     致使每显示一个cell都用autorelease的方式从新alloc一次;

     致使cell的内存不断的增长;
     每一个cell会显示一个单独的UIView, 在UIView发生内存泄漏,致使cell的内存不断增加;
    2: 频繁访问图片的时候,内存莫名的增加;
    频繁的访问网络图片,致使iOS内部API,会不断的分配autorelease方式的buffer来处理图片的解码与显示; 利用图片cache能够缓解一下此问题;
    3: 频繁打开和关闭SQLite,致使内存不断的增加
    在进行SQLite频繁打开和关闭操做,并且读写的数据buffer较大,那么 SQLite在每次打开与关闭的时候,都会利用autorelease的方式分配51K的内存; 若是访问次数不少,内存立刻就会顶到几十兆,甚至上百兆! 因此针对频繁的读写数据库且数据buffer较大的状况, 能够设置SQLite的长链接方式;避免频繁的打开和关闭数据库;
    3.2 自动释放池的概念
    NSAutoreleasePool内部包含一个数组(NSMutableArray),用来保存声名为autorelease的全部对象。若是一个对象声明为autorelease,系统所作的工做就是把这个对象加入到这个数组中去。
    ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1,把此对象加入autorelease pool中
    NSAutoreleasePool自身在销毁的时候,会遍历一遍这个数 组,release数组中的每一个成员。若是此时数组中成员的retain count为1,那么release以后,retain count为0,对象正式被销毁。若是此时数组中成员的retain count大于1,那么release以后,retain count大于0,此对象依然没有被销毁,内存泄露。
    3.3 自动释放池的做用域与嵌套
    AutoreleasePool是能够嵌套使用的!
    池是被嵌套的,嵌套的结果是个栈,同一线程只有当前栈顶pool实例是可用的:
 
    当短生命周期内,好比一个循环中,会产生大量的临时内存,能够建立一个临时的autorelease pool,这样能够达 到快速回收内存的目的;
    3.4 自动施放池的手动建立与自动建立
    3.4.1 须要手动建立自动释放池
    ●若是你正在编写一个不是基于Application Kit的程序,好比命令行工具,则没有对自动释放池的内置支持;你必  须本身建立它们。
    ●若是你生成了一个从属线程,则一旦该线程开始执行,你必须当即建立你本身的自动释放池;不然,你将会泄漏对象。
    ●若是你编写了一个循环,其中建立了许多临时对象,你能够在循环内部建立一个自动释放池,以便在下次迭代以前销毁这些对象。这能够帮助减小应用程序的最大内存占用量。
    3.4.2 系统自动建立自动释放池
    Application Kit会在一个事件周期(或事件循环迭代)的开端—好比鼠标按下事件—自动建立一个自动释放池,而且在事件周期的结尾释放它.
    4 iOS平台内存使用陷阱
    4.1 重复释放
    在前文已经提到,不要释放不是本身建立的对象; 释放本身的autorelease对象,app会crash;
释放系统的autorelease对象,app会crash;
    4.2 循环引用

    循环引用,容易产生野引用,内存没法回收,最终致使内存泄漏!能够经过弱引用的方式来打破循环引用链;所谓的弱引用就是不须要retain,直接赋值的方式,这样的话,能够避免循环引用的问题,可是须要注意的是,避免重复释放的问题;
    5 iOS平台内存报警机制
    因为iOS平台的内存管理机制,不支持虚拟内存,因此在内存不足的状况,不会去Ram上 建立虚拟内存;因此一旦出现内存不足的状况,iOS平台会通知全部已经运行的app,不管是前台app仍是后台挂起的app,都会收到 memory warning的notice;一旦app收到memory warning的notice,就应该回收占用内存较大的变量;
    5.1 内存报警处理流程
    1: app收到系统发过来的memory warning的notice;
    2: app释放占用较大的内存;
    3: 系统回收此app所建立的autorelease的对象;
    4: app返回到已经打开的页面时,系统从新调用viewdidload方法,view从新加载页面数据;从新显示;
    5.2 内存报警测试方法     在Simulate上能够模拟低内存报警消息;     iOS模拟器 -> 硬件 -> 模拟内存警告;     开发者能够在模拟器上来模拟手机上的低内存报警状况,能够避免因为低内存报警引出的app的莫名crash问题;   

相关文章
相关标签/搜索