`
hotfm
  • 浏览: 51088 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

iPhone开发内存管理

 
阅读更多

开发iPhone 应用程序并不难,基本上就是三个词 – “memory, memory, memory” 。iPhone OS 对内存的要求很严格,有memory leak ,杀掉; 内存使用超限额,杀掉。一个经过测试的程序,在使用过程中90%以上的崩溃都是内存问题造成的。在这里简单总结一下Object-C 内存管理。

 

基本概念

 

Object-C 的内存管理基于引用计数(Reference Count)这种非常常用的技术。简单讲,如果要使用一个对象,并希望确保在使用期间对象不被释放,需要通过函数调用来取得“所有权”,使用结束后再调用函数释放“所有权”。“所有权”的获得和释放,对应引用计数的增加和减少,为正数时代表对象还有引用,为零时代表可以释放。

函数

获得所有权的函数包括

  • alloc – 创建对象是调用alloc,为对象分配内存,对象引用计数加一。
  • copy – 拷贝一个对象,返回新对象,引用计数加一。
  • retain – 引用计数加一,获得对象的所有权。

另外,名字中带有alloc, copy, retain 字串的函数也都认为会为引用计数加一。

释放所有权的函数包括

  • release – 引用计数减一,释放所有权。如果引用计数减到零,对象会被释放。
  • autorelease – 在未来某个时机释放。下面具体解释。

autorelease

在某些情况下,并不想取得所有权,又不希望对象被释放。例如在一个函数中生成了一个新对象并返回,函数本身并不希望取得所有权,因为取得后再没有机会释放(除非创造出新的调用规则,而调用规则是一切混乱的开始),又不可能在函数内释放,可以借助autorelease 。所谓autorelease , 可以理解为把所有权交给一个外在的系统(这个系统实际上叫autorelease pool),由它来管理该对象的释放。通常认为交给 autorelease 的对象在当前event loop 中都是有效的。也可以自己创建NSAutoreleasePool 来控制autorelease的过程。

据苹果的人说,autorelease效率不高,所以能自己release的地方,尽量自己release,不要随便交给autorelease来处理。

规则

引用计数系统有自己的引用规则,遵守规则就可以少出错:

  • 获得所有权的函数要和释放所有权的函数一一对应。
  • 保证只有带alloc, copy, retain 字串的函数才会让调用者获得所有权,也就是引用计数加一。
  • 在对象的 dealloc函数中释放对象所拥有的实例变量。
  • 永远不要直接调用dealloc来释放对象,完全依赖引用计数来完成对象的释放。

有很多类都提供“便利构造函数(convenience constructors)”,它们创建对象但并不增加引用计数,意味着不需要调用release来释放所有权。很好辨认,它们的名字中不会有alloc和copy。

只要遵守这些规则,基本上可以消除所有Intrument可以发现的内存泄露问题。

容器

类似NSArray, NSDictionary, NSSet 等类,会在对象加入后引用计数加一获得所有权,在对象被移除或者整个容器对象被释放的时候释放容器内对象的所有权。类似的情况还有UIView对subview的所有权关系,UINavigationController对其栈上的controller的所有权关系等等。

其他所有权的产生

还有一些用法会让系统拥有对象的所有权。比如NSObject 的performSelector:withObject:afterDelay 。如果有必要,需要显示的调用cancelPreviousPerformRequestsWithTarget:selector:object: ,否则有可能产生内存泄露。

因这种原因产生的泄露因为并不违反任何规则,是Intrument所无法发现的。

循环引用

所有的引用计数系统,都存在循环应用的问题。例如下面的引用关系:

  • 对象a创建并引用到了对象b.
  • 对象b创建并引用到了对象c.
  • 对象c创建并引用到了对象b.

这时候b和c的引用计数分别是2和1。当a不再使用b,调用release释放对b的所有权,因为c还引用了b,所以b的引用计数为1,b不会被释放。b不释放,c的引用计数就是1,c也不会被释放。从此,b和c永远留在内存中。

这种情况,必须打断循环引用,通过其他规则来维护引用关系。比如,我们常见的delegate往往是assign方式的属性而不是retain方式的属性,赋值不会增加引用计数,就是为了防止delegation两端产生不必要的循环引用。如果一个UITableViewController 对象a通过retain获取了UITableView对象b的所有权,这个UITableView对象b的delegate又是a, 如果这个delegate是retain方式的,那基本上就没有机会释放这两个对象了。自己在设计使用delegate模式时,也要注意这点。

因为循环引用而产生的内存泄露也是Instrument无法发现的,所以要特别小心。

UIImage应用与内存管理 

UIImage加载图像的方法很多,最常用的是下面两种:

  1、用imageNamed函数

 

[UIImage imageNamed:ImageName];

  2、用NSData的方式加载,例如:

   1. NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:extension];
   2. NSData *image = [NSData dataWithContentsOfFile:filePath];
   3. [UIImage imageWithData:image];

    由于第一种方式要写的代码比较少,可能比较多人利用imageNamed的方式加载图像。其实这两种加载方式都有各自的特点。

    1)用imageNamed的方式加载时,系统会把图像Cache到内存。如果图像比较大,或者图像比较多,用这种方式会消耗很大的内存,而且释放图像的内存是一件相对来说比较麻烦的事情。例如:如果利用imageNamed的方式加载图像到一个动态数组NSMutableArray,然后将将数组赋予一个UIView的对象的animationImages进行逐帧动画,那么这将会很有可能造成内存泄露。并且释放图像所占据的内存也不会那么简单。但是利用imageNamed加载图像也有自己的优势。对于同一个图像系统只会把它Cache到内存一次,这对于图像的重复利用是非常有优势的。例如:你需要在一个TableView里重复加载同样一个图标,那么用imageNamed加载图像,系统会把那个图标Cache到内存,在Table里每次利用那个图像的时候,只会把图片指针指向同一块内存。这种情况使用imageNamed加载图像就会变得非常有效。

    2)利用NSData方式加载时,图像会被系统以数据方式加载到程序。当你不需要重用该图像,或者你需要将图像以数据方式存储到数据库,又或者你要通过网络下载一个很大的图像时,请尽量使用imageWithData的方式加载图像。

 

 

    无论用哪种方式加载图像,图像使用结束后,一定要记得显示释放内存。

 

 

UIViewController 的内存管理

      iOS3.0 后,UIViewController 多了一个叫做 viewDidUnLoad 的方法。不少人都不清楚这个方法的具体意义,苹果的文档也就一句 ”Called when the controller’s view is released from memory” 简单的解释了下,并要求你把 IBOutlet 绑定的视图给清空,为什么呢?

    先看下 UIViewController 从创建 view 到展示的流程的几个函数

    -init
    -initWithNibName:bundle:

这两个方法都是初始化一个 vc,但请注意 view 不是这时候载入的

    -loadView
    -viewDidLoad

    当一个视图准备展现时,vc 首先会判断 view 是否已经创建否则便通过之前指定的 xib 文件来初始化 view,以及绑定其他关系(若没有指定 xib 文件,则默认会搜索和 vc 同名的 xib,比如 myNameViewController 就会搜索myNameViewController.xib 文件)

    若是没有 xib 文件,你就可以在 loadview 中自己手动创建这个 viewControoler 需要的视图。接下来就是调用到 -viewDidLoad,许多人喜欢在这里做些其他事情,比如做个 http 请求、建立个数组啥的。这里若不处理正确,-viewDidUnload 激活时内存就容易泄露了,稍后提到。

    -view()appear
    -view()disappear

    这几个方法就不解释了

    -viewDidUnload

    该方法在收到内存警告,同时该视图并不在当前界面显示时候会被调用,此时该 controller  view 已经被释放并赋值为 nil
接下来你要做的是

1.    把实例变量的子视图释放(IBOulet ,以及自己添加的)

2.    其他实例变量,比如之前在 -viewDidLoaded 中实例的数据数组、http 请求释放掉

    因为当该 viewController 再次被激活准备显示时(比如 navigationControler 返回到上一级)vc 发现自己的view 为空后会重复之前的流程直到把 view 给创建起来。若没将自己额外添加的子视图,各种类实例变量释放,这里便会重新再次创建。

    于是,内存泄露了。


 

 

 

 

 

分享到:
评论
1 楼 liuxco 2011-10-14  
“另外,名字中带有alloc, copy, retain 字串的函数也都认为会为引用计数加一”这种命名规则我们要加入自己的编程规范中。

相关推荐

    ios iphone开发-内存管理

    ios iphone开发-内存管理 所有权是iPhone内存管理的核心思想,对象的所有者负责在使用完对象后进行释放。一个对象可以有多个所有者,当它没有所有者时将被设置为取消分配(deallocation)

    iPhone内存管理

    详细且全面地讲述了iPhone开发中的内存管理技术

    iphone开发官方指南-内存管理编程指南

    有大大弄成docx格式,我转换成pdf格式再次上传

    移动互联网开发笔记 VOL 2

    刊首语 征稿 在Android平台下定制系统的主屏幕 IPhone开发内存管理  Three20的使用 iOS开发者计划申请流程攻略 雅虎画报IPad版本的开发经验介绍

    iPhone 应用开发中Object-C 内存管理--千锋培训

    文档介绍了简介,基本概念,函数,获得所有权的函数包括,释放所有权的函数包括,规则,容器,其他所有权的产生,,循环引用

    iPhone开发基础教程

    Objective-C是扩展C的面向对象编程语言,也是iPhone开发用到的主要语言。. 本书结合理论知识与示例程序,全面而系统地讲述Objective-C编程的相关内容,包括Objective-C在C的基础上引入的特性和Cocoa工具包的功能及...

    iphone开发入门经典源码

    3.3 l 声明变量 3.3.2 分配、初始和释放对象 3.3 13使用方法及发送消息 3.3.4 表达式和决策 3.4 内存管理 34.1 释放对象 3.4.2 使用autorclease方法 3.4.3 保留对象 3.4.4 在dealloc中释放实例变量 34.5 释放规则 ...

    Objective-C高级编程 iOS与OS X多线程和内存管理

    帮助学习关于OC中内存管理的知识点

    IPhone开发常用技术笔记汇总

    本压缩包中包含了Iphone开发中常用到的技术总结笔记,五六十中技术方法以及季节方案,包括内存管理,方法回调,获取当前地点,自定义CELL,VIew圆角等等等,太多的奶水包,是我开发中所有的精华所在,只有你不知道的...

    iphone开发笔记

    退回输入键盘 2 CGRect 2 CGPoint & CGSize 3 设置透明度 3 设置背景色 3 自定义颜色 3 竖屏 3 横屏 3 状态栏高 3 导航栏、工具栏高 3 ...Objective-C内存管理 44 iphone更改键盘右下角按键的type 45

    《iPhone开发实战》.(Christopher Allen).pdf

     本书适合所有 iphone开发人员学习参考。... 目录 第一部分 iphone编程简介. 第1章 iphone简介2 1.1 iphone核心规范3 1.1.1 iphone的输入及输出规范3 1.1.2 iphone网络规范4 1.1.3 iphone浏览器规范5...

    iphone开发进阶

    iphone开发进阶 简要介绍iphone os的4个主要部分组成、OBJC的内存管理、归档、容器; 定制UIButton、视图切换、屏幕的触摸事件检测、使用SQLLite连接数据库等等

    object c/iphone 开发 试题

    16.内存管理 ①:程序A里有一段内存被成功申请完成之后,内存计数器就从0变为1 (这个过程是alloc); ②:然后程序B里也要使用这个内存,那么内存计数器从1变为2 (这个过程是retain); ③:紧接着程序A不需要这个内存...

    iPhone开发秘籍.part2.rar

    1.8.3 关于示例代码和内存管理的 注意事项.....18 1.9 构建Hello World 应用程序.....19 1.9.1 创建iPhone 项目.....19 1.9.2 运行主干.....20 1.9.3 定制iPhone 项目.....20 1.9.4 编辑标识信息.....21 1.9.5 使用...

    iPhone开发秘籍.part4.rar

    1.8.3 关于示例代码和内存管理的 注意事项.....18 1.9 构建Hello World 应用程序.....19 1.9.1 创建iPhone 项目.....19 1.9.2 运行主干.....20 1.9.3 定制iPhone 项目.....20 1.9.4 编辑标识信息.....21 1.9.5 使用...

    iPhone开发秘籍.part1.rar

    1.8.3 关于示例代码和内存管理的 注意事项.....18 1.9 构建Hello World 应用程序.....19 1.9.1 创建iPhone 项目.....19 1.9.2 运行主干.....20 1.9.3 定制iPhone 项目.....20 1.9.4 编辑标识信息.....21 1.9.5 使用...

    iphone3开发基础教程

    4.9 成为出色的内存使用者 67 4.10 小结 68 第5章 自动旋转和自动调整大小 70 5.1 使用自动调整属性处理旋转 71 5.1.1 指定旋转支持 71 5.1.2 使用自动调整属性设计界面 73 5.1.3 自动调整属性 74 5.1.4 设置按钮的...

Global site tag (gtag.js) - Google Analytics