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)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章