`
wsqwsq000
  • 浏览: 676567 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

iPhone开发中的内存管理

 
阅读更多

 

本文转载自:http://blog.sina.com.cn/s/blog_6b9c53390100sawg.html

 

 

移动开发的特点:资源的有限性。作为手持设备,iphone的内存与传统的PC不可同日而语,这就要求我们在开发IOS程序的过程中,首要也是最重要的任务就是解决内存释放问题,本文将在网络上搜集的关于内存管理的经验予以分享。


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

 

一、基本概念

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

 

1、函数

获得所有权的函数包括

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

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

释放所有权的函数包括

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

autorelease

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

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

 

2、规则

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

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

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

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

 

3、容器

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

 

4、其他所有权的产生

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

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

 

5、循环引用

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

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

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

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

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

 

惯有规则:

规则1: 是不是我们在用对象时,只要不是自己alloc的,就不需要去release了?

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

通常,不是你alloc, copy或者retain过的,就不需要去release了。


规则2:关于循环引用中的例子,为什么将delegate赋为nil,就可以避免循环问题?

并不是将delegate赋值为nil就可以避免循环问题,实际上delegate往往都设计成assign方式的属性,而不是retain属性的。也就是说并没有获得所有权,这是保证不产生循环问题的条件。

我们在项目中,有时需要一些全局变量存储一些基本信息,那么这些全局的变量内存又是如何管理的呢?

下面是网友分享的经验,具体的网址我记不清了,我在这里直接拿过来放到我的博客了,在此向写本文的作者致敬!

正是由于众多具有开源精神的网友,技术才能更好的传播!

 

项目中有时需要一些全局变量存储应用程序运行过程中的一直存在的信息翻看了一些资料决定使用Apple官方文档推荐的Signaleton模式,使用过程很顺利但是随之而来的是关于内存管理的考虑官方文档的示例代码如下:

 

static MyGizmoClass *sharedGizmoManager = nil;

 

+ (MyGizmoClass*)sharedManager

{

    if (sharedGizmoManager == nil) {

        sharedGizmoManager = [[super allocWithZone:NULL] init];

        }

    return sharedGizmoManager;

}

 

+ (id)allocWithZone: (NSZone *)zone

{

     return [[self sharedManager] retain];

}

 

- (id)copyWithZone: (NSZone *)zone

{

     return self ;

}

 

- (id)retain

{

     return self;

}

 

- (NSUInteger)retainCount

{

     return NSUIntegerMax;  //denotes an object that cannot be released

}

 

- (void)release

{

     //do nothing

}

 

- (id)autorelease

{

     return self;

}

      可以看到上面的代码中除了为保证这个静态类的唯一性还有一个奇怪的地方则是这个类没有dealloc方法那么这个类实例化后所分配的内存是在什么时候释放的如果这个类中还含有一些其他的成员变量这些变量所分配的内存又是在何时释放的呢

      以之前C/C++平台的经验静态变量的内存被放在全局区(或称之为静态区),静态变量的内存在应用程序启动之前由系统分配,在应用程序退出之后又由系统自己回收。Cocoa基于C设计所以大致上应该也是这个样子。

      为了验证这个问题我尝试着给这个类添加了一个dealloc方法之后更加奇怪的事情就发生了我在这个类dealloc方法中设置断点并且尝试输出log,但当程序退出时不仅断点没有断下来甚至在console中连这段log的踪影都看不到我开始怀疑这段dealloc没有被调用之后我又在其他类的dealloc方法中输出log,发现一个规律dealloc方法在程序运行过程中随着类实例的释放会被调用但是当直接退出程序时(HOME),所有的dealloc都不被调用这就说明当应用退出的时候,Cocoa是不会调用实例的dealloc方法的这是意味着什么呢我猜想可能Cocoa会在应用程序退出时自己回收所有的内存并且不像C++那样去调用存在的类实例的析构方法为证实这个猜想,google最终在Cocoa With LoveApple的另一份官方文档中找到了答案并证明我的这个猜测是正确的:

      官方文档中是这么解释的: " When an application terminates, objects may not be sent a dealloc message since the process’s memory is automatically cleared on exit—it is more efficient simply to allow the operating system to clean up resources than to invoke all the memory management methods." 大致的意思就是当应用程序退出时对象不会接受到一个dealloc消息系统会自己清理所有的资源,Apple认为这样比去调用一个内存管理方法更有 效率

      Cocoa With LoveMatt关于我提问的解答是这样的: "You don't need to free data from a singleton -- it lasts until the program quits, so the dealloc method will never be invoked. If you need to close a network connection, or something else that actually needs to be ended, you should do this in a "close" method and invoke the "close" method on the singleton in your applicationWillTerminate: method of your application delegate." 意思差不多和官方一样,应用退出时,dealloc方法不会被调用,并且他建议我避免在dealloc中去作网络或其他类似的必须终止的操作,而应该将这 些操作放在applicationWillTerminate:中,以确定这些操作被执行。

     呵呵,谢谢Matt,他的这个建议确实是非常有效果的,否则我的下一个问题肯定的是:那么一定得终止的操作放在哪里执行?

所以关于这个问题的研究暂时就告一段落了,得出的结论是
1.不用担心静态全局变量的内存的问题,系统会在应用程序结束之后,回收这些内存;
2.应用程序结束时会直接回收所有的程序运行中的资源,而不调用对象的dealloc方法;
3.不要将类似网络或文件的关闭(应该是任何)操作放在类的dealloc方法中执行;

 

当然这也只是告一段落了,由此我又产生了两个问题:
1.如果系统会在程序结束后回收所有的资源,那么是否基本不用考虑什么内存泄露的问题?内存泄露只会在程序运行过程中发生?即使发生了内存泄露,应用程序退出时,系统也会回收这些内存;
2.Cocoa如何实现这一套资源管理机制的呢?只以内存说,难道系统给应用程序指定一段内存,你应用程序分配的内存只会在这一段区域里,当应用程序退出之后,我系统就直接抹了这段内存?

这两个问题想请教tinyfool  robinlu不知他们二位有没有时间。不过我的这个blog太丑了,我先把字弄大点.

文中参考资料:
1.Apple推荐Sinaleton方法的文档Cocoa Fundamental Guide:Cocoa ObjectsCreating a Singleton Instance一节 这里还有中文的
2.Cocoa With Love中关于top-level data的话题:Singletons, AppDelegates and top-level data这里讨论还了你需要全局数据的条件,还提供了另外一个方法存放全局的数据:放置在AppDelegates,并且讨论了这种方法的不妥之处,建议和我一样的新手可以拜读一下.
3.Apple解释应用程序退出时不调用dealloc方法的文档:Memory Management Programming Guide for Cococa:Object Ownship and DisposalDeallocating an Object一节的important:一段


在iphone程序中,属性合成中的retain/copy/assign有什么区别?

 

1)assign就不用说了,因为基本上是为简单数据类型准备的,原子类类型,例如CGPoint、CGFloat等,而不是NS对象们;

2)retain VS copy

  • copy 建立一个索引计数为1的对象,然后释放旧对象
  • retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1

Copy其实是建立了一个相同的对象,而retain不是:

比如一个NSString对象,地址为0×1111,内容为@”STR”

Copy到另外一个NSString之后,地址为0×2222,内容相同,新的对象retain1,旧有对象没有变化

retain到另外一个NSString之后,地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain+1

也就是说,retain是指针拷贝,copy是内容拷贝。

 

ObjectiveC中的copyc++的一样分深拷贝和浅拷贝,怎样区分这两个对象呢?我的理解是:

   (1)深拷贝,就是新拷贝一块内存交给对象使用。

   (2)浅拷贝,就是觉得拷贝内存太浪费,直接给你我的地址吧,相当于retain

 

3)怎么区分这两种对象呢?

    ObjectiveC里面只有一种情况是浅拷贝,那就是不可变对象的copy,其它的都是深拷贝(包括不可变对象mutableCopy、可变对象的的copymutableCopy)。

 

 

 

分享到:
评论

相关推荐

    ios iphone开发-内存管理

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

    iPhone内存管理

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

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

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

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

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

    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 释放规则 ...

    IPhone开发常用技术笔记汇总

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

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

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

    iphone开发笔记

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

    IPhone中文开发文档

    什么是cocoa 起步 Objective—C语言 内存管理 Target/Action 辅助对象 Key—Value Coding ,Key—Value Observing NSArrayControler NsUndoManager Archiving Coredata基本原理 User Default

    《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...

    object c/iphone 开发 试题

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

    iPhone应用程序开发指南.中文.pdf

    虚拟内存系统 13 自动休眠定时器 14 应用程序的程序包 14 信息属性列表 16 应用程序图标和启动图像 21 Nib文件 21 处理关键的应用程序任务 22 初始化和终止 22 响应中断 23 观察低内存警告 25 定制应用程序的行为 25...

    iphone开发进阶

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

    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开发秘籍.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 使用...

    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 使用...

    iphone3开发基础教程

    17.3 现实中的iPhone:本地化应用程序 398 17.3.1 查看当前区域设置 401 17.3.2 测试LocalizeMe 401 17.3.3 本地化nib文件 402 17.3.4 查看本地化的项目结构 403 17.3.5 本地化图像 405 17.3.6 本地化应用程序图标 ...

Global site tag (gtag.js) - Google Analytics