在Android native編寫代碼時,會經常接觸到sp、wp,sp並不是smart pointer的意思,而是strong point;wp就是weak pointer。這兩個概念比較像JAVA中的強弱引用,使用sp和wp可以讓編程人員不需要再關係記憶體的釋放問題,防止記憶體泄露。下面先來看它們的類別關係圖:
要實現記憶體的自動釋放,sp、wp必須結合RefBase這個類來使用,在Android中,大多數類的最上層基類都是RefBase類。我們舉個簡單的例子,然後順著這個例子來分析RefBase、sp和wp四種不同的應用,並介紹其實現。<喎?http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+PC9wPgo8cHJlIGNsYXNzPQ=="brush:java;">class A : public RefBase{}
上面定義一個類A,繼承與RefBase,下面我們首先來看RefBases的建構函式:
RefBase::RefBase() : mRefs(new weakref_impl(this)){} weakref_impl(RefBase* base) : mStrong(INITIAL_STRONG_VALUE) , mWeak(0) , mBase(base) , mFlags(0) { }
在RefBase中,首先構造weakref_impl對象,在weakref_impl對mStong和mWeak進行強弱引用計數賦初始值,INITIAL_STRONG_VALUE是0X10000000,這裡不直接賦初始值為0,是方便我們區分,0到底是初始化的值,還是在sp釋放後再變為0,方便做不同的處理。
列舉第一種應用:只有sp指標,沒有wp指標的應用
{
sp spA(new A);
}
首先來看sp的建構函式:
templatesp::sp(T* other) : m_ptr(other) { if (other) other->incStrong(this);}
首先將實際的A對象的指標賦給m_ptr,然後調用A對象的incStrong方法,由上面的類圖關係,我們知道這裡會調用RefBase的incStrong方法:
void RefBase::incStrong(const void* id) const{ weakref_impl* const refs = mRefs; refs->incWeak(id); refs->addStrongRef(id); const int32_t c = android_atomic_inc(&refs->mStrong); ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs); if (c != INITIAL_STRONG_VALUE) { return; } android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); refs->mBase->onFirstRef();}
這裡首先調用weakref_impl的incWeak方法來增加弱指標引用數;addStrongRef用於debug版本,正式版中沒有實現;android_atomic_inc是一個原子操作,增加refs->mStrong的強指標引用數,並返回原值;如果是第一次引用改對象,則還會調用A對象的onFirstRef方法。首先來看incWeak的實現:
void RefBase::weakref_type::incWeak(const void* id){ weakref_impl* const impl = static_cast(this); impl->addWeakRef(id); const int32_t c = android_atomic_inc(&impl->mWeak); ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);}
這裡還是調用android_atomic_inc去增加weakref_impl的mWeak計數。經過建構函式,mStong和mWeak的計數都變成了1。當spA對象退出範圍以後,就會調用其解構函式來釋放這個對象:
templatesp::~sp() { if (m_ptr) m_ptr->decStrong(this);}void RefBase::decStrong(const void* id) const{ weakref_impl* const refs = mRefs; refs->removeStrongRef(id); const int32_t c = android_atomic_dec(&refs->mStrong); ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); if (c == 1) { refs->mBase->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { delete this; } } refs->decWeak(id);}
sp對象的解構函式調用RefBase的decStrong來減少強弱引用指標計數。weakref_impl的removeStrongRef用於debug版本;調用android_atomic_dec減少mStrong計數並返回原值,如果mStrong之前為1了,這是再減少,說明已經沒有其它sp指標引用了,這時候首先調用A對象的onLastStrongRef方法,如果Flag設定的是當前對象的生命週期由sp指標決定(這也是default的設定),則釋放掉A對象;然後調用weakref_impl的decWeak去減少弱引用指標計數:
void RefBase::weakref_type::decWeak(const void* id){ weakref_impl* const impl = static_cast(this); impl->removeWeakRef(id); const int32_t c = android_atomic_dec(&impl->mWeak); ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this); if (c != 1) return; if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { if (impl->mStrong == INITIAL_STRONG_VALUE) { delete impl->mBase; } else { delete impl; } } else { impl->mBase->onLastWeakRef(id); if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) { delete impl->mBase; } }}
weakref_impl的removeWeakRef方法也是用於debug版本;然後調用android_atomic_dec去減少mWeak計數,如果mWeak之前不等於1,表示還有其它的wp引用,這裡就直接返回。如果這裡的mWeak等於1,說明已經沒有其它sp和wp的引用了,所以這裡要去釋放A對象和weakref_impl對象。
來看判斷是否釋放的邏輯,如果Flag設定當前對象的生命週期由sp指標決定,並且之前沒有初始化過任何sp對象,則直接刪除A對象;如果之前由初始化過sp對象,則刪除weakref_impl本身,A對象會在RefBase的decStrong中被釋放。如果Flag設定當前對象的生命週期由wp指標決定,則首先調用A對象的onLastWeakRef方法,然後刪除對象A。在刪除對象A的時候,都會調用RefBase的解構函式,我們再來分析RefBase的系統函數:
RefBase::~RefBase(){ if (mRefs->mStrong == INITIAL_STRONG_VALUE) { delete mRefs; } else { if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) { if (mRefs->mWeak == 0) { delete mRefs; } } } const_cast(mRefs) = NULL;}
如果沒有初始化過sp對象,則刪除mRefs對象;如果Flag設定當前對象的生命週期由wp指標決定並且mWeak計數為0,也刪除mRefs對象。
列舉第二種應用:只有wp指標,沒有sp指標的應用
{
wp wpA(new A);
}
首先來看wp的構造方法:
templatewp::wp(const sp& other) : m_ptr(other.m_ptr){ if (m_ptr) { m_refs = m_ptr->createWeak(this); }}
首先將A對象的指標賦予給m_ptr,可見在sp和wp中都儲存有A對象的實際指標,但wp中並沒有重載"->",所以wp並不能直接調用A對象的方法,並且由前面sp的知識,我們知道,在decStrong的時候,有可能A對象會被釋放,並不會care 是否存在wp的引用。然後調用A對象的createWeak方法,實際上是調用RefBase的這個方法。注意的是在wp中,m_refs是weakref_type的指標;而在RefBase中,mRefs是weakref_impl的指標,所以在下面的代碼返回時要注意類型的轉型。
RefBase::weakref_type* RefBase::createWeak(const void* id) const{ mRefs->incWeak(id); return mRefs;}void RefBase::weakref_type::incWeak(const void* id){ weakref_impl* const impl = static_cast(this); impl->addWeakRef(id); const int32_t c = android_atomic_inc(&impl->mWeak); ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);}
這裡只會增加mWeak 計數,這是mStrong等於INITIAL_STRONG_VALUE,mWeak等於1。當wpA退出範圍後,調用wp的解構函式:
templatewp::~wp(){ if (m_ptr) m_refs->decWeak(this);}
decWeak函數我們上面講過,如果Flag設定當前對象的生命週期由sp指標決定,並且之前沒有初始化過任何sp對象,則直接刪除A對象;並在RefBase的解構函式中取釋放mRefs對象。
列舉第三種應用:既有sp指標,又有wp指標的應用
{
sp spA(new A)
wp wpA(spA);
}
從上面它們的建構函式我們知道,這是mStong等於1,mWeak等於2。在spA和wpA退出範圍時,首先調用wp的解構函式,再調用sp的解構函式。在wp解構函式中,只會減少mWeak計數為1,然後就然後了。再到sp的解構函式中,就和我們前面介紹的第一種應用一樣了。
列舉第四種應用:wp指標如果調用對象的方法
前面說過在wp中並沒有重載"->",所以wp並不能直接調用A對象的方法,並且由前面sp的知識,我們知道,在decStrong的時候,有可能A對象會被釋放,所以在wp中想要調用A對象的方法,必須獲得sp指標,這是通過wp的promote方法實現的:
templatesp wp::promote() const{ sp result; if (m_ptr && m_refs->attemptIncStrong(&result)) { result.set_pointer(m_ptr); } return result;}
這裡調用weakref_type的attemptIncStrong方法去嘗試增加mStrong計數:
bool RefBase::weakref_type::attemptIncStrong(const void* id){ incWeak(id); weakref_impl* const impl = static_cast(this); int32_t curCount = impl->mStrong; while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) { if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) { break; } curCount = impl->mStrong; } if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) { if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { if (curCount <= 0) { decWeak(id); return false; } while (curCount > 0) { if (android_atomic_cmpxchg(curCount, curCount + 1, &impl->mStrong) == 0) { break; } curCount = impl->mStrong; } if (curCount <= 0) { decWeak(id); return false; } } else { if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) { decWeak(id); return false; } curCount = android_atomic_inc(&impl->mStrong); } if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) { impl->mBase->onLastStrongRef(id); } } impl->addStrongRef(id); curCount = impl->mStrong; while (curCount >= INITIAL_STRONG_VALUE) { if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE, &impl->mStrong) == 0) { break; } curCount = impl->mStrong; } return true;}
首先調用incWeak來增加mWeak計數,因為這裡需要擷取sp指標,在sp的建構函式我們知道,會同時增加mWeak和mStrong值。然後根據mStong值分兩種情況討論:
1. 當前面存在sp的引用,即curCount > 0 && curCount != INITIAL_STRONG_VALUE,這時直接讓mStrong加1。
2.當前面不存在sp的引用,需要結合Flag去判斷。又分為以下幾種情況:
一. Flag = OBJECT_LIFETIME_STRONG,並且curCount等於0。說明之前的sp對象已經釋放,由前面的知識我們知道,在釋放sp對象的同時也會釋放對象A,所以這裡調用decWeak來釋放前面增加的一次mWeak值並返回false
二.Flag = OBJECT_LIFETIME_STRONG,並且curCount = INITIAL_STRONG_VALUE,說明前面沒有sp引用,這時我們可以增加mStrong值。
三.Flag = OBJECT_LIFETIME_WEAK,並且curCount <= 0 || curCount == INITIAL_STRONG_VALUE,則調用RefBase的onIncStrongAttempted去嘗試增加mStrong值
當上面任何一種情況增加了mStrong值以後,mSrong的值可能大於INITIAL_STRONG_VALUE,我們需要去修正mStrong,就是通過減去INITIAL_STRONG_VALUE計算。當attemptIncStrong返回true時,promote方法就會調用sp的set_pointer方法去設定StrongPointer中的實際A對象的指標。接下來就可以通過sp調用相關的方法了。