ANSI-C中使用引用计数(译)

ANSI-C中使用引用计数(译)

原作者:Jean-David Gadina

原文地址:Reference counting in ANSI-C

关于

内存管理在编写c程序时是一件困难的事情,

某些高等级的编程语言提供了不同的内存管理的方法。

主要有垃圾回收garbage collection) 和 引用计数reference counting)。

本文将教给你如何在c语言中实现引用计数的内存管理系统。

从个人角度讲,本人(原文章作者)作为一名使用c语言和Objective-c语言的程序员,更加偏爱引用计数的方法。

因为这种方法隐含着对象所有权的概念。(It implies the notion of ownership on objects.)

Objective-C 的例子

在Objective-C中,当你通过 alloc 或者 copy 方法创建一个对象时,你拥有这个对象(you own the object)。这也就意味着将由你来负责释放(release)你的对象,保证该块内存被回收利用。 

另外,对象也可以被保留(retained)。这种情况下他们也必须被释放(release)。 

通过简便的方法获得对象,调用者并不拥有该对象,所以也没有必要去释放他们,释放的工作自然会由其他的类或者方法来做。 

例如:

1 NSArray * object1 = [ NSArray array ];
2 NSArray * object2 = [ [ NSArray alloc ] init ];
3 NSArray * object3 = [ [ [ NSArrayarray ] retain ] retain ];

在这里,变量 object2 需要被release,因为我们明确的通过 alloc 创建了它。

变量object3 则需要被release 两次,因为我们retain 了它两次。

如下:

1 [ object2 release ];
2 [ [ object3 release ] release ];

语言实现

作为一名c语言编程者,原文作者将要使用ANSI-C来实现objective-c 中的引用计数。

具体实施如下:

首先,我们需要为我们的内存纪录定义一个结构体,这个结构体看起来像是下面这个样子:

1 typedef struct
2 {
3 unsigned int retainCount
4 void       * data;
5 }
6 MemoryObject;

我们将 retain 计数 使用一个 无符号整形变量 rerainCount 来储存,遇到retain则增加改变量的值,遇到release 则减少改变量的值,当改变量的值为0的时候,就释放掉该对象的内存。 

同时我们也需要自定义一个 分配函数(allocation function):

1 void * Alloc( size_t size )
2 {
3     MemoryObject * o;
4     o = ( MemoryObject * )calloc( sizeof( MemoryObject ) + size, 1 );

我们最终是要返回内存对象的指针,所以我们需要进行一些计算: 

第一、声明一个 指向char类型的指针,用来指向我们生成的内存对象结构体:

1 char * ptr = ( char * )o; 

第二、通过给该指针增加内存对象结构体的大小来获取 用户自定义数据的内存地址:

2 ptr += sizeof( MemoryObject );

第三、将用户自定义数据内存地址传给 内存对象结构体中的 data指针,然后将引用计数变量的值设置为1:

3 o->data        = ptr;
4 o->retainCount = 1;

最后,返回ptr指针,这样使用者就不需要知道我们内存对象结构体的内部结构了。

5 return ptr;

下面是完整的函数:

复制代码
 1 void * Alloc( size_t size )
 2 {
 3     MemoryObject * o;
 4     char         * ptr;
 5     o              = ( MemoryObject * )calloc( sizeof( MemoryObject ) + size, 1 );
 6     ptr            = ( char * )o;
 7     ptr           += sizeof( MemoryObject );
 8     o->retainCount = 1;
 9     o->data        = ptr;
10     return ( void * )ptr;
11 }
复制代码

这样,我们成功的返回了用户指定的内存大小,隐藏了定义在用户数据前面的结构体。

要找回我们的数据(译者:应该是自定的结构体),只需要很简单的减去 MemoryObject 结构体 的大小即可:

拿Retain 函数举例:

复制代码
1 void Retain( void * ptr )
2 {
3     MemoryObject * o;
4     char         * cptr;
5     cptr  = ( char * )ptr;
6     cptr -= sizeof( MemoryObject );
7     o     = ( MemoryObject * )cptr;   
8     o->retainCount++:
9
复制代码

在这里通过用户指针减去 MemoryObject 结构体的大小获得了,指向咱们之前自定义的结构体的地址,然后访问其中的 retainCount 变量,来增加引用计数。

Release 方法也是一样:

复制代码
 1 void Release( void * ptr )
 2 {
 3     MemoryObject * o;
 4     char         * cptr;
 5     cptr  = ( char * )ptr;
 6     cptr -= sizeof( MemoryObject );
 7     o     = ( MemoryObject * )cptr;
 8     o->retainCount--:
 9     if( o->retainCount == 0 )
10     {
11         free( o );
12     }
13 }
复制代码

当引用计数为0时,释放掉对象。

到此为止,我们已经拥有了一个 基于c语言的引用计数内存管理机制。

所要做的就是 使用 Alloc 创建对象,在需要的时候 Retain,在不需要的时候 Release.

某些情况下 对象已经被其他函数引用(retain),但这时你已经不必在意该对象是否会被正确释放,因为你已经不再拥有它了(It may have been retained by another function, but then you don't have to care if it will be freed or not, as you don't own the object anymore)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值