Fork me on GitHub

第九讲.内存管理初级.(内存管理的方式,引用计数机制及影响计数的各个方法,dealloc方法,内存管理的基本原则,掌握copy的实现 深拷贝 浅拷贝问题)

 

一.内存管理的方式简介.
 1.进行内存管理的原因: 
       1>.由于移动设备的内存极其有限,所以每个APP所占的内存也是有限制的,当app所占用的内存较多时,系统就会发出内存警告,这时需要回收一些不需要再继续使用的内存空间,比如回收一些不再使用的对象和变量等。
      管理范围:任何继承NSObject的对象,对其他的基本数据类型无效。
       2>.本质原因是因为对象和其他数据类型在系统中的存储空间不一样,其它局部变量主要存放于栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也被回收,此时对象已经没有指针指向,但依然存在于内存中,造成内存泄露。
    2. 内存常见问题体现在两个方面:内存溢出,野指针异常.
      内存溢出:ios给每个应用程序提供了一定的内存,用于程序的运行.一旦超出内存上限,程序就会Crash.(iphone3GS内存30M左右,iphone 5S 内存80M左右.程序中占内存最大的就是图片和音频等资源文件)
      野指针问题:对象内存空间已经被系统回收,仍然使用指针操作这块内存.野指针异常是程序Crash的主要原因.代码量越大的程序,越不容易找出野指针的位置.
         3.内存管理的方式
    垃圾回收(Garbage Collector垃圾收集器 简称gc):程序员只需要开辟内存空间,不需要用代码显示的释放,系统来判断哪些空间不再被使用,并回收这些内存空间,以便再次分配.整个过程不需要代码,系统自动回收.java开发中一直使用的就是垃圾回收技术.
   MRC(人工引用计数 Manual Reference Count):内存的开辟和释放都由程序代码进行控制.相对垃圾回收来说,对内存的控制更加灵活,可以在需要释放的时候及时释放对程序员的要求较高,程序员要熟悉内存管理的机制.
   ARC(自动引用计数 Auto Reference Count):ios5.0的编译器特征,它允许用户开辟空间,不用释放空间.它不是垃圾回收!它的本质还是MRC,只是编译器帮助程序员默认加了释放的代码.
        ios支持两种方式管理内存:ARC和MRC.MRC的内存管理机制是:引用计数. ARC是基于MRC的
 

 
二.内存管理机制
   1>.引用计数.
    c语言中,使用malloc 和 free,进行对内存的创建和释放.对内存只有正在使用和销毁这两种状态.实际开发中,可能会遇到,两个以上的指针使用同一块内存.c语言无法记录内存使用者的个数.
   oc采用引用计数管理内存,当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减.当引用计数到零时,该对象就将释放占用的资源.
   2>. 对象的基本结构
    每个OC对象都有自己的引用计数器,是一个整数表示对象被引用的次数,即现在有多少东西在使用这个对象.对象刚被创建时,默认计数器值为1,当计数器的值变为0时,则对象销毁。
    在每个OC对象内部,都专门有4个字节的存储空间来存储引用计数器
     
    3>.影响引用计数的方法
 
 
    (1)+alloc:开辟内存空间,让被开辟的内存空间引用计数变为1。这是由0到1的过程
 
       (2) -retain;引用计数加1,如果内存空间之前引用技术为1,retain之后变为2,如果引用计数是5,retain之后变为6.
 
     (3) -copy:把某一内存区域的内容拷贝一份,拷贝到新的内存空间里去,被拷贝区域的引用计数不变,新的内存区域的引用计数为1(深拷贝)
 
     (4) -release:引用技术减1,如果内存空间之前引用技术为4,releaes之后变为3,如果之前引用技术为1,release之后变为0,内存被系统回收。
 
    (5)-autorelease:未来的某一时刻引用技术减1.如果内存之前引用计数为4,autorelease之后仍然为4,未来某个时刻会变成3。
 
