iOS下内存管理的基本思想就是引用计数,通过对象的引用计数来对内存对象的生命周期进行控制。具体到编程时间方面,主要有两种方式:

1:MRR(manual retain-release),人工引用计数,对象的生成、销毁、引用计数的变化都是由开发人员来完成。

2:ARC(Automatic Reference Counting),自动引用计数,只负责对象的生成,其他过程开发人员不再需要关心其销毁,使用方式类似于垃圾回收,但其实质还是引用计数。

iOS不支持垃圾回收机制,这点与Mac OS有所不同。

ARC是Xcode 4.2之后加入的新特性,可能很多开发人员并不习惯使用,但使用ARC给开发带来的便利是显而易见的,鼓励大家都去尝试一下。

ARC的具体介绍及使用细则大家可以参考苹果官方文档,本文主要介绍MRR。

一:基本原则

关于MRR,我总结了一句话:是你的,就是你的;不是你的,就不是你的。

虽然看上去比较废话,但揭示了MRR内存管理里的一个核心原则,“只负责你拥有的对象的生命周期”,也就是说,如果你对一个对象有所有权,那么你就要负责其回收的工作,否则,你不需要,也不能取回收你不拥有的对象。

那些对象属于“拥有”范畴呢?

1:所有使用alloc, new, copy或mutabelCopy,以及这些关键词开头的函数返回的对象,你都是拥有所有权的,也就是要负责这些对象的内存回收工作。这是iOS开发中的一种约定,所以,当你编写自己的alloc, new或copy类型的函数时,也请遵循这样的命名规范。

2:retain返回的对象,拥有所有权。例如显示调用retain函数返回的结果,或者synthesize 的retain类型的成员变量。

3:所有使用其他函数返回的对象,没有所有权。

4:返回的对象的引用,没有所有权。

5:autorelease返回的对象没有所有权。

举例说明:

[cpp] view plaincopy

  1. //1:使用alloc函数生成,有所有权

  2. NSString* stringA = [[NSString alloc] init];

  3. stringA = @"abc";

  4. [stringA release];

  5. //2:使用initWithString返回对象,无所有权

  6. NSString* stringB = [NSString stringWithString: @"abc"];

  7. //3:显示调用retain返回对象, 拥有所有权

  8. NSString* stringC = [[NSString stringWithString: @"abc"] retain];

  9. stringC = @"def";

  10. [stringC release];

  11. //4:retain类型成员变量,拥有所有权

  12. @property (retain) NSString* stringD;

  13. @synthesize stringD = _stringD;

  14. - (void) dealloc

  15. {

  16. [_stringD release];

  17. [super dealloc];

  18. }

  19. //5:通过引用返回的对象无所有权

  20. NSString** stringERef = [self stringRef];

  21. *stringERef = @"abc";

二:成员变量的内存管理

注意上面的例子4,将成员变量声明为retain并synthesize时,编译器会帮你生成对应变量的setter与getter,其中, 生成的setter形式如下:

[cpp] view plaincopy

  1. - (void) setStringD: (NSString*) newString

  2. {

  3. [newString retain];

  4. [_stringD release];

  5. _stringD = newString;

  6. }

首先获得newString的所有权,然后释放掉已经拥有的_stringD(其实此对象有可能并没有被释放,取决与其release后的引用计数是否为0), 再将newString的值赋给_stringD,这样,获得了新的,释放了旧的,就完成了对象所有权的释放与获得。注意retain与release的调用顺序,避免了同一个对象赋值引起的悬空指针问题。

当成员变量声明为assign和copy时,其生成的setter形式如下:

[cpp] view plaincopy

  1. //copy

  2. - (void) setStringD: (NSString*) newString

  3. {

  4. NSString* temp = [newString copy];

  5. [_stringD release];

  6. _stringD = temp;

  7. }

  8. //assign

  9. - (void) setStringD: (NSString*) newString

  10. {

  11. _stringD = newString;

  12. }

copy类型的成员变量也是拥有所有权的,也要在dealloc函数中显式释放。而assign不是。

很多人对self.stringD = @"abc"的调用形式比较困惑; 其实,编译器会将此语句自动转换为[self.setStringD:@"abc"];

还有一点要注意,声明成员类型时只有copy,没有mutableCopy,那么,如果一个成员变量是MutableArray,且被声明成copy类型,但编译器生成的setter中调用的是copy,而不是MutableCopy,所以,向MutableArray类型的成员插入数据时就会报错,因为其保存的真正类型不是MutableArray,而是Array. 这种情况有两个解决方案:一是将改变量类型声明为retain形式; 二是手动编写setter,调用mutableCopy函数。

NSObject函数声明了dealloc函数来清理内存,所有有“retain, copy”类型成员变量的类都要实现这个函数。在其内,要调用相应对象的release函数,不要忘记在【最后】调用[super dealloc];

