`
aqbbsxiao
  • 浏览: 46070 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

IOS 内存管理

    博客分类:
  • 1
 
阅读更多

小议iphone内存管理与属性



一、前言

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

      OC没有像JAVA一样的垃圾回收机制,也就是说,OC编程需要程序员手动去管理内存。这就是为什么它烦的原因,苹果却一直推崇开发者在有限硬件资源内写出最优化的代码,使用CPU最少,占用内存最小。



二、基本原理

对象的创建:

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

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

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

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

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

      [a   dealloc];

如果指针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;



三、Autorelease Pool

      为了方便程序员管理内存,苹果在OC中引入了自动释放池(Autorelease Pool)。在遵守一些规则的情况下,可以自动释放对象。但即使有这么一个工具,OC的内存仍需要程序员时刻关注(这个自动释放池跟JAVA的垃圾回收机制不是一回事,或者说,骑马都追不上JAVA的机制,可能连尘都吃不到)。

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

      //retain count = 1,但无需release

Autorelease Pool 的原理:

      autorelease pool 全名叫做NSAutoreleasePool,是OC中的一个类。autorelease pool并不是天生就有的,你需要手动的去创建它

      NSAutoreleasePool  *pool = [[NSAutoreleasePool  alloc]  init];

一般地,在新建一个iphone 项目的时候,xcode会自动地为你创建一个autorelease pool,这个pool就写在Main函数里面。

在NSAutoreleasePool中包含了一个 可变数组,用来存储被声明为autorelease的对象。当NSAutoreleasePool自身被销毁的时候,它会遍历这个数组,release数组中的每一个成员(注意,这里只是release,并没有直接销毁对象)。若成员的retain count 大于1,那么对象没有被销毁,造成内存泄露。

      默认的NSAutoreleasePool 只有一个,你可以在你的程序中创建NSAutoreleasePool,被标记为autorelease的对象会跟最近的NSAutoreleasePool 匹配。     NSAutoreleasePool  *pool = [[NSAutoreleasePool  alloc]  init];

      //Create some objects

      //do something…

      [pool  release];

你也可以嵌套使用NSAutoreleasePool  ,就像你嵌套使用for一样。

      即使NSAutoreleasePool  看起来没有手动release那么繁琐,但是使用NSAutoreleasePool  来管理内存的方法还是不推荐的。因为在一个NSAutoreleasePool  里面,如果有大量对象被标记为autorelease,在程序运行的时候,内存会剧增,直到NSAutoreleasePool   被销毁的时候才会释放。如果其中的对象足够的多,在运行过程中你可能会收到系统的低内存警告,或者直接crash。



Autorelease Pool   扩展:

      如果你极具好奇心,把Main函数中的NSAutoreleasePool  代码删除掉,然后再自己的代码中把对象声明为autorelease,你会 发现系统并不会给你发出错误信息或者警告。用内存检测工具去检测内存的话,你可能会惊奇的发现你的对象仍然被销毁了。

      其实在新生成一个Run Loop的时候,系统会自动的创建一个NSAutoreleasePool  ,这个NSAutoreleasePool 无法被删除。



      在做内存测试的时候,请不要用NSString。OC对字符串作了特殊处理

      NSString  *str  =[ [NSString alloc]  stringWithString:@”123”];

在输出str的retain count 的时候,你会发现retain count 大于1。



四、手动管理内存

      使用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新指向的对象(副本)。





六、尾声

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

相关推荐

    庄延军《iOS内存管理及优化》

    庄延军所著的《iOS内存管理及优化》是一本专注于iOS平台内存管理技巧与性能优化的书籍。它旨在帮助读者深入理解iOS内存管理机制,掌握内存优化方法,从而提升应用性能、稳定性和用户体验。 内存管理是移动应用开发...

    IOS内存管理

    IOS 内存管理 详细讲解iOS内存管理,对于深入研究内存管理的同学必须下载啊!

    iOS内存管理1

    iOS内存管理1 iOS内存管理是iOS开发中非常重要的一部分,主要是为了避免内存泄露和循环引用。iOS内存管理有两种方式,一种是 Manual Reference Counting(MRC),一种是 Automatic Reference Counting(ARC)。 ...

    IOS内存管理与软件调试

    首先,我们来谈谈iOS内存管理。iOS系统采用自动引用计数(Automatic Reference Counting, ARC)作为主要的内存管理机制。ARC负责跟踪对象的所有者,并在适当的时候释放不再使用的内存。理解如何正确使用ARC至关重要...

    ios内存管理之深浅拷贝

    本文将深入探讨iOS内存管理中的一个重要概念——深浅拷贝,以及与其相关的`retain`、`copy`和`retainCount`。这些概念对于理解Objective-C中的内存管理机制至关重要。 首先,我们需要了解iOS内存管理的基本原则,即...

    iOS高级内存管理ARC模式手动模式英文文档含示例工程

    标题中的"iOS高级内存管理ARC模式手动模式英文文档含示例工程"表明,这个压缩包提供了深入探讨iOS内存管理的资源,包括了ARC和非ARC(即手动内存管理)的相关内容。这可能包括如何在ARC环境下处理特殊情况,以及何时...

    小议iOS内存管理-中文.pdf

    ### iOS内存管理核心知识点 #### 一、前言与背景 在探讨iOS内存管理之前,有必要理解为何这一主题如此重要。对于从C++或Java转向Objective-C(简称OC)的开发者来说,OC语言的特性显得既独特又挑战性十足。与Java...

    iOS内存管理方面的面试题汇总

    在iOS开发中,内存管理是...总之,理解并熟练掌握这些iOS内存管理的知识点,对于编写高效、稳定的应用至关重要。在面试中,能深入讨论这些问题,不仅体现了开发者的专业技能,也显示了其对程序性能优化的关注和重视。

    ios开发内存管理

    ios移动开发的内存管理ppt,讲的不错,你值得拥有

    iOS 内存管理机制 详细解读

    放入栈中。此区域的内存由编译器...了解并掌握内存管理是开发iOS应用的关键,合理的内存管理能确保应用的性能和稳定性。无论是手动的MRC还是自动的ARC,都需要开发者对内存管理有深入的理解,防止内存泄漏和意外崩溃。

    IOS高级内存管理编程指南.pdf

    本文将详细介绍iOS内存管理的知识点,包括内存管理的基本概念、策略、最佳实践和实战技巧,以及Autorelease池的使用等。 ### 1. 内存管理概述 内存管理是指在程序运行时对内存进行分配、使用和释放的过程。在...

    IOS内存管理Delegate与IOS下Speex语音压缩开发

    IOS的内存管理详解。 Delegate具体详解,使用流程。 Speex的语音压缩技术。 完整详细解读。40页,通俗简单,深入浅出,并有代码示例ppt。

    ios memory 内存管理

    ### iOS内存管理的核心概念 #### ARC (Automatic Reference Counting) ARC是苹果在iOS 5中引入的一项技术,用于自动管理对象的引用计数,从而避免了手动管理内存带来的复杂性和错误。通过ARC,编译器能够自动插入...

    UnityAndroid/iOS查看真机内存使用情况插件

    2. **Android和iOS内存管理**:Android和iOS操作系统都有自己的内存管理机制。Android使用Dalvik或ART虚拟机进行内存分配和回收,而iOS则依赖于Objective-C/Swift的自动引用计数(Automatic Reference Counting, ARC)...

    ios内存管理

    iOS开发有关object-c内存管理!

    详解关于iOS内存管理的规则思考

    iOS内存管理的核心在于引用计数(Reference Counting)机制,这是Objective-C中的基础概念。当对象的引用计数为0时,系统会自动回收该对象的内存。本文将深入探讨iOS内存管理的规则及其背后的思考。 1. **生成并...

    cz-add#iOS_maker#iOS 内存管理总结1

    3.未初始化数据:末初始化的全局变量、静态变量等 1.对字符串,字典,数组进行mutableCopy操作,属于深拷贝,并且会生成一个新的可变对象 2.对可变字符

    iOS内存管理中引用计数的学习

    本文主要讨论的是iOS内存管理中的引用计数机制,这是Objective-C和Swift(早期版本)中的一种自动内存管理策略。 引用计数是一种跟踪和管理对象生命周期的方法。简单来说,一个对象的引用计数表示了有多少个指针...

    iOS内存管理

    强引用、弱引用 weak和strong只有你打开ARC时才会被要求使用

Global site tag (gtag.js) - Google Analytics