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 语言实现

作为一名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)。

 (转载请标明出处:点我

原文地址:https://www.cnblogs.com/csusheep/p/3193639.html