注意:1.retainCount消息:获得对象当前的引用计数器值
     2.引用计数器的作用:判断对象要不要回收的唯一依据就是计数器是否为0,若不为0则存在。
 
 
 实例分析:
 
 
 
     (6)NSAutoreleasePool的使用
      1.NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];和[pool release];就像一个一对括号,[xxx autorelease];必须写在两者之间。
      2.[xxx autorelease];出现在两者之间,pool就会把接收autorelease的对象保存起来(以栈的方式,把对象放入栈)
      3.当[pool release];的时候,pool会向之前保存的对象逐一发送release消息(对象出栈,越晚autorelease的对象,越早接收release消息)
      4.在iOS5之后,不在推荐使用NSAutoreleasePool类,使用@autoreleasepool{}替代,之前写在NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];和[pool release]; 之间的代码,需要写在@autoreleasepool{}的大括号里。出现大括号,自动释放池才向各个对象发送release消息.
 
 
实例分析:
 
 
 
 
三.dealloc方法
    dealloc方法的代码规范

    (1)一定要[super dealloc],而且要放到最后

    (2)对self(当前)所拥有的的其他对象做一次release操作

 
 
 
 

 
四.内存管理的原则.


   (一)原则    
        1.引用计数的增加和减少相等,当引用技术降为0之后,不应该再使用这块内存空间.
        2.凡是使用了alloc、retain或者copy让内存的引用技术增加了,就需要使用release或者autorelease让内存的引用技术减少。在一段代码内,增加和减少的次数要相当。

   (二)谁创建,谁release
       (1)如果你通过alloc,new,copy来创建了一个对象,那么你就必须调用release或者autorelease方法
       (2)不是你创建的就不用你去负责

   (三)谁retain,谁release
       只要你调用了retain,无论这个对象时如何生成的,你都要调用release

   (四)总结
       有始有终,有加就应该有减。曾经让某个对象计数器加1,就应该让其在最后-1.
 
 
五.掌握copy的实现(深拷贝,浅拷贝问题)
 
       -copy:把某一内存区域的内容拷贝一份,拷贝到新的内存空间里去,被拷贝区域的引用计数不变,新的内存区域的引用计数为1

     retain:始终是浅复制。引用计数每次加一。返回对象是否可变与被复制的对象保持一致。

     copy对于可变对象为深复制,引用计数不改变;对于不可变对象是浅复制,

         引用计数每次加一。始终返回一个不可变对象。

 
 

    注意: 不可变对象:值发生改变,其内存首地址随之改变。

          可变对象:无论值是否改变,其内存首地址都不随之改变。

          引用计数:为了让使用者清楚的知道,该对象有多少个拥有者(即有多少个指针指向同一内存地址)。

 

 

//浅拷贝:对于被复制的对象只是指针复制,就像用两把钥匙开一个房间一样(只是指针指向型复制)


//深拷贝:复制对象的内容,地址不同值相同(就像克隆人又克隆了一个对象一样)


//深浅拷贝的本质区别是对象或者对象属性的内存地址是否一样,一样则为浅拷贝,不一样则为深拷贝。

 

 

 实例分析一:(深拷贝:内存地址不相同,但值相同,注意里面的注释)
 
 Person.h文件
 
 
 
Person.m文件(这是遵循copy协议进行深拷贝的固定方法)
 
 
main.m文件
 
 
 
 
 
 
 
 实例分析二:(深拷贝固定用法)
 深拷贝固定用法
 
 
 
 
 
 实例三:(数组,字符串的深浅拷贝分析)
 深拷贝
 
 
 
浅拷贝
 
 
 

   深浅拷贝总结:通俗的讲,多个指针同时指向同一块内存区域,那么这些个指针同时拥有对该内存区的所有权。所有权的瓜分过程,这时候就要用到浅拷贝了。

经典简化总结为:

问:什么时候用到深浅拷贝?

答:深拷贝是在要将一个对象从可变(不可变)转为不可变(可变)或者将一个对象内容克隆一份时用到;

     浅拷贝是在要复制一个对象的指针时用到。

 

 

 

 
 
 
 
 
posted @ 2015-09-01 18:00  DengHuiCheng  阅读(262)  评论(0)    收藏  举报