第9章 内存管理
9.1 对象生命周期
9.1.1 引用计数
一个对象被从堆中分配出来之后,我们需要明确的知道是谁拥有了这个对象,因为只有拥有这个对象的所有者能够销毁它。但我们在实际使用过程中, 这个对象可能被传递给另一个对象(例如通过传递指针参数),一旦这个过程复杂,我们很难确定谁最后拥有了这个对象。
使用引用计数就可以抛开这个问题,我们不需要再去关心谁拥有了这个对象,因为我们把管理权交割给了对象自己。当这个对象不再被任何人使用时,它自己负责销毁自己。
引用计数器(保留计数器):每个对象都有一个与之相关的整数,当某段代码需要访问一个对象时,该代码就将该对象的保留计数器值加1,表示“我要访问该对象”;反之,则减1,表示不再访问该对象。当保留计数器值为0时,表示不再有代码访问该对象,它将被销毁。
使用alloc、new方法或通过copy消息创建一个对象时,对象保留计数器值置为1。
-(id) retain; 计数加一,返回id类型的值,可以在接受消息同时调用retain
-(oneway void) release; 计数减一
-(NSUInteger) retainCount; 获取计数值
保留计数器值为0将被销毁时,Objective-C会自动发送一条dealloc消息,可以重写dealloc方法来释放特殊的资源,dealloc方法会在对象销毁时自动调用。
9.1.3 访问方法中的保留和释放
一种合理的访问方法
- (void) setEngine: (Engine *) newEngine
{
[newEngine retain];
[engine release];
engine = newEngine;
}
9.1.4 自动释放
自动释放池(autorelease pool),一个存放对象的池(集合),能够自动释放。
NSObject类提供了一个autorelease方法:
- (id) autorelease;
该方法预设定了一条在将来某个时间发送的release消息,其返回值是接收消息的对象。当给一个对象发送autorelease消息时,实际上是将该对象添加到NSAutoreleasePool中。当自动释放池被销毁时,会向该池中所有对象发送release消息。
示例
- (NSString *) description
{
NSString * description;
description = [[NSString alloc] initWithFormat: @"I am %d years old", 4];
return([description autorelease]);
}
用下面的代码调用
NSLog(@"%@", [someObject descripion]);
自动释放池的创建和销毁
自动释放池以栈的形式实现,创建和销毁的代价很小。
通过@autoreleasepool关键字(推荐使用)
使用@autorelease{}时,任何在花括号里定义的变量在花括号外无法使用
通过NSAutoreleasePool对象
NSAutoreleasePool *pool=[NSAutoreleasePool new];
…
[pool release];//释放该池
在使用AppKit或UIKit的时候,自动释放池会在明确的时间创建或释放,比如在处理当前用户事件的时候。
9.2 Cocoa的内存管理规则
- 当使用new、alloc或copy方法创建一个对象时,对象的保留计数器数值为1。当不再使用该对象时,应该向该多想发送release或autorelease消息,销毁对象
- 当通过其他方法获得一个对象时,假设该对象的保留计数器的值为1,而且已经被设置为自动释放,则不需要执行任何操作来确保该对象得到清理。如果打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它
- 如果保留了某个对象,就需要(最终)释放或自动释放该对象。必须保持retain和release方法的使用次数相等
如果我使用了new,clloc或copy方法获得了一个队,则我必须释放或自动释放该对象。
内存管理规则
获得途径 | 临时对象 | 拥有对象 |
---|---|---|
alloc/new/copy | 不再使用时释放对象 | 在dealloc方法中释放对象 |
任何其他方法 | 不需要执行任何操作 | 获得对象时保留,在dealloc方法中释放对象 |
自动释放池被清理的时间是完全确定的:要么是在代码中你自己动手销毁,要么是使用AppKit时在事件循环结束时销毁。
9.2.3 垃圾回收
Objective-C 2.0引入的自动内存管理机制,也称垃圾回收。
iOS中无法使用垃圾回收的,主要原因是无法知道垃圾回收器什么时候会起作用。
9.2.4 自动引用计数
ARC在编译时进行工作,会在需要的地方自动插入retain和release语句,无需自己动手。
1、 有时用weak会好一些
当指针志向某个对象时,如果管理它的内存(通过retain和release),就拥有这个对象的强引用(strong reference),不管理,拥有弱引用。
保留循环(retain cycle):循环引用导致内存泄漏
弱引用和归零弱引用可以解决这个问题
2、 一辆新车
Xcode可以将已有的项目转换为支持ARC的。但必须禁用垃圾回收机制。
3、 拥有者权限
为了便于ARC工作,必须告诉编译器哪个对象时指针的拥有者
9.3 异常
支持异常特性,需要在Xcode中启用Enable Objective—C Exceptions项。
捕获异常
@try{
} @catch(NSException *exception){
} @finally{
}
抛出异常
@throw theException或 [theException raise]
@try{
NSException *e=...;
@throw e;
} @catch(NSException *exception){
@throw; //rethrow e.
} @finally{
}
这里finally代码块会在@throw引发下一次异常处理调用前执行,@finally是在@throw发生之前调用。