zoukankan      html  css  js  c++  java
  • iOS-内存管理

    一、前言

          对于大多数从C++或者JAVA转过来学习Object-C(以下简称OC)的人来说,OC这门语言看起来非常奇怪,用起来也有点麻烦。

          OC没有像JAVA一样的垃圾回收机制,也就是说,OC编程需要程序员手动去管理内存(即使在ARC项目中,若我们引入第三方文件使用了MRC,我们也需要对单文件做MRC编译格式处理)。这就是为什么它烦的原因,苹果却一直推崇开发者在有限硬件资源内写出最优化的代码,使用CPU最少,占用内存最小。

    二、基本原理

    内存管理当然是帮助我们程序员管理内存的。

    1.而内存管理涉及两个操作:

                1)在创建新对象时,内存的分配;

                2)在不需要使用旧对象时,内存的回收;

    1):对象的创建:

          OC在创建对象时不会直接返回新创建的该对象,而是返回一个指向对象的指针,因此除了基本类型以外,我们在OC中基本上都在使用指针。

          ClassA  *a = [[ClassA   alloc]  init];

          在[ClassA   alloc]的时候,已经发送消息通知系统给ClassA的对象分配内存空间,并且返回了指向未初始化的对象的一个指针。

          未初始化的ClassA对象接收到init消息,init返回指向已初始化后的ClassA对象的一个指针,然后将其赋值给指针对象a。

    2):对象内存的回收:

    在创建并使用完一个对象的时候,用户需要手动地去释放该对象。

          [a   dealloc];

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------

    2.内存管理涉及两个类型和一个类

    1)ARC(自动内存管理)

    详解参考:

    2)MRC(手动内存管理)

    详解参考:

    3)NSAutoreleasePool(自动释放池子)

    1.在SDK4.0版本后,苹果官方推出了ARC(也就是Automatic Reference Counting,自动引用计数)来帮助我们程序员管理内存;

    2.在此之前我们程序员都需要手动代码管理内粗(即MRC);

    3.NSAutoreleasePool是配合我们苹果开发程序员管理内存的一个类,既可以用于ARC又可以用于MRC,但是两个不同环境下,稍有不同;

    使用详解参考:iOS---NSAutoreleasePool自动释放原理及详解

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------

    3.内存管理应当注意:

    1)旧项目升级

    iOS-旧项目中手动内存管理(MRC)转ARC

    2)项目引入第三方文件,文件中部分单文件需要用到内存管理类型的转换

    3)避免野指针空指针等问题防止内存泄露:

    如果指针a和b同时指向堆中同一块内存地址

          ClassA  *a = [[ClassA   alloc]  init];

          ClassA  *b = a;

          [a   dealloc];

    当执行到第三行的时候,指针b就成了无头指针(野指针)。这是一个在C++中也是常见的错误,我们需要避免这类错误,因为无头指针是危险的。

    引用计数:

          OC在内存管理上采用了  引用计数(retain count),在对象内部保存一个数字,用来表示被引用的次数。init、new和copy都会让retain count加1。当销毁对象的时候,系统不会直接调用dealloc方法,而是先调用release,让retain count 减1,当retain count等于0的时候,系统才会调用dealloc方法来销毁对象。

          在指针赋值的时候,retain count 是不会自动增加的,为了避免上面所说的错误,我们需要在赋值的时候手动retain一次,让retain count 增加1。

          ClassA  *a = [[ClassA   alloc]  init];  // retain count = 1

          ClassA  *b = a;

          [b   retain];  // retain count = 2

          [a   dealloc];

    这样在执行到第四行的时候,对象的retain count只是减了1,并没有被销毁,指针b仍然有效。

    内存泄露:

          就如上面列子所示,当生成ClassA对象时,指针a拥有对该对象的访问权。如果失去了对一个对象的访问权,而又没有将retain count减至0,就会造成内存泄露。也就是说,分配出去的内存无法回收。

          ClassA  *a = [[ClassA   alloc]  init];

          a  =  nil;

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------

    四、手动管理内存

          使用alloc、new、copy创建一个对象,该对象的retain count 都等于1,需要用release来释放该对象。谁创建,谁去释放。在这3钟方法以外的方法创建的对象,都被系统默认的声明为autorelease。

          ClassA  *a = [[ClassA   alloc]  init];

          ClassA  *b = a;

          [b   retain];

          //do smoething

          [b release];

          b  =  nil;

    把一个指针赋值给另外一个指针的时候,a 指针所指向的对象的引用次数并没有增加,也就是说,对象的retain count依然等于1。只有在retain了之后,retain count 才会加1。那么,如果这时候执行[a  release],只是a指针放弃了对对象的访问权,对象的retain count 减1,对象没有被销毁。只有当b也执行了release方法之后,才会将对象销毁掉。因此,谁retain了,谁就要release。

          在对象被销毁之后,指针依然是存在的。所以在release了之后,最好把指针赋为空,防止无头指针的出现。顺便一说,release一个空指针是合法的,但是不会发生任何事情。

          如果你在一个函数中创建并返回一个对象,那么你需要把这个对象声明为autorelease

          (ClassA  *)Function()

          {

               ClassA *a = [[[ClassA   alloc]  init]  autorelease];

               return a;

          }

    不这样做的话,会造成内存泄露。

    五、属性与内存管理

          苹果一直没有强调的一点是,关于属性中的retain。事实上,属性中带有retain的,在赋值的时候可能已经在合成的setter中retain了一次,因此,这里也需要release。

          @property实际上是getter和setter,@synthesize是合成这2个方法。为什么在声明了属性之后可以用“.”来直接调用成员变量呢?那是因为声明属性以后系统根据你给的属性合成了一个set方法和一个get方法。使用“.”与属性并没有直接关联,如果你不嫌麻烦,在你的程序里面多写一个set和get方法,你也可以使用“.”来调用变量。

          @property(),如果你里面什么都不写,那么系统会默认的把你的属性设置为:

          @property(atomic, assign)…..

    关于nonatomic:

          这个属性没有对应的atomic关键字,即使我上面是这么写,但atomic只是在你没有声明这个特性的时候系统默认,你无法主动去声明这一特性。

          如果你的程序只有一个主线程,或者你确定你的程序不会在2个或者以上线程运作的时候访问同一个变量,那么你可以声明为nonatomic。指定nonatomic特性,编译器合成访问器的时候不会去考虑线程安全问题。如果你的多个线程在同一时间会访问到这个变量的话,可以将特性声明为atomic(通过省略关键字nonatomic)。在这种特性的状态下,编辑器在合成访问器的时候就会在访问器里面加一个锁(@synchronized),在同一时间只能有一个线程访问该变量。

          但是使用锁是需要付出代价的,一个声明为atomic的属性,在设置和获取这个变量的时候都要比声明为nonatomic的慢。所以如果你不打算编写多线程代码,最好把变量的属性特性声明为nonatomic。

    关于assign、retain和copy:

          assign是系统默认的属性特性,它几乎适用于OC的所有变量类型。对于非对象类型的变量,assign是唯一可选的 特性。但是如果你在引用计数下给一个对象类型的变量声明为assign,那么你会在编译的时候收到一条来自编译器的警告。因为assign对于在引用计数下的对象特性,只创建了一个弱引用(也就是平时说的浅复制)。这样使用变量会很危险。当你release了前一个对象的时候,被赋值的对象指针就成了无头指针了。因此在为对象类型的变量声明属性的时候,尽量少(或者不要)使用assign。

          关于assign合成的setter,看起来是这样的:

          -(void)setObjA:(ClassA  *)a

          {

               objA  =  a;

          }

          在深入retain之前,先把声明为retain特性的setter写出来:

          -(void)setObjA:(ClassA  *)a

    {

               If(objA != a)

    {

       [objA  release];

       objA  =  a;

       [objA  retain];  //对象的retain count 加1

    }

    }

    明显的,在retain的setter中,变量retain了一次,那么,即使你在程序中

          self.objA  =  a;

    只写了这么一句,objA仍然需要release,才能保证对象的retain count 是正确的。但是如果你的代码

          objA  =  a;

    只写了这么一句,那么这里只是进行了一次浅复制,对象的retain count 并没有增加,因此这样写的话,你不需要在后面release objA。

          这2句话的区别是,第一句使用了编译器生成的setter来设置objA的值,而第二句只是一个简单的指针赋值。

          copy的setter看起来是这样的:

          -(void)setObjA:(ClassA  *)a

    {

      ClassA  * temp  =  objA;

      objA  =  [a   copyWithZone:nil];

      [temp  release];

    }

    复制必须通过实现copyWithZone:这个方法,因次copy这个特性只适用于拥有这个方法的类型,也就是说,必须这个类支持复制。复制是把原来的对象release掉,然后让指针指向一个新的对象的副本。因此即使在setter里面release了原来的对象,你仍然需要在后面release新指向的对象(副本)。

    六、尾声

          开发现在唯一能用的内存管理方式就是引用计数,无论你喜欢还是不喜欢。在一个内存紧缺的机器上,你编写程序的时候也只能步步为营,尽可能的让你的程序腾出内存空间,并保证系统不会给你一个警告。即使苹果在Mac OS X 雪豹(v10.5)系统里面添加了另外一种内存管理方式(垃圾收集),但目前不适用于。

  • 相关阅读:
    OptiMSoC
    xilinx官方设计指导
    Essential of FPGA Design
    数据结构学习记录_2019.02.22
    C语言学习记录_2019.02.12
    C语言学习记录_2019.02.10
    数据结构学习记录_2019.02.10
    数据结构学习记录_2019.02.09
    C语言学习记录_2019.02.09
    C语言学习记录_2019.02.08
  • 原文地址:https://www.cnblogs.com/LifeTechnologySupporter/p/5046952.html
Copyright © 2011-2022 走看看