IOS面经系列(一)浅析OC之内存管理

来源:互联网 发布:java怎么打成jar 编辑:程序博客网 时间:2024/04/30 20:17

要谈oc的内存管理,首先得从对象的生命周期说起。


1.对象的生命周期

1.1引用计数

oc利用引用计数来计算对象的使用寿命。每个对象都有一个与之相联的整数,称作它的引用计数器/保留计数器

当某段代码需要访问一个对象时,该代码将该对象的保留计数器的值加1,表示为“我要访问该对象”。当这段代码结束对象访问时,将对象的保留计数器值减1,表示它不再访问该对象。当保留计数器值为0,表示不再有代码访问该对象了。因此对象将被销毁,释放内存被系统回收以便重用。

当使用alloc,new方法或者通过copy消息(生成对象的一个副本)创建一个对象时,对象的保留计数器值被置为1。要增加对象的保留计数器值,可以给对象发送retain消息。要减少则发release消息。

当一个对象因其保留计数器归0而即将被销毁时,oc自动向对象发送dealloc消息。你可以重写对象的dealloc方法,释放已经分配的相关资源。发送retainCount来查询引用计数。

下面举个栗子来更深入了解:

@interface RetainTracker : NSObject@end@implementation RetainTracker- (id) init{   if(self = [super init]){      NSLog(@"init:retainCount :%d",[self retainCount]);   }   return self;}- (void)dealloc{   NSLog(@"dealloc:bye");   [super dealloc];}@end

int main (int argc, const char * argv[])    {      RetainTracker *tracker = [RetainTracker new];  //init:retainCount:1     [tracker retain];                             //count:2     NSLog(@"%d",[tracker retainCount]);       [tracker retain];                            //count:3                    NSLog(@"%d",[tracker retainCount]);      [tracker release];                            //count:2     NSLog(@"%d",[tracker retainCount]);      [tracker release];                            //count:1     NSLog(@"%d",[tracker retainCount]);      [tracker retain];                             //count:2     NSLog(@"%d",[tracker retainCount]);     [tracker release];                             //count:1    NSLog(@"%d",[tracker retainCount]);       [tracker release];                           //dealloc:bye   return 0;} 

1.2对象所有权

也许你觉得内存管理so easy。 但是我告诉你,当你开始思考对象所有权时,内存管理就变得复杂多了。

当我们说某个实体“拥有一个对象”时,就意味着该实体要负责确保对其拥有的对象进行清理。

如果一个对象具有指向其他对象的实例变量,则称该对象拥有这些对象。

举个栗子:实例对象拥有他的成员变量。同样,如果一个函数创建了一个对象,则该函数拥有它创建的这个对象。

当多个实体拥有某个特定对象时,对象的所有权关系就复杂了,这也是保留计数器值可能大于1的原因。在上面的代码例子里,main()函数拥有RetainTracker的对象,因此main()要负责清理该类的对象。

假如有Car类,其engine setter方法为:

-(void)setEngine:(Engine *)newEngine;
在main()里调用该方法:

Engine *engine = [Engine new];[car setEngine : engine];

现在是哪个实体拥有engine对象呢?是main()函数 还是car类?哪个实体负责确保当engine对象不再被使用时能够收到release消息?因为car类正在使用engine对象,所以不可能是main函数,因为main函数随后可能还会使用engine对象,所以也不可能是car类。

解决办法是让car类保留engine对象,将engine对象的保留计数器增加到2,car类应该在setEngine方法中保留engine对象,而main函数运行结束时释放engine对象,当car类完成其任务时再释放engine对象(在其dealloc方法中),最后engine对象占用的资源被回收。

- (void) setEngine: (Enigne *) newEngine{     if(engine != newEngine)     {        [engine release];        engine = [newEngine retain];     }}//setEngine

最完美的解决方法是在访问方法中 先判断新旧对象是否相等,如果不是, 先释放旧对象,再保持新对象。

2.自动释放

自动释放池(autorelease pool),它是一个存放实体的池(集合),这些实体可能是对象,能够被自动释放。NSObject类提供了一个autorelease方法。

该方法预先设定了一条在将来某个时间发送的release消息,其返回值是接收消息的对象。当给一个对象发送autorelease消息时,实际上是将该对象添加到NSAutoreleasePool中。当自动释放池被销毁时,会向该池中的所有对象发送release消息。

- (NSString *) description   {      NSString *description;       description = [[NSString alloc]          initWithFormat: @"I am %d years old", 4];      return ([description autorelease]);7} // description 

自动释放池的销毁时间:

在使用Foundation库工具中,创建和销毁自动释放池的方法非常明确:

NSAutoreleasePool *pool;pool = [[NSAutoreleasePool alloc] init];…[pool release];  

创建一个自动释放池时,该池自动成为活动的池。

释放该池时,其保留计数器值归0,然后该池被销毁。

在销毁过程中,该池释放其包含的所有对象。

当使用AppKit时,Cocoa定期自动为你创建和销毁自动释放池。通常是在程序处理完当前事件(如鼠标单击或按键)以后执行这些操作。

你可以使用任意多的自动释放对象,当不再使用它们时,自动释放池将自动为你清理这些对象。

@interface RetainTracker : NSObject   @end   @implementation RetainTracker   - (id) init  {   if (self = [super init])   {    NSLog(@"init: Retain count of %d.",[self retainCount]);   }   return self;  }   - (void) dealloc  {   NSLog(@"dealloc called.Bye Bye.");   [super dealloc];  }   @end   int main (int argc, const char * argv[])   {   // 创建一个自动释放池   NSAutoreleasePool *pool;   pool = [[NSAutoreleasePool alloc] init];      // 创建(new)一个RetainTracket对象   RetainTracker *tracker = [RetainTracker new]; // count:1   NSLog(@"%d",[tracker retainCount]);      // 处于演示目的,retain一个trancker   [tracker retain]; // count:2   NSLog(@"%d",[tracker retainCount]);      // 向对象发送autorelease消息,将该对象添加到自动释放池pool中   [tracker autorelease]; // count: still 2   NSLog(@"%d",[tracker retainCount]);      // release该对象以抵消上面的 retain   [tracker release]; //count: 1   NSLog(@"%d",[tracker retainCount]);      NSLog(@"releaseing pool");   [pool release];      return 0;  } 


3.内存管理规则

1.当你使用new,alloc,copy创建一个对象时,该对象的保留计数器值为1.当不再使用该对象,你要负责向该对象发送一条release或autoRelease消息。这样,该对象将在其寿命结束时销毁。

2.当你通过任何方式获得一个对象时,则假设该对象的保留计数器值为1,而且已经被设置为自动释放,你不再需要执行任何操作来确保该对象被清理。如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它。

3.如果你保留了某个对象,你需要(最终)释放或自动释放该对象。必须保持retain方法和release方法的使用次数相等。


4.垃圾回收(ARC)

oc2.0引入了自动内存管理机制,也称垃圾回收。

垃圾回收器会定期检查变量和对象以及他们之间的指针,当发现没有任何变量指向某个对象时,会将该对象视为垃圾。

最糟糕的事情莫过于保留一个指向不再使用的对象的指针。因此,如果你在一个实例变量中指向某个对象,一定要在某个时候将该实例变量置为nil,以取消对该对象的引用,并使垃圾回收器知道该对象可以被清理。

0 0
原创粉丝点击