不论哪门编程语言,内存管理都一直是一个广受关注的问题,包括有垃圾回收机制的 Java (前几天还听几个搞 Android 的小伙伴在热切的讨论 Java 中的内存管理)。作为有节操的 iOS 开发者,大家对内存管理问题更是时刻保持着高度的使命感。这两年新开的项目可能都已经使用 ARC
( Automatic Reference Counting )了。想想自己刚从 MRR
( manual retain-release ) 往 ARC
过渡那会儿,虽然苹果官方一再强调可以放心得使用 ARC
,但由于在 ARR
下谨慎惯了,一时还是不太敢放心的把内存管理全都交给编译器去做。现在再看 ARC
,确实是个可以放心托付的家伙,而且它在编译器级对内存管理做了更进一步的优化。其实当苹果在 Xcode 中内置了强大的静态分析器( Clang Static Analyzer )时我就想,既然内存泄漏隐患都能在编译阶段这么精准得定位到,为什么不更进一步直接把它们解决了呢。后来果然不久苹果就推出了强大的 ARC
机制,直接帮开发者把内存管理的事儿给做了,真是家强大的公司。虽然在 ARC
下不用太过多的关注内存管理问题,但是对 OC 的内存管理机制有一个深刻的理解还是非常有必要的,而且有时你也可能不得不去维护由于历史原因还在使用 MRR
的项目。今天再看官方文档 Advanced Memory Management Programming Guide 时,依然感觉很精练,故决定将它翻译一下。
PS. 在苹果的助推下,开发者很快的从 MRR 过渡到了 ARC,包括一些固执的老程序员,这让人不禁猜测,swift 是不是也会在苹果官方的助推下,在一两年内实现从 OC 到 swift 的全面转换呢?让我们拭目以待。
概述
应用程序的内存管理是这样一个过程:在程序运行时分配内存,使用它,然后在不需要它时将其释放。一支优秀的程序通常会尽量少得使用内存。在 Objective-C 中,它也可被看做是在很多块数据和代码之间,为有限的内存资源分配使用权的一种方式。读完这个系列之后你将会了解到,要想控制好应用的内存使用,就需要精确得管理对象的生命周期,并在不再需要它们时将其释放掉。
虽然内存管理通常只针对单个对象,但是应该把管理object graphs
作为目标。要确保已不再需要的对象所占的内存都被全部释放。
Objective-C 提供了两种管理内存的方式:
-
第一种方式在本系列中被称为
manual retain-release
,或者称为 MRR,你需要监控所创建的对象来精确地管理内存。这是通过由 Foundation 的NSObject
类结合运行时环境所提供的引用计数( reference counting )模型来实现的。 -
第二种方式被称为自动引用计数,即 ARC ( Automatic Reference Counting ),使用和 MRR 一样的引用计数的方式,但不同的是该方式会在编译时期为你在适当的位置插入相应的内存管理方法调用。强烈建议在新的项目中开始使用 ARC。如果使用 ARC,可以说就不需要去理解即将介绍的有关 MRR 的知识了,但友情提示下,这些知识在某些情况下依然是需要的。更多关于 ARC 的内容,请参阅 Transitioning to ARC Release Notes 。
防止内存泄漏的最佳实践
不当的内存管理会导致两类问题:
-
释放或覆盖了仍在使用的数据
这会引起内存污染( memory corruption ),通常会导致应用 crash,或者更糟,覆盖掉用户数据。 -
没有释放掉已不再使用的数据,导致内存泄漏( memory leak )
内存泄漏是指被分配的内存已不再使用了,却没被释放掉。内存泄漏会使应用所占用的内存持续增加,从而导致糟糕的系统性能,或者应用直接被系统干掉。
站在引用计数的角度去考虑内存管理问题,常常事与愿违,因为这样往往更倾向于根据实现细节去考虑内存管理问题,而不是根据实际目标。应该站在对象持有者和object graphs
的角度去考虑内存管理问题。
Cocoa 采用了一套明确的命名约定,来表明你当前是否拥有通过某方法所返回对象的所有权。详情参阅 内存管理策略 ( 原文: Memory Management Policy )。
虽然基本的策略很简单,但仍有一些实用的套路可使内存管理更加简单,而且能帮助确保在最大限度得减少程序资源需求的同时,程序依然能够可靠健壮得运行。详情参阅 内存管理实践 ( 原文: Practical Memory Management )。
自动释放池块( Autorelease Pool Blocks )提供了一个可以延迟给对象发送release消息的机制。它的使用场景是,当想要放弃某对象的所有权,但又想避免它被直接释放掉(比如在要从某方法返回一个对象时)。详情参阅 使用自动释放池块 ( 原文: Using Autorelease Pool Blocks )。
使用分析工具调试内存问题
使用 Xcode 内置的 Clang Static Analyzer (静态分析器),可以在编译时期找出代码所存在的内存问题。
如果内存问题仍然出现,还有一些其他的工具和技巧能确定和诊断问题所在。
-
在 Technical Note TN2239 和 iOS Debugging Magic 中介绍了很多与这些工具和技巧相关的内容,特别是
NSZombie
的使用,都能很好得帮忙定位到过度释放( over-released )的对象。 -
使用 Instruments 来追踪引用计数事件,查找内存泄漏。详情参阅 Instruments User Guide 中的 Collecting Data on Your App 章节。