内存管理
第29条:理解引用计数
通过引用计数来管理内存,当对象使用 new
、alloc
、retain
,创建的时候,其 引用计数+1
。当使用release
的时候,引用计数-1
。当其引用计数为0的时候,对象会被释放,同时需要将指针置 nil,防止出现悬垂指针
。
自动释放池
:当对象调用 release
会立即递减对象的引用计数,有时候可以不调用它。改为调用 autorelease
,此方法会在稍后递减计数,通常会在下一次事件循环的时候递减。此方法可以保证对象在跨越方法调用边界
后一定存活。使用 autorelease
可以延长对象生命周期。
1 | - (NSString *)stringValue { |
由于返回的 str 对象稍后将自动释放,所以无需再执行内存管理操作。但是如果要持有该对象的话(比如要将其设置给实例变量),那就需要保留,并稍后释放:
1 | _instanceVariable = [[self stringValue] retain]; |
第30条:用 ARC 简化引用计数
- 在 ARC 环境中,引用计数实际上还是要执行的,只不过引用计数现在是由 ARC 自动添加。
- 不能使用
retain
、release
、autorelease
、dealloc
方法。 - 若方法名以
alloc
、new
、copy
、mutableCopy
开头,则返回的对象归调用者所有。 - 不使用 ARC 情况下,在实现单例类的时候,因为单例不可以释放,所有我们经常个覆写
release
方法,将其替换为空操作
。 - ARC 下会借用 Objective-C++ 中的一项特性来清理对象,回收 Objective-C++ 对象时,待回收对象会调用 C++ 对象的析构函数。编译器如果发现对象里含有 C++ 对象,就会生成
.cxx_destruct
的方法。在此方法中生成清理内存的代码。 - ARC 只负责管理 Objective-C 对象内存,CoreFoundation 对象不归 ARC 管理,开发者必须适时调用 CFRetain/CFRelease。
第31条:在 dealloc 方法中只释放引用并解除监听
- 在 dealloc 方法中,ARC 会通过自动生成
.cxx_destruct
方法,并自动释放所有的 Objective-C 对象。 - 在此方法中移除通知
- 如果不使用 ARC ,那么最后还需要调用
[super dealloc]
。ARC 会自动执行此操作。 - 如果对象持有文件描述符等资源,应该撰文写一个方法来释放此种资源。这样的类要和使用者约定:用完资源后必须调用 close 方法。
- 执行异步任务的方法不应该在 dealloc 里调用。只能在正常状态下执行的那些方法也不应该在 dealloc 里调用,因为此时对象已处在正在回收的状态了。
第32条:编写异常安全代码
时要注意内存管理问题
- 在非 ARC 下,使用
@try...@catch
来捕获异常,当异常发生时,程序直接执行@catch
中的语句。忽略了@try
中的内存管理语句。最好的做法是将@try
中的内存管理语句放在@finally
当中。 - 默认情况下,ARC 不生成安全处理异常的所需要的清理代码。开启编译器
-fobjc-arc-exceptions
标志后,可以生成这种代码,不过会导致应用程序变大。降低运行效率。
第33条:用弱引用避免循环引用
- 将某些引用设置为
weak
,避免循环引用。 - weak 引用可以自动清空,也可以不自动清空。自动清空是随着 ARC 而引入的新特性,由运行时系统来实习那。在自动清空功能的弱引用上,可以随意读取其数据,因为这种引用不会指向已经回收过的对象。
第34条:使用 autoreleasepool
来降低内存峰值
- 当程序当中需要创建大量的临时对象(比如循环),会造成内存增长,可以考虑将临时代码放在
autoreleasepool
当中,那么在循环中自动释放的对象就会放在这个池,而不是线程的主池里面。 autoreleasepool
存放在栈中,对象收到autorelease
消息后,系统将其放入最顶端的池里。
第35条:用僵尸对象
调试内存管理问题
- 向已经回收的对象发送消息是不安全的,因为这有时可以,有时不行。完全取决于对象所占内存有没有为其他内容所覆写,而这块内存有没有移作他用,又无法确定。此时需要开启僵尸对象进行检查。
- 系统在回收对象的时候,可以不将其真的回收,而是把它转化为僵尸对象。通过
NSZombieEnabled
可开启此功能。
第36条:不要使用 retainCount
- 不要使用 retainCount 来查看对象是否还被持有,因为任何给定时间点上
绝对引用计数
都无法反应对象生命周期的全貌。引用计数可能处在自动释放池中,retainCount 可能不准确。 - ARC 后,retainCount 就被废止了。