Objective-C内存管理

10 篇文章 0 订阅

 

一、为什么要管理内存

        为什么要管理内存呢?我们都知道系统可供程序使用的资源是有限的,包括内存,而且每个应用程序所占用的内存也是有限的。如果一直不断地打开应用程序,使应用程序一直处于启动状态而从不关闭,最终会占满设备内存空间。以公共图书馆为例,如果每个人都只借不还,那么图书馆终将会被借空导致无书可借而倒闭,每个人也都无法再使用其他书籍。程序运行结束时,如果不及时进行内存清理,系统的内存得不到释放可能会导致程序崩溃。

          我们必须确保再需要的时候分配内存,在程序结束时释放占用的内存。同时注意不要使用刚释放的内存,否则可能会误用陈旧的数据,从而引发各种各样的错误,而且如果该内存已经加载了其他数据,将会破坏这些新数据。

 

二、基本原理

       1.  对象的基本结构

                   --> 每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”,即有多少人正在使用这个OC对象

              --> 每个OC对象内部专门有4个字节的存储空间来存储引用计数器

         2. 引用计数器的作用

              --> 当使用allocnew或者copy创建一个新对象时,新对象的引用计数器默认就是1

              --> 当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收。

                    换句话说,如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收,除非整个程序已经退出

         3. 引用计数器的操作

              --> 给对象发送一条retain消息,可以使引用计数器值+1retain方法返回对象本身)

              --> 给对象发送一条release消息,可以使引用计数器值-1

              --> 可以给对象发送retainCount消息获得当前的引用计数器值


 


 

           4. 对象的销毁

             --> 当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收

             --> 当一个对象被销毁时,系统会自动向对象发送一条dealloc消息

             --> 一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言

             --> 一旦重写了dealloc方法,就必须调用[superdealloc],并且放在最后面调用

             --> 不要直接调用dealloc方法

             --> 一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)

 

                        野指针:指向僵尸对象(不可用内存的指针)称为野指针。

 

                   使用野指针操作一块不可用内存就会出现下面的经典错误!

                   野指针错误 :

                           EXC_BAD_ACCESS:访问一块已经被回收的内存(坏内存,不可用的)


                  OC中要防止野指针错误,可以在指针使用完毕之后将指针赋值 nil

                  OC中不存在空指针错误,给空指针发送消息是不会报错的

 

                  

 
三、内存管理原则
 

        只要你使用这个对象,就让对象的计数器+1

           当你不再使用这个对象时,就让对象的计数器-1


             --> 谁创建,谁release:有始有终!! 

                    果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease

                    换句话说,不是你创建的,就不用你去[auto]release


             --> 谁retain,谁release

                   只要你调用了retain,无论这个对象是如何生成的,你都要调用release

 

 

 

四、set方法的内存管理

 

               1. 只要调用了alloc,必须有release(或autorelease)

                 对象不是通过alloc产生的就不要release

 
            2.  set方法的实现:
 
- (void)setCar:(Car *)car
{
       if (car!= _car)  // 如果是一辆新车
       {
	  [_car release];  // 将旧车做一次release
	   _car = [car retain]; // 将新车做一次retain
       }
}
 
                  3. dealloc方法的实现
 
               一定要[super release],而且必须放到最后面;
               对self(当前)所拥有的其他对象做一次release;
- (void)dealloc 
{
	[_car release];
	[super dealloc];
}

 
 
 
 
五、@property参数
 

          1.  控制set方法的内存管理

                --> retain: release旧值,retain新值(用于OC对象)

        --> assign:直接赋值,不做任何内存管理(默认,用于非OC对象类型)

                --> copy: release旧值,copy新值(一般用于NSString *)

 

         2. 控制需不需生成set方法

              -->readwrite:同时生成set方法和get方法(默认)

              -->readonly  :只会生成get方法

 

        3. 多线程管理

               -->atomic:性能低(默认)

        -->nonatomic:性能高

 

         4.   控制set方法和get方法的名称

               --> setter:设置set方法的名称,一定有个冒号:

               --> getter:设置get方法的名称

 

 

 

六、循环引用
       
          假如A类中有B类这个成员变量,B类中又有A类类型的成员变量,则就是循环引用;
          循环引用要用 @class 类名   来声明。在真的需要用到这个类的方法的时候才导入@import“类名.h”

          1、@class的作用:
                 -->  仅仅告诉编译器,@class 类名  这是个类
                        如@class Person;  仅仅告诉编译器,Person是一个类

          2、开发中引用到一个类的规范:
                --> 在.h文件中用@class来声明类
                --> 在.m文件中用@import来声明类

          3、循环引用的时候一端用retain,一端用assign
              注意:
                     --> 用assign那边dealloc里面就不要release引用的对象了。不然会引起野指针错误!
                      --> 只要是闭环的循环引用都必须至少有一端是用得时assign,一端是retain;
                            A引用B,B引用C,C引用D,D引用A
 
        4.  @class 和 #import的区别
              --> #import方式会包含被引用类的所有信息,包括被引用类的变量和方法@class方式只是告诉编译器在A.h文件中  B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息
              --> 如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,而相对来 讲,使用@class方式就不会出现这种问题了
              --> 在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类
 
 
 

七、autorelease方法
 
           --> autorelease 会将对象放到一个自动释放池中;
           --> 当自动释放池被销毁时,会对池子里面所有的对象做一次release操作一次;
           --> autorelease 会返回对象本身;
           --> 调用完autorelease后,对象的引用计数器不会改变;
           --> autorelease可以无限创建(也可以无限嵌套)
           --> autorelease是将自动释放池放到一个栈里面,遵循先进后出的原则。
           优点:
                    --> 不用关心对象释放的时间;
                    -->不用关心什么时候掉哦那个release;

           使用注意:
                    -->占用内存较大对象不要随便使用autorelease(不能精确的控制对象的销毁时间)
                    --> 占用内存较小的对象可以使用,没有太大的影响

           一般写法:
                    --> autorelease 会返回对象本身;
                    --> 调用完autorelease后,对象的引用计数器不会改变;
                    --> autorelease 会讲对象放到一个自动释放池中;
                    --> 当自动释放池被销毁时,会对池子里面所有的对象做一次release操作一次;
                          Person *p = [[[Person alloc] init] autorelease];
 
 
 
<span style="font-size:18px;color:#000000;">@autoreleasepool
{  //  {开始带表创建了释放池
      Person *p =[[[Person alloc] init] autorelease];//autorelease会将p指向的对象放到栈顶池子中
  
}  // }结束代表销毁释放池</span>
             autorelease的创建:

 

                   IOS5.0后:

 

<span style="font-size:18px;">@autoreleasepool
{
// ....
}
</span>

             IOS5.0前:

   

<span style="font-size:18px;">NSAutoreleasePool *pool = [[NSAutoreleasePoolalloc] init];
// .....
[pool release]; // 或[pool drain];
</span>

          在程序运行过程中,可以创建多个自动释放池,它们是以栈的形式存在内存中

         OC对象只需要发送一条autorelease消息,就会把这个对象添加到最近的自动释放池中(栈顶的释放池)

             
 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值