Objective-C的MRC手动内存管理——引用计数详解

0x01 对象生命周期

对象的生命周期包括诞生(通过alloc或new方法实现)、生存(接收消息并执行操作)、交友(通过复合以及向方法传递参数)以及最终死去(被释放)。

生命周期结束后,它们所占用的内存将被回收并被新的对象使用。

 

0x02 基本的内存分配规则

Objective-C的对象都是在堆里面生成,然后有一个指针指向它,使用完后不会自动销毁,需要执行dealloc来销毁,否则就会出现内存泄漏:

ClassA *obj1 = [[ClassA alloc] init];   //alloc
[obj1 dealloc];                         //dealloc

 

0x03 引用计数

Cocoa采用了引用计数(Reference Counting)或称为保留计数(Retain Counting)的技术来表示对象的生命。

每个对象都有一个与之相关联的整数作为引用计数器。

当某段代码需要访问一个对象时,该代码就将该对象的引用计数器值+1,表示“我需要访问该对象”;

当某段代码结束对象访问时,该代码就将该对象的引用计数器值-1,表示“不再访问该对象”;

当对象的引用计数器值为0时,表示“不再有代码访问该对象了”,因此该对象将被销毁,其占用的内存资源将被释放。

 

获得引用计数器的值

可以发送retainCount消息来获得引用计数器的值:

- (NSUInteger) retainCount;

当使用alloc、new方法或者通过copy消息(接收到消息的对象会创建一个自身的副本)创建一个对象时,对象的引用计数器被置为1。

要增加对象引用计数器的值,可以给对象发送一条retain消息,retain消息返回的是一个id类型的值:

- (id) retain;

要减少对象的引用计数器值,可以给对象发送一条release消息:

- (oneway void) release;

执行release方法释放的是一块内存区域的内容,而不是仅仅切断对象和该内存区域的联系!

 

示例程序:

@interface RetainTracker : NSObject
@end // RetainTracker

@implementation RetainTracker
- (id) init
{
  if (self = [super init]) {
       [self retainCount]);
}
NSLog (@"init: Retain count of %d.",  
        return (self);
} // init

- (void) dealloc
{
  NSLog (@"dealloc called. Bye Bye.");
  [super dealloc];
} // dealloc
@end // RetainTracker

int main (int argc, const char *argv[])
{
RetainTracker *tracker = [RetainTracker new];  // count: 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];                             // count: 0, dealloc it
return (0);
} // main

当一个对象因其保留计数器归0而即将被销毁时,Objective-C会自动向对象发送一条dealloc消息。

我们可以自己在对象中重写dealloc方法,这样就能释放掉已经分配的全部相关资源。

但一定不要直接调用dealloc方法,因为Objective-C会在需要销毁对象的时候自己调用它!

 

0x04 对象所有权

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

一般来说,我们说某个实体“拥有一个对象”时,其实也意味着该实体要负责清理其拥有的对象所占用的资源。

当多个实体拥有某个特定对象时,对象的所有权将变得很复杂,这也是引用计数器的值大于1的原因。

所有权变得复杂起来,就可能出现下列情况:

ClassA *obj1 = [[ClassA alloc] init];      // count = 1
[obj1 release];                            // count = 0, 自动dealloc
ClassA *obj1 = [[ClassA alloc] init];      // count = 1
ClassA *obj2 = obj1;                       // count = 1, 指针赋值不会增加引用计数
[obj1 hello];
[obj1 release];                            // count = 0, 自动dealloc
// 因对象已被dealloc, 故以下代码不会执行, obj2仍然是无效指针:
[obj2 hello];
[obj2 release];

正确的做法是,凡是访问方法,都先保留新对象,再释放对象:

ClassA *obj1 = [[ClassA alloc] init];     // count = 1
ClassA *obj2 = obj1;                      // count = 1
[obj2 retain];                            // count = 2,先为新对象做保留
[obj1 hello];
[obj1 release];                           // count = 1
[obj2 hello];
[obj2 release];                           // count = 0, 自动dealloc

按照这个规则,我们可以编写setEngine方法的内存管理版本:

- (void) setEngine: (Engine *) newEngine
{
 [newEngine retain];                  //保留新对象
 [engine release];                    //释放对象
 engine = newEngine;
} // setEngine

 

 

你可能感兴趣的:(Objective-C,Objective-C基础教程)