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

要谈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,以取消对该对象的引用,并使垃圾回收器知道该对象可以被清理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值