1、概念理解
举例:以图书馆为例,如果每个人都只借不还,那么图书馆最终将会因无书可借而倒闭。
概念:当程序运行结束时,操作系统将收回占用的资源,但是只要程序还在运行,它会一直占用资源,如果不进行清理,某些资源将被耗尽,程序有可能会崩溃,内存管理就是确保在需要的时候分配内存,不需要使用的时候释放内存。
2、对象生命周期
包含诞生(通过alloc或者new方法实现)、生存(接受信息并执行操作)、交友(通过复合以及向方法传递参数)、以及死去(释放内存)。
3、引用计数
cocoa采用引用计数了解对象的生命周期是否结束了。
(1)计数方法
a.计数器值加1,表示某段代码需要访问一个对象;
b.计数器值减1,表示某段代码结束访问一个对象;
c.计数器值为0,表示不再有代码访问该对象了,因此它将销毁,其占用的内存被系统回收以便重用。
(2)使用方法
a. 当计数器值为0时,object-c会自动向对象发送一条dealloc消息;可以重写dealloc方法,把已经分配的全部相关资源释放。
注意:一定不要直接调用dealloc方法,,object-c会自动销毁对象时自动调用它。
b.当需要获得引用计数器当前的值,可以发送retainCount消息。
//retain、release、retainCount的方法申明 1 -(id)retain; 2 - (oneway void) release; 3 - (NSUInteger) retainCount;
retain方法返回一个id类型的值,通过这种方式,可以在接受其他消息的同时进行retain调用,增加对象的保留计数器的值和傲气对象完成某种操作。例如:
[[car retain]setTire:atIndex:2]; 表示要求car对象将其保留计数器的值加1并执行setTire操作。
(3)案例
a.创建一个RetainTracker类的对象,该对象在初始化和销毁时调用了NSLog()函数,当对象的保留计数器的值归0时,将自动发送dealloc消息,例子中,init和dealloc这两个方法使用NSLog()输出一条信息,表明他们被调用了。
1 @interface RetainTracker : NSObject 2 @end//RetainTracker 3 4 @implemetation RetainTracker 5 -(id) init 6 { 7 if (self = [super init]) 8 { 9 NSLog(@"init : Reetain count of %d." , [self retainCount]); 10 } 11 return(self); 12 }//init 13 14 -(void)dealloc 15 { 16 NSLog(@"dealloc called. Bye Bye."); 17 [super dealloc]; 18 }//dealloc 19 @end//RetainTracker
在main()函数中创建了一个新的RetainTracker类的对象,并间接调用了由Retain-Tracker类定义的两个方法。当一个新的RetainTracker类的对象创建完毕后,就会向它发送retain消息或release消息,以增加和减少对象的保留计数器的值,以下是NSLog()函数有趣的输出结果。
1 int main(int argc,const char *argv[]) 2 { 3 RetainTracker *tracker = [RetainTracker new]; //count:1 4 5 [tracker retain]; //count:2 6 NSLog(@"%d",[tracker retainCount]); 7 8 9 [tracker retain]; //count:3 10 NSLog(@"%d",[tracker retainCount]); 11 12 [tracker release]; //count:2 13 NSLog(@"%d",[tracker retainCount]); 14 15 [tracker release]; //count:1 16 NSLog(@"%d",[tracker retainCount]); 17 18 [tracker retain]; //count:2 19 NSLog(@"%d",[tracker retainCount]); 20 21 [tracker release]; //count:1 22 NSLog(@"%d",[tracker retainCount]); 23 24 [tracker release]; //count:1 25 return(0); 26 }//main
运行后结论:
-------------------------------------------------------------------------------------------------------------------------------------------
init Retain count of 1.
2
3
2
1
2
1
dealloc called. Bye Bye.
-------------------------------------------------------------------------------------------------------------------------------------------
4、对象所有权
回忆下,car类的变量engine的存取方法:
1 //car类的变量engine的存取方法 2 -(void) setEngine : (Engine *) newEngine; 3 4 //在main()函数中调用该方法: 5 Engine *engine = [Engine new]; 6 [car setEngine : engine];
那么那个实体拥有engine对象?
car类正在使用engine底线,所以不可能是main()函数;main()随后可能会使用engine对象,也不可能是car的;解决办法是car类保存engine对象,将engine对象的保留计数器的值增加到2.
5、访问方法中的保留和释放
setEngine方法的第一个内存管理版本
1 -(void)setEngine : (Engine *)newEngine 2 { 3 [newEngine retain]; 4 [engine release]; 5 engine = [newEngine retain]; 6 }//setEngine 7 //首先保留新的engine对象,即使newEngine和engine是同一个对象,保留计数器的值也会先增加,然后立即减少,由于没有归0,engine对象也没被销毁,就不会引发错误了。 8 //假设先保留新对象,在释放对象就会出问题。
6、自动释放
description方法的例子(该方法返回一个用来描述对象信息NSString类型值)
1 -(NSString *) descrinption 2 { 3 NSString *description; 4 description = [[NSString alloc] initWithFormat: @"I am %d years old",4]; 5 6 return(description); 7 }//description 8 //使用alloc 方法创建一个新的字符串实例(alloc方法将该对象的保留计数器的值设置为1),然后返回该字符串实例。 9 //调用description方法的代码将返回的字符串赋在某个变量中,并在使用完毕后将其释放 10 NSString *desc = [someObject description]; 11 NSLog(@"%@",desc); 12 [desc release];
7、所有对象放入池中
Cocoa中有一个自动释放池(autorelease pool)概念:用来存储对象的集合,并且能够自动释放。
NSObject类提供一个叫autorelease的方法:
-(id)autorelease;
该方法预先设定了一条在未来某个时间发送的release消息,其返回是接收这条信息的对象,当给一个对象发送autorelease消息时,实际上是该对象添加到了自动释放池中,当自动释放池被销毁是,会像该池中的所以对象发送release消息。
-(id)autorelease; -(NSString *) descrinption { NSString *description; description = [[NSString alloc] initWithFormat: @"I am %d years old",4]; return(description); }//description NSLog(@"%@", [description autorelease] ); //因为description方法首先创建了一个新的字符串对象,然后自动释放该对象最好将其返回给NSLog()函数,所以内存管理问题解决了。由于description方法中的字符串对象是自动释放的,该对象暂时被放入了当前活动的自动释放池中,等到调用NSLog()函数的代码结束后,自动释放池会被自动销毁。
8、自动释放池的销毁时间
介绍2种方法创建一个自动释放池
1、通过@autoreleaspool关键字
2、通过NSAutoreleasepool对象
在Foundation库工具集中,创建和销毁自动释放池由@autorelease关键字完成。当你使用@autorelease{}时,所有在花括号里的代码都会被放入这个新的池子中,如果你的程序是密集型的可以使用这种自动释放池。
注意:任何在花括号里定义的变量在括号外都无法使用;使用NSAutoleasePool对象,如果你使用了这个方法,创建和释放NSAutoreleasePool对象之间的代码就会使用这个新的池子。
NSAutoreleasePool *pool; pool = [NSAutoreleasePool new]; ~ [pool release]; //创建一个自动释放池后,该池子就会成为活动的池子,释放该池后,其保留计数器的值归0,然后该池被销毁,销毁过程中,该池将释放其包含的所有对象
9、自动释放池的工作流程
1 int main(int argc , const char *argv[]) 2 { 3 //创建自动释放池 4 NSAutoreleasePool *pool; 5 pool = [[NSAutoreleasePool alloc] init ]; 6 7 //每次向某对象发送autorelease消息,该对象会被添加到自动释放池中 8 RetainTracker *tracker; 9 tracker = [[RetainTracker new]];//count:1 10 11 //创建一个新的tracker对象,因为创建时接收了一条new消息,所以保留计数器值为1,接下来保留该对象,保留计数器增加到2 12 [tracker retain];//count:2 13 //该对象被自动释放,但其保留计数器值仍然不变; 14 [tracker autorelease]; //count:still 2 15 //注意我们之前创建的自动释放池中现有一个引用指向该对象,当自动释放池被销毁时,将tracker对象发送一条release消息 16 [tracker release];//count:1 17 //之后释放该对象以抵消之前对他执行的保留操作,该对象的保留计数器值仍大于0,所以处于激活状态 18 NSLog(@"releasing pool"); 19 [pool release]; 20 21 @autoreleasepool 22 { 23 RetainTracker *tracker2; 24 tracker2 = [RetainTracker new]; //count:1 25 [tracker2 retain];//count:2 26 [tracker2 autorelease]; //count:still 2 27 [tracker2 release];//count:1 28 29 NSLog(@"auto releasing pool"); 30 } 31 32 return(0); 33 }//main