第九章内存管理
如果要从事Leopard或更高版本的MacOS程序开发使用Objective-C 2.0的垃圾回收机制,若要从事旧版本MacOS或iPhone开发则要全部阅读本章内容
一、对象生命周期
程序中的对象由生命周期,其周期包括:诞生(alloc或new)、生存(接受消息和执行操作)、 交友(借助方法的组合和参数)和消亡(被释放)
1、引用计数
Cocoa采用了一种称为引用计数(保留计数)的技术:
(1)每个对象有一个与之相关联的整数,称为他的引用计数器或保留计数器
(2)当代码访问某个对象时,改代码将该对象保留计数器值增加1,表示我要访问该对象
(3)当代码结束对象访问时,将对象的保留计数器值减1,表示不再访问该对象
(4)在保留计数器值为0时,表示不再有代码访问该对象,对象将被销毁,启动垃圾回收机制
使用方法:
(1)使用alloc、new或通过copy消息(生成接收对象副本)创建对象时对象的保留计数器数值被设置为1
(2)要增加计数器值,可以调用对象的retain方法
-(id) retain; 返回一个id类型值。
通过这种方式,可以嵌套执行带有其他方法参数的保留调用,增加对象的保留计数器值并要求对象完成某种操作
例:[[car retain] setTire : tire anIndex : 2]
表示要求car对象将其保留计数器值加1并执行setTire操作
(3)要减少对象的保留计数器值,调用对象的release方法
-(void) release;
(4)当对象因保留计数器归0将被销毁时,Objective-C自动调用dealloc方法,此方法可重写,但不要直接调用,而是要让Objective-C自动调用
-(unsigned) retainCount;
(5)获得当前保留计数器的当前值,可以调用retainCount方法
2、对象所有权object ownership
如果一个对象具有指向其他对象的实际变量,则称该对象拥有这些对象
3、访问方法中对象的保留和释放
-(void) setEngine : (Engine *) newEngine
{
[newEngine retain];//增加newEngine的保留计数器
[engine release];//减少engine的保留计数器
engine = newEngine;//将engine保存为newEngine
}
二、自动释放
1、所有对象全部入池
Cocoa中有一个自动释放池(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 ([desciption autorelease]);
}
description方法首先创建了一个新的字符串对象,然后自动释放,最后将其返回给调用的函数
2、自动释放池的销毁时间
Cocoa定期自动创建和销毁自动释放池,通常是在处理完当前事件以后执行操作。
我们可以使用任意多的自动释放对象,当不需要他们时,自动释放池将自动为你清理这些对象
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
……
[pool release];
Tip:Xcode自动生成的另一种销毁自动释放池-drain方法只是清空自动释放池而不销毁它,这种方法只适用于10.4以及更高版本,使用-release方法适用于全部版本
3、自动释放池的工作过程
举例代码:
int main(int argc, const char *argv[])
{
NSAutoreleasePool *pool; //首先创建一个自动释放池
pool = [[NSAutoreleasePool alloc] init];
RetainTracker *tracker; //新的tracker对象被创建
tracker = [RetainTracker new]; //保留计数器值为1
[tracker retain]; //使保留计数器值增加到2
[tracker autorelease]; //将对象添加到自动释放池,值仍为2
[tracker release]; //释放对象,保留计数器值为1,对象仍存在
NSLog(@"releasing pool");
[pool release]; //销毁自动释放池,NSAutoreleasePool是一个普通对象遵从相同的内 存管理规则,因为创建时发送了alloc消息其保留计数器为1,执行 release后变为0,自动释放池将被销毁
return(0); //函数返回0表示全部操作成功执行
}
三、Cocaoa内存管理规则
规则:
(1) 当使用new、alloc或copy方法创建一个对象时,该对象的保留计数器值为1
当不再使用该对象时,向该对象发送release或autorelease消息,该对象将被销毁
(2) 当对象被设置为自动释放,我们不需要执行任何操作来确保该对象被清理
如果要在一段时间内拥有该对象,则需要保留它并确保操作完成时释放他
(3) 如果保留了某个对象,需要释放或自动释放该对象,需保持retain和release方法的使 用次数相等
1、临时对象
使用任何其他方法(非new、alloc或者copy)获得对象不需要关心如何销毁该对象
举例代码:
NSMutableArray *array;
array = [NSMutableArray arrayWithCapacity : 17];
arrayWithCapacity不属于alloc、new、copy方法,使用该方法返回的对象保留计数器值为1并且已经被设置为自动施放
举例代码:
NSColor *color;
color = [NSColor blueColor];
blueColor返回一个全局单例对象,这个对象永远不会被销毁
2、拥有对象
(1)使用new、alloc或copy方法获得对象,手动释放
举例代码:
-(void) doStuff
{
flonkArray = [NSMutableArray new];
}
-(void) dealloc
{
[flonkArray release];
[super dealloc];
}
(2)使用非new、alloc或copy方法获得对象,自动释放
-(void) doStuff
{
flonkArray = [NSMutableArray new];
[flonkArray retain]; //唯一的不同点
}
-(void) dealloc
{
[flonkArray release];
[super dealloc];
}
(3)再循环中清空自动释放池
举例代码:
NSAutoreleasePool *pool;
pool = [[NSAutorelease alloc] init];
int i;
for(i = 0; i < 1000000; i++){
id object = [someArray objectAtIndex : 1];
NSString *desc = [object description];
if(i % 1000 == 0){ //每循环1000次时清理一次自动释放池,如果不这样 [pool release]; //做将会在1百万次循环时清理内存,程序将非常慢
pool = [[NSAutorelease alloc] init];
}
}
[pool release];
3、垃圾回收
Objective-C 2.0引入了自动内存管理机制,也称垃圾回收
启动垃圾回收功能:转到项目信息窗口的Build选项卡,然后选择Required [-fobjc-gc-only]选项即可
Tip: -fobjc-gc是为了使代码既支持垃圾回收又支持对象的保留和释放
启用垃圾回收以后,内存管理命令全部变成了空操作指令,不执行任何操作
当没有指针指向某个对象时,该对象将被视为垃圾,因此,如果将该对象实际变量赋值为nil可以使垃圾回收器知道该对象可以被清理了
垃圾回收器也是在事件结束后出发的