说到成员变量的声明周期,还要提一下IBOutlet类型的变量,默认情况下, IBOutlet对象的类型都是retain的,由于这些对象来自界面文件(xib, storyboard),所以其出事化过程无需关心,其他处理方式与普通成员变量大体相同,只有一点,需要在-  (void) viewDidUnload函数中将这些变量置空, 如下所示:

[cpp] view plaincopy

  1. - (void) viewDidUnload

  2. {

  3. self.outletA = nil;

  4. self.outletB = nil;

  5. [super viewDidUnload];

  6. }

当出现内存警告时,viewDidUnload将会被调用。前面提到过,self.outletA = nil 等价于[self setOutletA: nil], 所以,在出现内存警告的情况下,IBOutlet类型的对象会被释放。

三:容器对象与内存管理

iOS中,容器对象对其内的对象拥有所有权,也就是说,当一个对象被插入到容器内时,其retainCount会加一,从容器内移除时,其retainCount会减一,当容器本身被release时,期内所有对象的retainCount都会减一。如下代码所示:

[cpp] view plaincopy

  1. NSString* stringA = [[NSString alloc] init];//stringA的retainCount: 1

  2. NSArray* array = [[NSArray alloc] init];

  3. [array addObject: stringA];//stringA的retainCount:2

  4. [stringA release];//stringA的retainCount:1

  5. [array release];//retainCount: 0

四:稀缺资源的管理

稀缺资源包括文件,网络连接,缓存等,这些资源是很关键的系统资源,系统内其他应用可能会随时需要这些资源,所以,这些资源就不适合作为类的成员变量了,因为dealloc的实际调用时间,是否真正调用是我们无法控制的,很有可能造成稀缺资源被无意义的占用,二其他应用却无法获得相应资源。所以,随时申请随时释放是最好的选择。

五:AutoRelease

简单说,autorelease对象的释放动作由AutoReleasePool完成,所有autorelease对象在其【对应】的AutoReleasePool释放的过程中,都会受到一条release消息,也就是说,pool析构的实际也就是autorelease对象析构的时机,注意,这里的【对应】指的是离改autorelese最近的那一个AutoRelasePool。

有这么几种情况必须自己创建AutoReleasePool:

1:程序没有界面,也就是没有消息循环的程序,

2:一个循环内创建大量临时的autorelease对象,那么写法最好是这样的:

[cpp] view plaincopy

  1. NSAutoReleasePool* outPool = [[NSAutoReleasePool alloc] init];

  2. for (int index = 0; index != 1000000; ++index) {

  3. NSAutoReleasePool* pool = [[NSAutoReleasePool alloc] init];

  4. NSString* temp = [[[NSString alloc] init] autorelease];

  5. [pool drain];

  6. }

  7. [outPool release];

若果没有循环中的pool, 那么直到结束循环之前,这1000000个autorelease 临时对象都不会被释放掉,占用大量内存。

3:线程函数内需要有AutoReleasePool对象,否则期内生成的autorelease对象在线程函数结束时不会被释放(此条对Cocoa-Touch不适用)。

4:普通函数返回对象,这里的普通函数指的是非alloc, new, copy, mutablecopy开头的函数,为了确保返回的对象有效,需要将返回的对象设为autorelease,如下所示:

[cpp] view plaincopy

  1. - (NSString*) returnString

  2. {

  3. NSString* tempString = [[NSString alloc] init];

  4. tempString = @"autorelease";

  5. return [tempString autorelease];

  6. }

实际上,iOS SDK中绝大多数普通函数返回的都是autorelease对象。

六:其他注意事项

1:避免循环引用,如果两个对象互相为对方的成员变量,那么这两个对象一定不能同时为retain,否则,两个对象的dealloc函数形成死锁,两个对象都无法释放。

2:不要滥用autorelease,如果一个对象的生命周期很清晰,那最好在结束使用后马上调用release,过多的等待autorelease对象会给内存造成不必要的负担。

3:编码过程中,建议将内存相关的函数,如init, dealloc, viewdidload, viewdidunload等函数放在最前,这样比较显眼,忘记处理的概率也会降低。

4:AutoReleasePool对象在释放的过程中,在IOS下,release和drain没有区别。但为了一致性,还是推荐使用drain。

参考:Advanced Memory Management Programming Guide

