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