轉自:http://www.linuxidc.com/Linux/2011-03/33674.htm
研究android的時候,經常會遇到sp、wp的東西,網上一搜,原來是android封裝了c++中對象回收機制。
說明:
1. 如果一個類想使用智能指針,那麼必須滿足下面兩個條件:
a. 該類是虛基類RefBase的子類或間接子類
b. 該類必須定義虛構造函數。如virtual ~MyClass();
2. 本文以類BBinder來進行說明,其餘類使用sp或wp的情況類似
3. 代碼路徑:frameworks/base/libs/utils/RefBase.cpp
frameworks/base/include/utils/RefBase.h
一、calss BBinder類說明
class RefBase
class IBinder
class BpBinder class BBinder
class BBinder : public IBinder
{
...
protected:
virtual ~BBinder();
...
}
class IBinder : public virtual RefBase
{
...
protected:
inline virtual ~IBinder() { }
...
}
由上,可以看出BBinder和IBinder都是以public的方式繼承於虛基類RefBase的。
二、sp wp對象的建立過程
解析:sp<BBinder> BB_ptr(new BBinder);
這是一條定義sp指針BB_ptr的語句,他只想的對象是一個BBinder對象。
如圖所示。
1》首先看一下new BBinder時都做了什麼,特別是和該機制相關的初始化。
c++中創建一個對象時,需要調用去構造函數,對於繼承類,則是先調用其父類的構造函數,然後纔會調用本身的
構造函數。這裏new一個BBinder對象時,順序調用了:
RefBase::RefBase() : mRefs(new weakref_impl(this)) {}
inline IBinder() {}
BBinder::BBinder() : mExtras(NULL){}
主要關注的是RefBase的構造函數,
可以看出他是通過new weakref_impl(this)的結果來初始化私有成員mRefs
這裏的this指向BBinder對象自身,class weakref_impl繼承於類RefBase的內嵌類weakref_type,然後該類
weakref_impl又被類RefBase引用。類weakref_impl的構造函數如下:
weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE) // 1 << 28
, mWeak(0)
, mBase(base) // new BBinder指針
, mFlags(0)
, mStrongRefs(NULL) // sp引用鏈表指針
, mWeakRefs(NULL) // wp引用鏈表指針
, mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) // 1
, mRetain(false) {}
2》new BBinder返回的是BBinder對象的指針,如:sp<BBinder> BB_ptr(0x????????);
sp實際上是一個類模板,這條語句最終是要建立一個sp的實例化對象,叫模板類BB_ptr
這裏生成BB_ptr對象所調用的構造函數是:
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other)
{
if (other) other->incStrong(this);
}
BB_ptr對象的私有指針指向剛剛前面生成的BBinder對象。
接着調用函數incStrong(),該函數是RefBase類的成員函數,在子類中沒有被重載,所以這裏
other->incStrong(this)的調用實際上是調用基類成員函數incStrong(this),這個this值是指向sp對象
BB_ptr的指針。現在轉去查看該成員函數的實現。
void RefBase::incStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
/* 取得BBinder對象基類中的私有隻讀指針mRefs */
refs->addWeakRef(id);
/* 調用weakref_impl類定義時實現的成員函數addWeakRef, 見下注釋1*/
refs->incWeak(id);
/* 調用weakref_impl類的基類weakref_type成員函數incWeak, 見下注釋2*/
refs->addStrongRef(id);
// 調用weakref_impl類定義時實現的成員函數addStrongRef, 見下注釋1
const int32_t c = android_atomic_inc(&refs->mStrong);
/* 該函數實際將refs->mStrong值加1,也就是增加強引用計數值。但是返回值爲refs->mStrong-1 */
LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
LOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
if (c != INITIAL_STRONG_VALUE) {
return;
}
/* c = INITIAL_STRONG_VALUE, 第一個強引用產生的時候纔會出現這個情況 */
android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
/* 返回值爲INITIAL_STRONG_VALUE,refs->mStrong值變成1 */
const_cast<RefBase*>(this)->onFirstRef();
}
/************************註釋1********************************/
void addWeakRef(const void* id)
{
addRef(&mWeakRefs, id, mWeak);
}
void addStrongRef(const void* id)
{
addRef(&mStrongRefs, id, mStrong);
}
addRef()是類weakref_impl的私有成員函數,addWeakRef()函數引用的是public成員變量,而addRef()函數可以操作私有數據。
struct ref_entry
{
ref_entry* next;
const void* id;
int32_t ref;
};
void addRef(ref_entry** refs, const void* id, int32_t mRef)
{
if (mTrackEnabled) {
AutoMutex _l(mMutex);
ref_entry* ref = new ref_entry;
ref->ref = mRef;
ref->id = id;
ref->next = *refs;
*refs = ref;
/*
新出現的ref_entry結構體加入到鏈表頭上,如果有n個sp指針指向同一個目標對象
那麼這裏就有n個ref_entry結構體加入到這個單鏈表中,該結構體記錄着如下數據
1. id域記錄着對應的sp強指針類對象的this值
2. ref域記錄的是當前sp強指針類對象是第幾個引用目標對象的指針
3. next域指向下一個指向目標對象的sp強指針對應的ref_entry結構體
類RefBase的嵌套類weakref_type的子類的私有數據mRefs的私有二級指針成員mWeakRefs指向的是
最後一個sp強指針對應的ref_entry結構體指針。
總結一下:
一個目標對象,可能被n個sp強指針指向,那麼就存在n個class sp對象,同時每一個sp
對象在目標對象的虛基類對象的成員類mRefs的私有二級指針成員mWeakRefs登記了一個
ref_entry結構體,這些ref_entry結構體的地址都是由該鏈表管理,每一個
ref_entry結構體和哪一個sp對象對應,也由該鏈表管理。同時鏈接數就是該鏈表節點的
個數
*/
}
}
/************************註釋1********************************/
/************************註釋2********************************/
void RefBase::weakref_type::incWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
// 強制類型轉換,將基類指針轉換成子類指針
impl->addWeakRef(id);
// 調用類weakref_impl成員函數addWeakRef(),產生一個ref_entry結構體掛載mWeakRefs鏈表上
const int32_t c = android_atomic_inc(&impl->mWeak);
/* impl->mWeak加1,表示已存在一個weak引用。但返回值c爲操作前的結果 */
LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
/************************註釋2********************************/
3》上面是定義一個sp指針,下面看看定義一個wp指針式如何實現的。
wp<BBinder> BB_wp_ptr(BB_ptr);
下面是wp類對應上面定義類型的構造函數
template<typename T>
wp<T>::wp(const sp<T>& other)
: m_ptr(other.m_ptr)
{
if (m_ptr) {
m_refs = m_ptr->createWeak(this);
}
}
this指針是指向wp對象的。createWeak()函數是RefBase類的成員函數。
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
mRefs->incWeak(id);
return mRefs;
}
mRefs指向的是第二步驟中產生的weakref_impl對象,調用基類weakref_type的成員函數incWeak()
void RefBase::weakref_type::incWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->addWeakRef(id);
const int32_t c = android_atomic_inc(&impl->mWeak);
/* impl->mWeak有加1,但返回值爲操作前的結果 */
LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
三、sp、wp釋放過程
sp<BBinder> BB_SP_ptr(BB_ptr);
實際上BB_SP_ptr和前面的BB_ptr一樣,指向的是同一個BBinder對象。另外需要注意的時,調用sp構造函數:
template<typename T>
sp<T>::sp(const sp<T>& other)
: m_ptr(other.m_ptr)
{
if (m_ptr) m_ptr->incStrong(this);
}
同樣是需要調用BBinder對象的incStrong()函數,使用weakref_impl對象來管理新添加進來的強引用,同時增加一個
ref_entry結構體到weakref_impl對象的mStrongRefs,增加2個ref_entry結構體到weakref_impl對象的mWeakRefs。
如上圖所示。
現在來看看釋放sp、wp指針的情況。
delete BB_SP_ptr;
將會調用如下形式的sp析構函數:
template<typename T>
sp<T>::~sp()
{
if (m_ptr) m_ptr->decStrong(this);
}
m_ptr指向的是前面生成的BBinder對象,調用其基類函數decStrong(this),this值是指向BB_SP_ptr對象。
void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->removeStrongRef(id); // 註釋3,移除mStrongRefs鏈表中和該sp對應的ref_entry結構體
const int32_t c = android_atomic_dec(&refs->mStrong);
/* 強引用計數減1, 但返回的是操作之前的引用計數值 */
LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
if (c == 1) {
/* c == 1說明剛剛removeStrongRef之前,整個系統中只存在一個sp對象引用目標對象,現在的情況就是
系統中沒有任何強指針對象來引用目標對象了,此時目標對象就會被刪除釋放
*/
const_cast<RefBase*>(this)->onLastStrongRef(id);
if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
delete this; // mFlags =0 ,條件成立,刪除目標對象,這裏就會刪除前面new出來的BBinder對象
}
}// 如果此時還有其他指向該目標對象的sp指針存在的話,就不會刪除目標對象
refs->removeWeakRef(id);
refs->decWeak(id);
/* 刪除新建目標對象sp指針時在mWeakRefs鏈表上增加的兩個ref_entry結構體 */
}
/*********************************註釋3*********************************/
void removeStrongRef(const void* id)
{
if (!mRetain) // mRetain 初始化成 flase
removeRef(&mStrongRefs, id);
/* 刪除mStrongRefs鏈表中對應id的ref_entry一項 */
/* 也就是取消了該sp對象和目標對象的聯繫 */
else
addRef(&mStrongRefs, id, -mStrong);
}
void removeRef(ref_entry** refs, const void* id)
{
if (mTrackEnabled) {
AutoMutex _l(mMutex);
ref_entry* ref = *refs;
while (ref != NULL) {
if (ref->id == id) {
*refs = ref->next;
delete ref;
return;
}
refs = &ref->next;
ref = *refs;
}
}
}
/*********************************註釋3*********************************/
delete BB_wp_ptr;
這是刪除目標對象的一個wp指針,會調用wp的析構函數:
template<typename T>
wp<T>::~wp()
{
if (m_ptr) m_refs->decWeak(this);
}
調用weakref_type類的decWeak()函數,如下:
void RefBase::weakref_type::decWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->removeWeakRef(id);// 移除weakref_impl對象mWeakRefs鏈表中對應id的ref_entry結構體
const int32_t c = android_atomic_dec(&impl->mWeak);// 引用計數減1
LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
if (c != 1) return; // c == 1, 說明這是系統中存在的指向目標對象的最後一個wp指針
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
if (impl->mStrong == INITIAL_STRONG_VALUE)
delete impl->mBase;
// delete impl; 是不是應該加上這麼一句,防止用戶新建了wp後,不用,馬上又刪除的情況呢?
/* 當目標對象的最後一個wp被析構時,如果目標對象還沒有建立任何一個sp,那麼目標對象被刪除 */
else {
delete impl;
/* 當目標對象的最後一個wp被析構時,但此時和目標對象相關的sp全部被析構,那麼impl->mStrong = 0
在最後一個sp被析構的時候,目標對象也被釋放,所以此時只需要釋放weakref_impl對象即可
*/
}
} else {
impl->mBase->onLastWeakRef(id);
if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
delete impl->mBase;
}
}
}
四、wp升級爲sp的過程
wp的定義包含了:sp<T> promote() const;
template<typename T>
sp<T> wp<T>::promote() const
{
return sp<T>(m_ptr, m_refs);
}
wp,sp互爲友元類,這裏promote就是以友元身份調用了sp<Binder>類的構造函數: sp(T* p, weakref_type* refs);
template<typename T>
sp<T>::sp(T* p, weakref_type* refs)
: m_ptr((p && refs->attemptIncStrong(this)) ? p : 0)
{
}
這裏如果升級成功,那麼將會產生一個sp對象指向目標對象,原來的wp仍然存在。
如果升級不成功,返回NULL
看看關鍵函數refs->attemptIncStrong(this)
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
incWeak(id);
weakref_impl* const impl = static_cast<weakref_impl*>(this);
int32_t curCount = impl->mStrong;
LOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow",
this);
while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
break;
}
curCount = impl->mStrong;
}// 系統中還有其他sp指向目標對象的情況
if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
bool allow;
if (curCount == INITIAL_STRONG_VALUE) {
// 發現該目標對象還沒有一個sp對象與之相關聯的話,那麼將會新建一個對目標對象的強引用
allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK
|| impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
} else {
/*
發現系統中原來指向目標對象的sp全部被釋放,最後一次sp釋放也將目標對象釋放了
*/
allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK
&& impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
}
if (!allow) {
decWeak(id); // 目標對象已經不存在了,釋放前面incWeak(id)產生的ref_entry結構體
return false;
}
curCount = android_atomic_inc(&impl->mStrong);
if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {
impl->mBase->onLastStrongRef(id);
}
}
// 走完生成一個sp的必要過程,和前面介紹的是一樣
impl->addWeakRef(id);
impl->addStrongRef(id);
if (curCount == INITIAL_STRONG_VALUE) {
android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);
impl->mBase->onFirstRef();
}
return true; // 返回true
}
五、總結:
1. weakref_impl對象會隨着目標對象的生成而產生,但不一定會隨着目標對象的釋放而釋放。例如:如果目標對象被
1個sp引用,但是同時被2個wp引用,那麼在sp被刪除的時候,刪除了目標對象,但沒有刪除weakref_impl對象,
只有在最後一個wp釋放時,weakref_impl對象會被釋放。
2. 一個目標對象被多個sp指針引用,沒有wp引用的情況下。釋放這些sp的時候,delete會調用sp析構函數,
然後調用RefBase類的成員函數decStrong(), 最後一個sp被釋放時,weakref_impl對象數據成員mStrong會
從1減到0(注意mStrong的初始化值爲1<<28, 從這個值可以判斷出該目標對象有沒有被sp指針引用過),
同時釋放目標對象。
3. 一個目標對象被多個wp指針引用,沒有sp引用的情況下。delete這些wp的時候,會調用wp的析構函數,該函數會
調用函數decWeak()。當刪除最後一個wp的時候,代碼中只是刪除了目標對象,而沒有釋放weakref_impl對象,
暫時沒發現在哪裏釋放了它。
4. 一個目標對象既有sp,又有wp來引用。如果sp先被刪除光,那麼最後一個sp刪除的時候會釋放掉目標對象,那麼此時
mStrong = 0。在後續最後一個wp的釋放過程中,在decWeak()函數中就會判斷出impl->mStrong !=
INITIAL_STRONG_VALUE,而釋放掉剩下的weakref_impl對象了。如果先所以的wp刪除光,此時mWeak還等於剩餘的sp
的個數,所以此時的釋放情況,同第2小點的說明。
5. 從wp定義來看,wp是不能直接操作對象的,必須先升級爲sp才行。這個升級的過程是依靠函數promote()來完成的。
升級成功,返回新生成的sp對象指針,升級失敗,返回NULL。需要注意的是,如果目標對象之前有過sp指向,但後來
將所有的sp釋放完之後,此時目標對象是不存在的,那麼此時用戶還想將指向該目標對象的wp升級爲sp的話,
此時就返回NULL。那麼這個時候我們應該delete這些剩下的wp。