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)。
(转载请标明出处:点我)