轉自:http://blog.csdn.net/innost/article/details/6752443
5.1 概述
初次接觸Android源碼時,見到最多的一定是sp和wp。即使你隻是沉迷于Java世界的編碼,那麼Looper和Handler也是避不開的。本章的目的,就是把經常碰到的這些内容中的“攔路虎”一網打盡,将它們徹底搞懂。至于弄明白它們有什麼好處,就仁者見仁,智者見智了。個人覺得Looper和Handler相對會更實用一些。
5.2 以“三闆斧”揭秘RefBase、sp和wp
RefBase是Android中所有對象的始祖,類似于MFC中的CObject及Java中的Object對象。在Android中,RefBase結合sp和wp,實作了一套通過引用計數的方法來控制對象生命周期的機制。就如我們想像的那樣,這三者的關系非常暧昧。初次接觸Android源碼的人往往會被那個随處可見的sp和wp搞暈了頭。
什麼是sp和wp呢?其實,sp并不是我開始所想的smart pointer(C++語言中有這個東西),它真實的意思應該是strong pointer,而wp則是weak pointer的意思。我認為,Android推出這一套機制可能是模仿Java,因為Java世界中有所謂weak reference之類的東西。sp和wp的目的,就是為了幫助健忘的程式員回收new出來的記憶體。
說明 我還是喜歡赤裸裸地管理記憶體的配置設定和釋放。不過,目前sp和wp的使用已經深入到Android系統的各個角落,想把它去掉真是不太可能了。
這三者的關系比較複雜,都說程咬金的“三闆斧”很厲害,那麼我們就借用這三闆斧,揭密其間的暧昧關系。
5.2.1 第一闆斧—初識影子對象
我們的“三闆斧”,其實就是三個例子。相信這三闆斧劈下去,你會很容易了解它們。
[-->例子1]
//類A從RefBase派生,RefBase是萬物的始祖。
class A:public RefBase
{
//A沒有任何自己的功能。
}
int main()
{
A* pA = new A;
{
//注意我們的sp、wp對象是在{}中建立的,下面的代碼先建立sp,然後建立wp。
sp<A> spA(pA);
wp<A> wpA(spA);
//大括号結束前,先析構wp,再析構sp。
}
}
例子夠簡單吧?但也需一步一步分析這斧子是怎麼劈下去的。
1. RefBase和它的影子
類A從RefBase中派生。使用的是RefBase構造函數。代碼如下所示:
[-->RefBase.cpp]
RefBase::RefBase()
: mRefs(new weakref_impl(this))//注意這句話
{
//mRefs是RefBase的成員變量,類型是weakref_impl,我們暫且叫它影子對象。
//是以A有一個影子對象。
}
mRefs是引用計數管理的關鍵類,需要進一步觀察。它是從RefBase的内部類weakref_type中派生出來的。
先看看它的聲明:
class RefBase::weakref_impl : public RefBase::weakref_type
//從RefBase的内部類weakref_type派生。
由于Android頻繁使用C++内部類的方法,是以初次閱讀Android代碼時可能會有點不太習慣,C++的内部類和Java的内部類相似,但有一點不同,即它需要一個顯式的成員指向外部類對象,而Java的内部類對象有一個隐式的成員指向外部類對象的。
說明 内部類在C++中的學名叫nested class(内嵌類)。
[-->RefBase.cpp::weakref_imple構造]
weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE) //強引用計數,初始值為0x1000000。
, mWeak(0) //弱引用計數,初始值為0。
, mBase(base)//該影子對象所指向的實際對象。
, mFlags(0)
, mStrongRefs(NULL)
, mWeakRefs(NULL)
, mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
, mRetain(false)
{
}
如你所見,new了一個A對象後,其實還new了一個weakref_impl對象,這裡稱它為影子對象,另外我們稱A為實際對象。
這裡有一個問題:影子對象有什麼用?
可以仔細想一下,是不是發現影子對象成員中有兩個引用計數?一個強引用,一個弱引用。如果知道引用計數和對象生死有些許關聯的話,就容易想到影子對象的作用了。
說明 按上面的分析來看,在構造一個實際對象的同時,還會悄悄地構造一個影子對象,在嵌入式裝置的記憶體不是很緊俏的今天,這個影子對象的記憶體占用已經不成問題了。
2.sp上場
程式繼續運作,現在到了:
sp<A> spA(pA);
請看sp的構造函數,它的代碼如下所示(注意,sp是一個模闆類,對此不熟悉的讀者可以去翻翻書,或者幹脆把所有出現的T都換成A):
[-->RefBase.h::sp(T* other)]
template<typename T>
sp<T>::sp(T* other) //這裡的other就是剛才建立的pA。
: m_ptr(other)// sp儲存了pA的指針。
{
if (other) other->incStrong(this);//調用pA的incStrong。
}
OK,戰場轉到RefBase的incStrong中。它的代碼如下所示:
[-->RefBase.cpp]
void RefBase::incStrong(const void* id) const
{
//mRefs就是剛才在RefBase構造函數中new出來的影子對象。
weakref_impl* const refs = mRefs;
//操作影子對象,先增加弱引用計數。
refs->addWeakRef(id);
refs->incWeak(id);
......
先來看看影子對象的這兩個weak函數都幹了些什麼。
(1)眼見而心不煩
下面看看第一個函數addWeakRef,代碼如下所示:
[-->RefBase.cpp]
void addWeakRef(const void* ) { }
呵呵,addWeakRef啥都沒做,因為這是release版走的分支。調試版的代碼我們就不讨論了,它是給創造RefBase、 sp,以及wp的人調試用的。
說明 調試版分支的代碼很多,看來創造它們的人也在為不了解它們之間的暧昧關系痛苦不已。
總之,一共有這麼幾個不用考慮的函數,下面都已列出來了。以後再碰見它們,幹脆就直接跳過去:
void addStrongRef(const void* ) { }
void removeStrongRef(const void* ) { }
void addWeakRef(const void* ) { }
void removeWeakRef(const void* ) { }
void printRefs() const { }
void trackMe(bool, bool) { }
繼續我們的征程。再看incWeak函數,代碼如下所示:
[-->RefBase.cpp]
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);
//原子操作,影子對象的弱引用計數加1。
//千萬記住影子對象的強弱引用計數的值,這是徹底了解sp和wp的關鍵。
}
好,我們再回到incStrong,繼續看代碼:
[-->RefBase.cpp]
......
//剛才增加了弱引用計數。
//再增加強引用計數。
refs->addStrongRef(id); //非調試版這裡什麼都不幹。
//下面函數為原子加1操作,并傳回舊值。是以c=0x1000000,而mStrong變為0x1000001。
const int32_t c = android_atomic_inc(&refs->mStrong);
if (c != INITIAL_STRONG_VALUE) {
//如果c不是初始值,則表明這個對象已經被強引用過一次了。
return;
}
//下面這個是原子加操作,相當于執行refs->mStrong +(-0x1000000),最終mStrong=1。
android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
const_cast<RefBase*>(this)->onFirstRef();
}
說明 android_atomic_xxx是Android平台提供的原子操作函數,原子操作函數是多線程程式設計中的常見函數,讀者可以學習原子操作函數的相關知識,本章後面也會對其進行介紹。
(2)sp構造的影響
sp構造完後,它給這個世界帶來了什麼?
那就是在RefBase中影子對象的強引用計數變為1,且弱引用計數也變為1。
更準确的說法是,sp的出生導緻影子對象的強引用計數加1,且弱引用計數也加1。
(3)wp構造的影響
繼續看wp,例子中的調用方式如下:
wp<A> wpA(spA)
wp有好幾個構造函數,原理都一樣。來看這個最常見的:
[-->RefBase.h::wp(const sp<T>& other)]
template<typename T>
wp<T>::wp(const sp<T>& other)
: m_ptr(other.m_ptr) //wp的成員變量m_ptr指向實際對象。
{
if (m_ptr) {
//調用pA的createWeak,并且儲存傳回值到成員變量m_refs中。
m_refs = m_ptr->createWeak(this);
}
}
[-->RefBase.cpp]
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
//調用影子對象的incWeak,這個我們剛才講過了,它會導緻影子對象的弱引用計數增加1。
mRefs->incWeak(id);
return mRefs; //傳回影子對象本身。
}
我們可以看到,wp化後,影子對象的弱引用計數将增加1,是以現在弱引用計數為2,而強引用計數仍為1。另外,wp中有兩個成員變量,一個儲存實際對象,另一個儲存影子對象。sp隻有一個成員變量,用來儲存實際對象,但這個實際對象内部已包含了對應的影子對象。
OK,wp建立完了,現在開始進行wp的析構。
(4)wp析構的影響
wp進入析構函數,則表明它快要離世了,代碼如下所示:
[-->RefBase.h]
template<typename T>
wp<T>::~wp()
{
if (m_ptr) m_refs->decWeak(this); //調用影子對象的decWeak,由影子對象的基類實作。
}
[-->RefBase.cpp]
void RefBase::weakref_type::decWeak(const void* id)
{
//把基類指針轉換成子類(影子對象)的類型,這種做法有些違背面向對象程式設計的思想。
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->removeWeakRef(id);//非調試版不做任何事情。
//原子減1,傳回舊值,c=2,而弱引用計數從2變為1。
const int32_t c = android_atomic_dec(&impl->mWeak);
if (c != 1) return; //c=2,直接傳回。
//如果c為1,則弱引用計數為0,這說明沒用弱引用指向實際對象,需要考慮是否釋放記憶體。
// OBJECT_LIFETIME_XXX和生命周期有關系,我們後面再說。
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
if (impl->mStrong == INITIAL_STRONG_VALUE)
delete impl->mBase;
else {
delete impl;
}
} else {
impl->mBase->onLastWeakRef(id);
if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
delete impl->mBase;
}
}
}
在例1中,wp析構後,弱引用計數減1。但由于此時強引用計數和弱引用計數仍為1,是以沒有對象被幹掉,即沒有釋放實際對象和影子對象占據的記憶體。
(5)sp析構的影響
下面進入sp的析構。
[-->RefBase.h]
template<typename T>
sp<T>::~sp()
{
if (m_ptr) m_ptr->decStrong(this); //調用實際對象的decStrong,由RefBase實作。
}
[-->RefBase.cpp]
void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->removeStrongRef(id);//調用影子對象的removeStrongRef,啥都不幹。
//注意,此時強弱引用計數都是1,下面函數調用的結果是c=1,強引用計數為0。
const int32_t c = android_atomic_dec(&refs->mStrong);
if (c == 1) { //對于我們的例子, c為1
//調用onLastStrongRef,表明強引用計數減為0,對象有可能被delete。
const_cast<RefBase*>(this)->onLastStrongRef(id);
//mFlags為0,是以會通過delete this把自己幹掉。
//注意,此時弱引用計數仍為1。
if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
delete this;
}
......
}
先看delete this的處理,它會導緻A的析構函數被調用。再來看A的析構函數,代碼如下所示:
[-->例子1::~A()]
//A的析構直接導緻進入RefBase的析構。
RefBase::~RefBase()
{
if (mRefs->mWeak == 0) { //弱引用計數不為0,而是1。
delete mRefs;
}
}
RefBase的delete this自殺行為沒有把影子對象幹掉,但我們還在decStrong中,可從delete this接着往下看:
[-->RefBase.cpp]
.... //接前面的delete this
if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
delete this;
}
//注意,實際資料對象已經被幹掉了,是以mRefs也沒有用了,但是decStrong剛進來
//的時候就把mRefs儲存到refs了,是以這裡的refs指向影子對象。
refs->removeWeakRef(id);
refs->decWeak(id);//調用影子對象decWeak
}
[-->RefBase.cpp]
void RefBase::weakref_type::decWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->removeWeakRef(id);//非調試版不做任何事情。
//調用前影子對象的弱引用計數為1,強引用計數為0,調用結束後c=1,弱引用計數為0。
const int32_t c = android_atomic_dec(&impl->mWeak);
if (c != 1) return;
//這次弱引用計數終于變為0了,并且mFlags為0, mStrong也為0。
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
if (impl->mStrong == INITIAL_STRONG_VALUE)
delete impl->mBase;
else {
delete impl; //impl就是this,把影子對象也就是自己幹掉。
}
} else {
impl->mBase->onLastWeakRef(id);
if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
delete impl->mBase;
}
}
}
好,第一闆斧劈下去了!來看看它的結果是什麼。
3.第一闆斧的結果
第一闆斧過後,來總結一下剛才所學的知識:
RefBase中有一個隐含的影子對象,該影子對象内部有強弱引用計數。
sp化後,強弱引用計數各增加1,sp析構後,強弱引用計數各減1。
wp化後,弱引用計數增加1,wp析構後,弱引用計數減1。
完全徹底地消滅RefBase對象,包括讓實際對象和影子對象滅亡,這些都是由強弱引用計數控制的,另外還要考慮flag的取值情況。當flag為0時,可得出如下結論:
強引用為0将導緻實際對象被delete。
弱引用為0将導緻影子對象被delete。
5.2.2 第二闆斧—由弱生強
再看第二個例子,代碼如下所示:
[-->例子2]
int main()
{
A *pA = new A();
wp<A> wpA(pA);
sp<A> spA = wpA.promote();//通過promote函數,得到一個sp。
}
對A的wp化,不再做分析了。按照前面所講的知識,wp化後僅會使弱引用計數加1,是以此處wp化的結果是:
影子對象的弱引用計數為1,強引用計數仍然是初始值0x1000000。
wpA的promote函數是從一個弱對象産生一個強對象的重要函數,試看—
1. 由弱生強的方法
代碼如下所示:
[-->RefBase.h]
template<typename T>
sp<T> wp<T>::promote() const
{
return sp<T>(m_ptr, m_refs); //調用sp的構造函數。
}
[-->RefBase.h]
template<typename T>
sp<T>::sp(T* p, weakref_type* refs)
: m_ptr((p && refs->attemptIncStrong(this)) ? p : 0)//有點看不清楚。
{
//上面那行代碼夠簡潔,但是不友善閱讀,我們寫成下面這樣:
}
2.成敗在此一舉
由弱生強的關鍵函數是attemptIncStrong,它的代碼如下所示:
[-->RefBase.cpp]
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
incWeak(id); //增加弱引用計數,此時弱引用計數變為2。
weakref_impl* const impl = static_cast<weakref_impl*>(this);
int32_t curCount = impl->mStrong; //這個仍是初始值。
//下面這個循環,在多線程操作同一個對象時可能會循環多次。這裡可以不去管它,
//它的目的就是使強引用計數增加1。
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) {
bool allow;
if (curCount == INITIAL_STRONG_VALUE) {
allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK
|| impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
} else {
allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK
&& impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
}
if (!allow) {
//allow為false,表示不允許由弱生強,弱引用計數要減去1,這是因為咱們進來時加過一次。
decWeak(id);
return false; //由弱生強失敗。
}
//允許由弱生強,強引用計數要增加1,而弱引用計數已經增加過了。
curCount = android_atomic_inc(&impl->mStrong);
if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {
impl->mBase->onLastStrongRef(id);
}
}
impl->addWeakRef(id);
impl->addStrongRef(id);//兩個函數調用沒有作用。
if (curCount == INITIAL_STRONG_VALUE) {
//強引用計數變為1。
android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);
//調用onFirstRef,通知該對象第一次被強引用。
impl->mBase->onFirstRef();
}
return true; //由弱生強成功。
}
3. 第二闆斧的結果
promote完成後,相當于增加了一個強引用。根據上面所學的知識可知:
由弱生強成功後,強弱引用計數均增加1。是以現在影子對象的強引用計數為1,弱引用計數為2。
5.2.3 第三闆斧—破解生死魔咒
1. 延長生命的魔咒
RefBase為我們提供了一個這樣的函數:
extendObjectLifetime(int32_t mode)
另外還定義了一個枚舉:
enum {
OBJECT_LIFETIME_WEAK = 0x0001,
OBJECT_LIFETIME_FOREVER = 0x0003
};
注意:FOREVER的值是3,用二進制表示是B11,而WEAK的二進制是B01,也就是說FOREVER包括了WEAK的情況。
上面這兩個枚舉值,是破除強弱引用計數作用的魔咒。先觀察flags為OBJECT_LIFETIME_ WEAK的情況,見下面的例子。
[-->例子3]
class A:public RefBase
{
public A()
{
extendObjectLifetime(OBJECT_LIFETIME_WEAK);//在構造函數中調用。
}
}
int main()
{
A *pA = new A();
wp<A> wpA(pA);//弱引用計數加1。
{
sp<A> spA(pA) //sp後,結果是強引用計數為1,弱引用計數為2。
}
....
}
sp的析構将直接調用RefBase的decStrong,它的代碼如下所示:
[-->RefBase.cpp]
void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->removeStrongRef(id);
const int32_t c = android_atomic_dec(&refs->mStrong);
if (c == 1) { //上面進行原子操作後,強引用計數為0
const_cast<RefBase*>(this)->onLastStrongRef(id);
//注意這句話。如果flags不是WEAK或FOREVER的話,将delete 資料對象。
//現在我們的flags是WEAK,是以不會delete 它。
if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
delete this;
}
}
refs->removeWeakRef(id);
refs->decWeak(id);//調用前弱引用計數是2。
}
然後調用影子對象的decWeak。再來看它的處理,代碼如下所示:
[-->RefBase.cpp::weakref_type的decWeak()函數]
void RefBase::weakref_type::decWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->removeWeakRef(id);
const int32_t c = android_atomic_dec(&impl->mWeak);
if (c != 1) return; //c為2,弱引用計數為1,直接傳回。
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
if (impl->mStrong == INITIAL_STRONG_VALUE)
delete impl->mBase;
else {
delete impl;
}
} else {//flag為WEAK,滿足else分支的條件。
impl->mBase->onLastWeakRef(id);
if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
delete impl->mBase;
}
}
}
2. LIFETIME_WEAK的魔力
看完上面的例子,我們發現什麼了?
在LIFETIME_WEAK的魔法下,強引用計數為0,而弱引用計數不為0的時候,實際對象沒有被delete!隻有當強引用計數和弱引用計數同時為0時,實際對象和影子對象才會被delete。
3. 魔咒大揭秘
至于LIFETIME_FOREVER的破解,就不用再來一斧子了,我直接給出答案:
flags為0,強引用計數控制實際對象的生命周期,弱引用計數控制影子對象的生命周期。強引用計數為0後,實際對象被delete。是以對于這種情況,應記住的是,使用wp時要由弱生強,以免收到segment fault信号。
flags為LIFETIME_WEAK,強引用計數為0,弱引用計數不為0時,實際對象不會被delete。當弱引用計數減為0時,實際對象和影子對象會同時被delete。這是功德圓滿的情況。
flags為LIFETIME_FOREVER,對象将長生不老,徹底擺脫強弱引用計數的控制。是以你要在适當的時候殺死這些“老妖精”,免得她禍害“人間”。
5.2.4 輕量級的引用計數控制類LightRefBase
上面介紹的RefBase,是一個重量級的引用計數控制類。那麼,究竟有沒有一個簡單些的引用計數控制類呢?Android為我們提供了一個輕量級的LightRefBase。這個類非常簡單,我們不妨一起來看看。
[-->RefBase.h]
template <class T>
class LightRefBase
{
public:
inline LightRefBase() : mCount(0) { }
inline void incStrong(const void* id) const {
//LightRefBase隻有一個引用計數控制量mCount。incStrong的時候使它增加1。
android_atomic_inc(&mCount);
}
inline void decStrong(const void* id) const {
//decStrong的時候減1,當引用計數變為零的時候,delete掉自己。
if (android_atomic_dec(&mCount) == 1) {
delete static_cast<const T*>(this);
}
}
inline int32_t getStrongCount() const {
return mCount;
}
protected:
inline ~LightRefBase() { }
private:
mutable volatile int32_t mCount;//引用計數控制變量。
};
LightRefBase類夠簡單吧?不過它是一個模闆類,我們該怎麼用它呢?下面給出一個例子,其中類A是從LightRefBase派生的,寫法如下:
class A:public LightRefBase<A> //注意派生的時候要指明是LightRefBase<A>。
{
public:
A(){};
~A(){};
};
另外,我們從LightRefBase的定義中可以知道,它支援sp的控制,因為它隻有incStrong和decStrong函數。
5.2.5 題外話—三闆斧的來曆
從代碼量上看,RefBase、sp和wp的代碼量并不多,但裡面的關系,尤其是flags的引入,曾一度讓我眼花缭亂。當時,我确實很希望能自己調試一下這些例子,但在裝置上調試native代碼,需要花費很大的精力,即使是通過輸出log的方式來調試也需要花很多時間。該怎麼解決這一難題?
既然它的代碼不多而且簡單,那何不把它移植到桌上型電腦的開發環境下,整一個類似的RefBase呢?有了這樣的構想,我便用上了Visual Studio。至于那些原子操作,Windows平台上有很直接的InterlockedExchangeXXX與之對應,真的是踏破鐵鞋無覓處,得來全不費功夫!(在Linux平台上,不考慮多線程的話,将原子操作換成普通的非原子操作不是也可以嗎?如果更細心更負責任的話,你可以自己用彙編來實作常用的原子操作,核心代碼中有現成的函數,一看就會明白。)
如果把破解代碼看成是攻城略地的話,我們必須學會靈活多變,而且應力求破解方法日臻極緻!