iOS内存管理编程指南的更多相关文章

  1. iOS 内存管理-copy、 retain、 assign 、readonly 、 readwrite、nonatomic、@property、@synthesize、@dynamic、IB_DESIGNABLE 、 IBInspectable、IBOutletCollection

    浅谈iOS内存管理机制 alloc,retain,copy,release,autorelease 1)使用@property配合@synthesize可以让编译器自动实现getter/setter方 ...

  2. iOS内存管理策略和实践

    转:http://www.cocoachina.com/applenews/devnews/2013/1126/7418.html 内存管理策略(memory Management Policy) N ...

  3. iOS内存管理(objective-c)

    移动app开发中,由于移动设备内存的限制,内存管理是一个非常重要的话题.objective-c的内存管理,不仅是面试当中老生常谈的一个必问话题,也是日常项目开发中,特别需要重视的环节.对于笔者这种以j ...

  4. iOS内存管理布局及管理方案-理论篇

    苹果设备备受欢迎的背后离不开iOS优秀的内存管理机制,那iOS的内存布局及管理方案是怎样的呢?我们一起研究下. 内存管理分为五大块 栈区(stack):线性结构,内存连续,系统自己管理内存,程序运行记 ...

  5. iOS内存管理

    iOS内存管理的方式是引用计数机制.分为MRC(人式引用计数)和ARC(自动引用计数). 为什么要学习内存管理? 内存管理方式是引用计数机制,通过控制对象的引用计数来实现操作对象的功能.一个对象的生命 ...

  6. 【Bugly干货分享】iOS内存管理:从MRC到ARC实践

    Bugly 技术干货系列内容主要涉及移动开发方向,是由Bugly邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 对于iOS程序员来说,内存管理是入门的 ...

  7. iOS内存管理个人总结

    一.变量,本质代表一段可以操作的内存,她使用方式无非就是内存符号化+数据类型 1.保存变量有三个区域: 1>静态存储区 2>stack 3>heap 2.变量又根据声明的位置有两种称 ...

  8. IOS内存管理学习笔记

    内存管理作为iOS中非常重要的部分,每一个iOS开发者都应该深入了解iOS内存管理,最近在学习iOS中整理出了一些知识点,先从MRC开始说起. 1.当一个对象在创建之后它的引用计数器为1,当调用这个对 ...

  9. iOS内存管理(一)

    最近有时间,正好把iOS相关的基础知识好好的梳理了一下,记录一下内存相关方面的知识. 在理解内存管理之前我觉得先对堆区和栈区有一定的了解是非常有必要的. 栈区:就是由编译器自动管理内存分配,释放过程的 ...

随机推荐

  1. 夺命雷公狗---DEDECMS----31dedecms数据库创建一张表完成curl操作

    首先我们创建一张测试表,格式如下所示: 然后我们还是在plus目录下创建一个test3.php进行测试,首先写一个添加的: <?php header("Content-Type:tex ...

  2. VC6.0 error LNK2001: unresolved external symbol _main(转)

    学习VC++时经常会遇到链接错误LNK2001,该错误非常讨厌,因为对于编程者来说,最好改的错误莫过于编译错误,而一般说来发生连接错误时,编译都已通过.产生连接错误的原因非常多,尤其LNK2001错误 ...

  3. JSP-08-第三方控件的使用

    添加图片 下载 commons-fileupload-1.2.2.jar和commons-io-2.4.jar 导入项目 在添加涂抹的页面设置表单属性  enctype=”multipart/form ...

  4. [php/html/CSS]给Aptana3 安装 Emmet插件

    aptana studio3 安装 zencoding(Emmet) 插件 zen coding 更名为Emmet emmet 谷歌主页地址:http://code.google.com/p/zen- ...

  5. 解决PHP在IE浏览器下载文件,中文文件名乱码问题

    前提:我们网站所有文件全部使用的是UTF-8 NO BOM的编码方式 1.找测试重现.360浏览器下载的呵呵,果然文件名是乱码.再请测试在ie浏览器下测试.IE9,8,7也全部是乱码.查看编码就是UT ...

  6. XAMPP和Bugfree详细教程

    一.XAMPP安装配置 xampp是一款跨平台的集成 apache + mysql + php环境,是的配置AMP服务器变得简单轻松,支持windows,solaris, 下载地址:http://so ...

  7. 改Bug总结

    [1]屏蔽取舍法 屏蔽取舍,即所谓与问题无关的前后“语境”完全可以忽略,首先屏蔽掉,再根据问题复现路径查看问题发生的区间,然后逐近锁定“病灶”,确定需要修改的目标. [2]追溯原形法 追溯原形,即需要 ...

  8. iOS 学习笔记 三 (2015.03.05)

    服务和特征都是用UUID来唯一标识的,UUID的概念如果不清楚请自行google,国际蓝牙组织为一些很典型的设备(比如测量心跳和血压的设备)规定了标准的service UUID(特征的UUID比较多, ...

  9. vi的使用规则

    1.vi的基本概念 基本上vi可以分为三种状态,分别是 命令模式(command mode).插入模式(Insertmode)和  底行模式(last line mode) 各模式的功能区分如下: 1 ...

  10. 【PHP设计模式 04_GongChang.php】 工厂方法

    <?php /** * [工厂方法] * 之前 03.php 简单工厂,如果再增加一个oracle客户端,就需要再次修改服务端Factory的代码. * 在面向对象设计法则中,有一个重要的[开闭 ...