Android sp,wp,RefBase淺析

對於native層new出來的c++對象的生命週期的管理,Android通過對這個對象引用計數的方式實現對象生命週期的管理(自動析構和釋放內存)。爲此Android提供了兩個引用計數幫助類:sp,wp。sp,wp都是模板類,模板參數就是繼承了RefBase的子類。sp代表強引用,wp代表弱引用。根據生命週期的管理策略(比如對象的週期受強引用計數管理,或者受強引用和弱引用共同管理)的不同,這兩種引用對對象生命週期的影響有所不同。


RefBase的類圖如下:

RefBase中保存着一個指向weakref_impl類的指針。通過weakref_impl的成員變量保存着RefBase類的引用計數。mStrong表示強引用計數的個數,mWeak表示弱引用計數的個數。假設有如下的代碼:

class A:public RefBase {
public:
    A() {
        cout<<"constructor A"<<endl;
    }
    
    ~A() {
        cout<<"destructor A"<<endl;
    }
};

{
sp<A> pA1 = new A();
sp<A> pA2 = pA1; 
}
以上代碼通過new的方式產生出來的A的對象的強引用計數和弱引用計數爲2,即weakref_imp中mStrong和mWeak的值爲2. 當new A()完成的時候,A的基類RefBase還new了一個weakref_iml對象,用來記錄A的強弱引用計數。當把new A()所產生的A*指針作爲參數構造一個sp<A>對象的時候,在sp<A>的copy constructor中會調用A的incStrong()方法,這個方法中先增加A的弱引用計數一次,然後增加強引用計數一次,強弱引用計數此時爲1.當把pA1作爲參數調用sp<A>  的賦值函數產生出pA2的時候,在賦值函數中再次調用A的基類RefBase的incStrong函數,這樣強弱引用計數就變爲了2.

過了pA1的作用域之後,pA1的析構函數被調用,在析構函數中會調用A的decStrong函數減少一次強弱引用計數。同理,過了pA2的作用域之後,也會減少一次強弱引用計數,這時,強弱引用計數就變爲了0.在RefBase類的decStrong函數中會做判斷,當引用計數爲1的時候,表明本次調用後,強引用計數會爲0,如果對象的生命週期只受強引用計數管理,這個時候就會調用delete來釋放自己的內存。


從上面的分析來看,如果對象的生命週期只受強引用計數管理(默認如此),當對象的強引用計數爲0的時候就會delete掉這個對象,而不管弱引用計數的值,當弱引用計數爲0的時候就會delete掉weakref_imp類。那麼弱引用計數究竟有什麼作用呢,當調用了RefBase的extendObjectLifetime方法,參數爲OBJECT_LIFETIME_WEAK,這將改變對象的生命週期的管理策略爲強弱引用計數同時決定何時delete對象。


一般而言,程序代碼中最好只有一個地方是採取sp的方式保存着new出來對象的地址,其他的用處用wp。這樣對象的生命週期只受一處管理,其他地方通過wp來使用A的時候,先通過promote()函數看所保存的對象是否已經被釋放。如果沒有被釋放,就可以返回一個sp類供調用者使用。


在實際使用中要注意的是:時刻要清楚自己new出來的對象的引用計數的次數。清楚自己對sp,wp構造,賦值等操作對引用計數的影響。比如有以下代碼:

{
sp<A> pA1 = new A();
pA1->decStrong();
}
那麼在花括號的作用域之後,程序會崩潰。原因如下:

第一句,強引用和弱引用計數爲1.

第二句,強引用計數爲爲0,弱引用計數爲0,A的對象被delete;但是sp<A>中還是保存着A的指針;

過了作用域之後,pA1的析構函數被調用,這個時候會再次通過pA1中保存的A的指針調用decStrong函數。但是此時,A的對象已經被delete。

所以程序崩潰。

下面是sp,wp與RefBase的類圖關係:



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