指針
在傳統(tǒng)的C++編程中,指針的使用一直是一把雙刃劍。指針賦予了我們直接操作硬件地址的能力,但同時(shí)也帶來(lái)了諸多問(wèn)題。其中最常見(jiàn)的問(wèn)題可以歸納為三個(gè):
1、指針沒(méi)有初始化。
2、new了對(duì)象以后沒(méi)有及時(shí)delete。
3、野指針。指針指向的對(duì)象已經(jīng)被delete了,但是指針并沒(méi)有被置空。這種情況多發(fā)生在多個(gè)指針同時(shí)指向同一個(gè)對(duì)象,其中某個(gè)指針釋放了對(duì)象的情況。
為了解決上述這些問(wèn)題,Android引入了引用計(jì)數(shù)技術(shù),只有當(dāng)引用計(jì)數(shù)為0的時(shí)候,才允許并且自動(dòng)釋放對(duì)象。但傳統(tǒng)的引用計(jì)數(shù)技術(shù),對(duì)于父子循環(huán)依賴的情況是無(wú)能為力的。因此又衍生出了強(qiáng)弱引用計(jì)數(shù)技術(shù)。因?yàn)閺?qiáng)弱引用計(jì)數(shù)其實(shí)包含了普通引用計(jì)數(shù)的部分,所以我們就直接介紹強(qiáng)弱引用計(jì)數(shù)的實(shí)現(xiàn)方式。
強(qiáng)弱引用計(jì)數(shù)
引用計(jì)數(shù)需要解決的關(guān)鍵點(diǎn)有三點(diǎn):
一、如何得知一個(gè)新的引用指向了該對(duì)象,如何得知指向該對(duì)象的舊引用已經(jīng)失效
二、該對(duì)象的引用計(jì)數(shù)值如何維護(hù),存儲(chǔ)在哪里,何時(shí)進(jìn)行修改。
三、釋放該對(duì)象的合適的時(shí)機(jī)是什么時(shí)候?
Android中的強(qiáng)弱引用計(jì)數(shù)的總體思路是比較簡(jiǎn)單的。
1、為所有的需要使用到引用計(jì)數(shù)的對(duì)象提供一個(gè)基類,這個(gè)基類中有一個(gè)內(nèi)部類專門(mén)負(fù)責(zé)維護(hù)該對(duì)象的強(qiáng)引用計(jì)數(shù)和弱引用計(jì)數(shù)。
2、定義一個(gè)強(qiáng)指針類sp,重載其所有賦值相關(guān)操作,當(dāng)把對(duì)象賦值給sp的時(shí)候,sp會(huì)增加該對(duì)象的引用計(jì)數(shù),當(dāng)sp不再指向該對(duì)象的時(shí)候,會(huì)在sp的析構(gòu)函數(shù)中減少該對(duì)象的引用計(jì)數(shù)
3、定義弱指針類wp,和sp類似,唯一的區(qū)別是沒(méi)有重載解引用操作。因此當(dāng)wp需要真正使用所指向的對(duì)象時(shí)需要提升至強(qiáng)指針。
4、對(duì)象主要有兩種生命周期管理方式。一是由強(qiáng)指針控制生命周期,當(dāng)強(qiáng)引用計(jì)數(shù)為0時(shí)就回收該對(duì)象。弱指針不能再提升為強(qiáng)指針。一種是由弱指針控制生命周期,只有當(dāng)強(qiáng)引用計(jì)數(shù)和弱引用計(jì)數(shù)同時(shí)為0的時(shí)候,才可以回收該對(duì)象。
主要思路如上4點(diǎn),剩下的都是具體的實(shí)現(xiàn)方式以及某些特殊情況的處理。比如從來(lái)沒(méi)有強(qiáng)指針指向過(guò)該對(duì)象,而生命周期又由強(qiáng)指針控制的情況等等。
舉個(gè)栗子
舉一個(gè)不恰當(dāng)?shù)纳畹睦觼?lái)類比一下:
A是個(gè)燒餅鋪的老板,能夠制作燒餅并且出售。
為了擴(kuò)大銷量,A允許他人簽訂代理合同進(jìn)行分銷。也允許他人僅僅簽訂意向合同,在之后再簽訂正式的代理合同。但是簽訂代理合同之前必須先簽訂意向合同。
以上是約束條件。
這個(gè)時(shí)候B和A簽訂了一份代理合同放在A那里。C也和A簽訂了一份代理合同放在A那里。于是A就知道他有兩個(gè)銷售代理,那他們存續(xù)期間自己是不能關(guān)門(mén)的,因?yàn)樗麄冸S時(shí)可能需要取貨。
這時(shí)候,D也想做A的代理但又下不了決心,于是跟A簽訂了一份意向合同,只是表示自己有這個(gè)想法,如果決定了再簽訂正式的代理合同。
這個(gè)時(shí)候,A手里就有三份意向合同,兩份代理合同。
假設(shè)B, C決定不再做A的代理,取消了跟A的代理以及意向合同。這時(shí)候A手里就只剩下和D的意向合同了。
此時(shí)就分化為幾種情況。
A決定關(guān)門(mén)了。也就是說(shuō)A關(guān)不關(guān)門(mén),只看自己手上的代理合同,而不看意向合同。而當(dāng)D想要前來(lái)正式簽訂代理合同的時(shí)候發(fā)現(xiàn)A已經(jīng)關(guān)門(mén)了,代理合同簽訂失敗。
這就是生命周期由強(qiáng)引用計(jì)數(shù)控制,而強(qiáng)引用計(jì)數(shù)為0,弱指針提升為強(qiáng)指針失敗的例子。
A沒(méi)有關(guān)門(mén),而是等待D前來(lái)取消了意向合同才關(guān)門(mén)。
這就是生命周期由弱引用計(jì)數(shù)控制,而弱引用計(jì)數(shù)變化為0的情況。
而如果A沒(méi)有關(guān)門(mén),直到D前來(lái)又簽訂了一份正式的代理合同。
那么這就是生命周期由弱引用計(jì)數(shù)控制,而弱指針成功提升為強(qiáng)指針的情況。
從這個(gè)例子中我們可以看到一個(gè)問(wèn)題:如果A手里的意向合同有聯(lián)系方式的話,是不是可以直接打電話告訴D,自己打算關(guān)門(mén)了而不用等D上門(mén)來(lái)簽合同的時(shí)候才知道呢?也即RefBase中除了引用計(jì)數(shù)的值之外,是不是也可以保存一個(gè)wp和sp列表呢?
1、引用產(chǎn)生和失效的判斷
針對(duì)第一點(diǎn),Android中定義了兩個(gè)類模板類sp和wp。分別是strongPointer和weakPointer的縮寫(xiě)。他們內(nèi)部都持有一個(gè)T* m_ptr的對(duì)象引用。并且重載了所有賦值和構(gòu)造相關(guān)的函數(shù)。也就是說(shuō)當(dāng)出現(xiàn)諸如sp<Object> sp_object = object;的調(diào)用時(shí),sp類的重載函數(shù)就會(huì)被調(diào)用。而這些重載函數(shù)內(nèi)部的主要邏輯就是增加object對(duì)象的強(qiáng)引用計(jì)數(shù)值(增加強(qiáng)引用計(jì)數(shù)的同時(shí),弱引用計(jì)數(shù)也會(huì)同時(shí)增加)。wp也是同理,只不過(guò)增加的是弱引用計(jì)數(shù)值。于是通過(guò)sp和wp去引用一個(gè)對(duì)象,就解決了如何得知新的引用產(chǎn)生的問(wèn)題。同理,引用失效的時(shí)機(jī)就在sp和wp的析構(gòu)函數(shù)中,其主要邏輯也就是減少object的強(qiáng)弱引用計(jì)數(shù)。
所以我們這里明確,要使用強(qiáng)弱引用技術(shù),就必須使用sp和wp去引用一個(gè)對(duì)象。普通的指針是沒(méi)有這種效果的。sp和wp的代碼如下:
sp
template<typename T>
class sp
{
? ? public:
? ? ? ? ? ?typedef typename RefBase::weakref_type weakref_type; //暫時(shí)可以忽略
? ? ? ? ? ?inline sp():m_ptr(0) {}? //構(gòu)造函數(shù)
? ? ? ? ? /*以下這段就是賦值相關(guān)的操作符重載以及構(gòu)造相關(guān)的重載函數(shù),當(dāng)對(duì)sp賦值或者構(gòu)造sp的時(shí)候,這些函數(shù)就會(huì)被調(diào)用。其中除了各函數(shù)原本的邏輯之外,最重要的邏輯就是修改引用對(duì)象的引用計(jì)數(shù)*/
? ? ? ? ? ?sp(T* other);? ? ?
? ? ? ? ? ?sp(const sp<T>& other);?
? ? ? ? ? ?template<typename U> sp(U* other);?
? ? ? ? ? ?template<typename U> sp(const sp<U>& other);
? ? ? ? ? ?sp& operator = (T* other);
? ? ? ? ? ?sp& operator = (const sp<T> & other);
? ? ? ? ? ?template<typename U> sp& operator = (const sp<U>& other);
? ? ? ? ? ?template<typename U> sp& operator = (U* other);
? ? ? ? ? /*以上*/
? ? ? ? ? ~sp;? ?//析構(gòu)函數(shù)? ?減少所引用對(duì)象的引用計(jì)數(shù)
? ? ? ? ? //解引用部分,也即調(diào)用sp->以及sp.操作時(shí),實(shí)際調(diào)用的是其所指向的對(duì)象m_ptr的操作。
? ? ? ? ? inline T& operator* () const {return *m_ptr;}
? ? ? ? ? inline T* operator->() const {return m_ptr;}
? ? ? ? ? inline T* get() const {return m_ptr;}
? ? ? ? ? ...
? ? ? ? ? //其余比較操作符重載的部分,因?yàn)閷?duì)于sp的比較實(shí)際上應(yīng)該是m_ptr之間的比較
? ? ? ? ? ...
? ? ?private:
? ? ? ? ? ?sp(T* p,? weakref_type* refs);? ?//暫時(shí)可以忽略
? ? ? ? ? ?T* m_ptr;? //指向的真實(shí)的對(duì)象。
}
由于C++提供了操作符重載操作,所以sp重載了->和*等操作符,使得對(duì)于sp的操作等同于直接對(duì)于m_ptr的操作。同時(shí)重載了賦值操作符=,使得賦值的時(shí)候我們有機(jī)會(huì)進(jìn)行一些操作。構(gòu)造函數(shù)以及拷貝構(gòu)造函數(shù)也做了適當(dāng)?shù)奶幚怼?/p>
wp
wp的主要實(shí)現(xiàn)和sp一致,重載比較操作符,解引用操作符,賦值操作符等。在構(gòu)造函數(shù),拷貝函數(shù)以及賦值時(shí)增加引用對(duì)象的引用計(jì)數(shù)。在析構(gòu)函數(shù)中減少引用對(duì)象的引用計(jì)數(shù)。其主要代碼如下:
template <typename T>
class wp
{
? ? public:
? ? ? ? typedef typename RefBase::weakref_type weakref_type;
? ? ? ? inline wp(): m_ptr(0) {}
? ? ? ? wp(T* other);
? ? ? ? wp(const wp<T>& other);
? ? ? ? wp(const sp<T>& other);
? ? ? ? template<typename U> wp(U* other);
? ? ? ? template<typename U> wp(const sp<U>& other);
? ? ? ? template<typename U> wp(const wp<U>& other);
? ? ? ? wp& operator = (T* ohter);
? ? ? ? wp& operator = (const wp<T>& other);
? ? ? ?wp& operator = (const sp<T>& other);
? ? ? ?template<typename U> wp& operator = (U* other);
? ? ? ?template<typename U> wp& operator = (const wp<U>& other);
? ? ? ?template<typename U> wp&operator = (const sp<U>& other);
? ? ? ?~wp();
? ? ? ?sp<T> promote() const;? ?//用于將弱引用提升為強(qiáng)引用
? ? ? ?//其余操作符重載部分
? ? ? ?...
private:
? ? ? ? T* m_ptr;? ?//引用的對(duì)象
? ? ? ? weakref_type* m_refs;
}
可以看到wp和sp的主要結(jié)構(gòu)是一致的。但是wp多了一個(gè)promote函數(shù)以及m_refs成員變量。前者是為了將弱引用提升為強(qiáng)引用用的。后者稍后會(huì)提及。
2、引用計(jì)數(shù)值的維護(hù)
在1中,我們知道當(dāng)sp或者wp被賦值以及析構(gòu)的時(shí)候,都會(huì)去修改對(duì)象的引用計(jì)數(shù)值。但是這個(gè)引用計(jì)數(shù)究竟是放在哪里的?放在wp或者sp中是不合適的,因?yàn)橹赶蛲粋€(gè)對(duì)象的sp或者wp可能同時(shí)有多個(gè)。那既然所有的sp和wp都能夠獲得引用的對(duì)象,那我們能夠想到的就是由所引用的對(duì)象自己去維護(hù)這個(gè)引用計(jì)數(shù)了。因此,Android為所有需要實(shí)現(xiàn)引用計(jì)數(shù)的對(duì)象提供了一個(gè)基類。RefBase. RefBase擁有一個(gè)weakref_impl類型的成員變量mRefs,weakref_impl又具有int_32_t類型的mStrong以及mWeak兩個(gè)成員變量。他們分別負(fù)責(zé)記錄一個(gè)對(duì)象的強(qiáng)引用計(jì)數(shù)和弱引用計(jì)數(shù)。所有關(guān)于引用計(jì)數(shù)的操作最終實(shí)際上就是修改這兩個(gè)變量的值。需要注意的是weakref_impl是繼承了weakref_type類型。weakref_type類型是在RefBase中定義的。
RefBase
class RefBase?
{
public:
? ? void? incStrong(const void* id) const;
? ? void decStrong(const void* id) const;
? ? class weakref_type?
? ? {
? ? ?public:?
? ? ? ? ?RefBase*? refBase() const;
? ? ? ? ?void incWeak(const void* id);
? ? ? ? ?void decWeak(const void* id);
? ? ? ? ?bool attemptIncStrong(const void* id);
? ? ?}
protected:
? ?RefBase();??
? ?virtual ~RefBase();
? ?emum {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//默認(rèn)情況該對(duì)象的生命周期由強(qiáng)引用計(jì)數(shù)控制
? ? ? ? OBJECT_LIFETIME_WEAK = 0x0001,? ?//該對(duì)象的生命周期由弱引用計(jì)數(shù)控制
? ? ? ? OBJECT_LIFETIME_FOREVER = 0x0003;? //該對(duì)象的生命周期由用戶自己維護(hù)
? ?};
? ?void? extendObjectLifetime(int32_t mode);? ?//修改該對(duì)象的生命周期控制條件
? ?enum {
? ? ? ?FIRST_INC_STRONG = 0x0001;
? ?}
? ? virtual void onFirstRef();? ?//第一次有引用指向了這個(gè)對(duì)象。Andorid系統(tǒng)中很多組件的初始化都是放在這個(gè)函數(shù)里的。
? ? virtual void onLastStrongRef(const void* id);? //強(qiáng)引用計(jì)數(shù)變?yōu)榱?.
? ? virtual bool onIncStrongRef(const void* id);
? ? virtual void onLastWeakRef(const void* id);? //弱引用計(jì)數(shù)變?yōu)榱?.
private :
? ? RefBase(const RefBase& o);
? ? RefBase operator = (const Refbase& o);
? ? weakref_impl* const mRefs;? //真正維護(hù)強(qiáng)弱引用計(jì)數(shù)的對(duì)象
}
weakref_impl
class RefBase::weakref_impl : public RefBase::weakref_type?
{
? ? public:
? ? ? ?volatile int32_t mStrong;
? ? ? ?volatile int32_t mWeak;
? ? ? ?RefBase* const mBase;
? ? ? ?volatile int32_t mFlags;
? ? ? ?weakref_impl(RefBase* base)
? ? ? ? ? ?:mStrong(INITIAL_STRONG_VALUE)
? ? ? ? ? ? ,mWeak(0)
? ? ? ? ? ? ,mBase(base)
? ? ? ? ? ? ,mFlgas(0)?
? ? ? ? {}
? ? ? ? //debug相關(guān)函數(shù)
? ? ? ?...
}
通過(guò)一張類圖,我們可以更為清晰的了解三者之間的關(guān)系。
我們?cè)谟^察下incStrong, decStrong, incWeak, decWeak的實(shí)現(xiàn)。
incStrong
void RefBase::incStrong(const void * id) const
{
? ? weakref_impl* const refs = mRefs;
? ? refs->incWeak(id);? //增加強(qiáng)引用的時(shí)候必須同時(shí)增加弱引用。所以弱引用計(jì)數(shù)的數(shù)值是肯定大于強(qiáng)引用計(jì)數(shù)的。而弱引用計(jì)數(shù)為0的時(shí)候,強(qiáng)引用計(jì)數(shù)肯定為0.
? ? const int32_t c= android_atomic_inc(&refs->mStrong);? ? //c是atomic_inc之前的值
? ? if (c != INITIAL_STRONG_VALUE) {? //說(shuō)明不是第一次
? ? ? ? ?return;
? ? }
? ? android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);? //如果是第一次,需要減去INITAL_STRONG_VALUE。
? ? const_cast<RefBase*>(this)->onFirstRef();??
}
給refs->mStrong設(shè)定一個(gè)INITIAL_STRONG_VALUE的初始值的意義在于區(qū)分這個(gè)值是不是被修改過(guò)。而如果初始值是0的話,就分不清楚是本來(lái)就為0,還是經(jīng)過(guò)一系列增增減減后又變?yōu)榱?.這跟后面的生命周期有直接關(guān)系。如果mStrong的值為INITAL_STRONG_VALUE,那就說(shuō)明沒(méi)有任何強(qiáng)引用引用過(guò)這個(gè)對(duì)象。
incWeak
void RefBase::weakref_type::incWeak(const void* id)
{
? ? weakref_impl* const impl = static_cast<weakref_impl*>(this);
? ? const int32_t c = android_atomic_inc(&impl->mWeak);
}
decStrong
void RefBase::decStrong(const void* id) const
{
? ? weakref_impl* const refs = mRefs;
? ? const int32_t c = android_atomic_dec(&refs->mStrong);
? ? refs->decWeak(id);
? ? ...
}
decWeak
void RefBase::weakref_type::decWeak(const void* id)
{
? ? weakref_impl* const impl = static_cast<weakref_impl>(this);
? ? const int32_t c = android_atomic_dec(&impl->mWeak);
? ?...
}
可以看到IncStrong, incWeak, decStrong, decWeak的主要邏輯都是修改weakref_impl的成員變量mStrong以及mWeak的值。
至此,結(jié)合第1節(jié)以及第2節(jié)的內(nèi)容,我們可以得出操作強(qiáng)弱引用計(jì)數(shù)的大體步驟。
1、需要使用引用計(jì)數(shù)的對(duì)象必須繼承自RefBase。 RefBase中有個(gè)實(shí)際類型為weakref_impl,父類型為weakref_type的mRefs成員,其包含兩個(gè)成員變量mStrong和mWeak. 所有RefBase上調(diào)用的incStrong, incWeak, decWeak, decStrong最終的操作都是修改這兩個(gè)值。
2、要使用引用計(jì)數(shù),必須用sp和wp去指向一個(gè)繼承自RefBase的對(duì)象。在sp和wp初始化操作以及析構(gòu)函數(shù)中,會(huì)調(diào)用RefBase相關(guān)的操作引用計(jì)數(shù)的方法。
簡(jiǎn)化后的理解就是:
sp或者wp初始化或者析構(gòu)------>獲取RefBase類型的成員變量m_ptr------>獲取RefBase的weakref_impl類型的mRefs成員變量------>修改mStrong以及mWeak的值。
3、對(duì)象的生命周期
前兩節(jié)中,我們已經(jīng)清楚了對(duì)象是怎么感知到指向自身的引用變化以及引用計(jì)數(shù)的數(shù)值更改的流程。但是這個(gè)引用計(jì)數(shù)值究竟是如何變化的,又怎么和對(duì)象的生命周期產(chǎn)生關(guān)聯(lián)的,我們還沒(méi)有提及。
主要是兩點(diǎn)。一是RefBase對(duì)象回收的時(shí)機(jī),二是weakref_impl對(duì)象回收的時(shí)機(jī)。
在第二節(jié)中,我們提到過(guò)。RefBase的生命周期有三種形式:OBJECT_LIFETIME_STRONG,OBJECT_LIFETIME_WEAK,OBJECT_LIFETIME_FOREVER。分別代表強(qiáng)引用計(jì)數(shù)控制生命周期,弱引用計(jì)數(shù)控制生命周期,以及用戶自己控制其生命周期。
OBJECT_LIFETIME_STRONG
這種方式下。RefBase對(duì)象的生命周期完全由強(qiáng)引用計(jì)數(shù)控制。所以我們很容易的就能想到當(dāng)weakref_imp的mStrong值為0的時(shí)候也就可以釋放RefBase對(duì)象了。因此我們可以在decStrong中去進(jìn)行判斷。事實(shí)上也是如此。
void RefBase::decStrong(const void* id) const
{
? ? weakref_impl* const refs = mRefs;
? ? const int32_t c = android_atomic_dec(&refs->mStrong);
? ? if (c == 1) {
? ? ? ? ?const_cast<RefBase*>(this)->onLastStrongRef(id);
? ? ? ? //生命周期由強(qiáng)引用計(jì)數(shù)控制,且強(qiáng)引用計(jì)數(shù)值為0,那么就直接回收這個(gè)對(duì)象。
? ? ? ? ?if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
? ? ? ? ? ? ? delete this;
? ? ? ? ?}
? ? }
? ? refs->decWeak(id);
}
當(dāng)RefBase的生命周期控制方式不為OBJECT_LIFETIME_WEAK時(shí),也即完全由強(qiáng)引用計(jì)數(shù)決定時(shí),那么在強(qiáng)引用計(jì)數(shù)為0時(shí)就可以直接delete this了。
OBJECT_LIFETIME_WEAK
如果RefBase的生命周期由弱引用計(jì)數(shù)控制。那么在強(qiáng)引用計(jì)數(shù)為0的時(shí)候,就不能直接delete對(duì)象。而必須延遲到decWeak中去進(jìn)行判斷。當(dāng)
void RefBase::weakref_type::decWeak(const void* id)
{
? ? weakref_impl* const impl = static_cast<weakref_impl*>(this);
? ? const int32_t c = android_atomic_dec(&impl->mWeak);
? ? if (c != 1) return;? //弱引用計(jì)數(shù)不為0,就直接返回
? ? if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {?
? ? ? ? if (impl->mStrong == INITIAL_STRONG_VALUE)??
? ? ? ? ? ? ?//弱引用計(jì)數(shù)為0,且強(qiáng)引用計(jì)數(shù)為初始值,且生命周期不由弱引用計(jì)數(shù)控制
? ? ? ? ? ? //就需要?jiǎng)h除該對(duì)象。因?yàn)闆](méi)有強(qiáng)引用指向過(guò)該對(duì)象,也就不可能有decStrong的機(jī)會(huì)去回收這個(gè)對(duì)象。
? ? ? ? ? ? ? delete impl->mBase;??
? ? ? ? ?else
? ? ? ? ? ? ? //生命周期由強(qiáng)引用計(jì)數(shù)控制,那么現(xiàn)在弱引用計(jì)數(shù)為0,強(qiáng)引用計(jì)數(shù)也必然為0,那么在decStrong中該對(duì)象已經(jīng)被回收過(guò)了。
? ? ? ? ? ? ? //只需要?jiǎng)h除weakref_impl對(duì)象即可
? ? ? ? ? ? ? ?delete impl;??
? ? } else {? //生命周期由弱引用計(jì)數(shù)控制
? ? ? ? ? ? impl->mBase->onLastWeakRef(id);
? ? ? ? ? ? //生命周期由弱引用計(jì)數(shù)控制,且弱引用計(jì)數(shù)為0,那么就需要回收該對(duì)象。
? ? ? ? ? ? if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
? ? ? ? ? ? ? ? ?delete impl->mBase;
? ? ? ? ? ? }
? ? }
}
整理一下,RefBase對(duì)象的回收時(shí)機(jī)有以下幾種情況:
1、生命周期由強(qiáng)引用計(jì)數(shù)控制,有強(qiáng)引用指向過(guò)該對(duì)象,那么對(duì)象在decStrong中發(fā)現(xiàn)mStrong為0時(shí)回收。
2、生命周期由強(qiáng)引用計(jì)數(shù)控制,從來(lái)沒(méi)有強(qiáng)引用指向過(guò)該對(duì)象,有弱引用指向過(guò)該對(duì)象。那么對(duì)象在decWeak中發(fā)現(xiàn)mWeak為0時(shí)回收。
3、生命周期由弱引用計(jì)數(shù)控制,mWeak為0時(shí)回收該對(duì)象。
weakref_impl的回收時(shí)機(jī)則有兩個(gè),一是在RefBase的析構(gòu)函數(shù)中。另一個(gè)則是在decWeak中的delete impl處。
如果是由弱引用計(jì)數(shù)控制RefBase生命周期的話,那么當(dāng)RefBase對(duì)象析構(gòu)時(shí),那弱引用計(jì)數(shù)肯定為0,強(qiáng)引用計(jì)數(shù)也肯定為0,所以在RefBase的析構(gòu)函數(shù)中就可以安全回收weakref_impl對(duì)象。
如果是由強(qiáng)引用計(jì)數(shù)控制RefBase生命周期的話,那么當(dāng)RefBase對(duì)象析構(gòu)時(shí),強(qiáng)引用計(jì)數(shù)為0,弱引用計(jì)數(shù)不一定為0,那么就不能夠在RefBase析構(gòu)函數(shù)中去回收weakref_impl。而只能在弱引用計(jì)數(shù)為0時(shí)去進(jìn)行回收。
因此現(xiàn)在RefBase的生命周期徹底和其內(nèi)部的weakref_impl的mWeak和mStrong的值,以及wp和sp的構(gòu)造和析構(gòu)函數(shù)聯(lián)系在一起了。
簡(jiǎn)而言之就是:
wp以及sp的析構(gòu)函數(shù)會(huì)觸發(fā)所引用的RefBase對(duì)象的decStrong或者decWeak函數(shù),從而改變RefBase的weakref_impl類型的成員變量的mWeak以及mStrong值。
從而觸發(fā)RefBase對(duì)象的回收邏輯。
弱引用提升為強(qiáng)引用
在上一節(jié)中,比較特別的一種情況就是強(qiáng)引用計(jì)數(shù)為0,弱引用計(jì)數(shù)不為0的情況,且生命周期由強(qiáng)引用計(jì)數(shù)控制的情況。在這種情況下,實(shí)際上RefBase對(duì)象已經(jīng)被回收了,但是仍然有弱引用指向了這個(gè)對(duì)象。那么怎么防止弱引用使用這個(gè)已經(jīng)被回收的對(duì)象呢?
Android中使用的方法是不重載wp類的解引用操作符,operator*以及operator->,使得wp無(wú)法直接使用m_ptr對(duì)象,必須調(diào)用wp的promote方法將wp升級(jí)為sp才能夠使用。因此wp的promote方法是關(guān)鍵。
template<typename T>
sp<T> wp<T>::promote() const
{
? ? ?return sp<T><m_ptr, m_refs);? //調(diào)用強(qiáng)引用的構(gòu)造器
}
template<typename T>
sp<T>::sp(T* p, weakref_type* refs):m_ptr(p && refs->attemptIncStrong(this)) ? p : 0) {}? //試圖調(diào)用弱引用所指向?qū)ο蟮膚eakref_impl成員的attemptIncStrong方法。
bool RefBase:;weakref_type::attemptIncStrong(const void* id)?
{
? ? incWeak(id);
? ? weakref_impl* const impl = static_cast<weakref_impl*>(this);
? ? int32_t curCount = impl->mStrong;
? ?//如果強(qiáng)引用計(jì)數(shù)不為0,且不為初始值,那肯定是可以增加強(qiáng)引用計(jì)數(shù)的。
? ? while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {? //自旋
? ? ? ?//cmpxchg是個(gè)原子操作,全稱是compareAndChange,只有mStrong的值為curCount時(shí),才會(huì)將其值修改為curCount + 1, 否則修改失敗。
? ? ? //這樣子便可以防止其他線程先一步修改了mStrong的值。在ConcurrentHashmap以及輕量級(jí)鎖中都用到了這個(gè)原子操作。
? ? ? ? if (android_atomic_cmpxchg(curCount, curCount + 1, &impl->mStrong) == 0) {
? ? ? ? ? ? ? //修改curCount值與前面while中判斷的間隙期間,引用計(jì)數(shù)值沒(méi)有變化,那么實(shí)際上是可以直接返回true了,不會(huì)進(jìn)入后面的分支條件
? ? ? ? ? ? ? break;
? ? ? ? }
? ? ? ? //修改curCount值與前面while中判斷的間隙期間,引用計(jì)數(shù)值發(fā)生變化了。將curCount更新為mStrong的最新值,再次嘗試修改。
? ? ? ? curCount = impl->mStrong;?
? ? }
? ? //這里退出循環(huán)有兩種情況,一種是修改成功break了,那么是不會(huì)進(jìn)入下面的條件的。
? ?//另一個(gè)是修改失敗了并且獲取最新的引用計(jì)數(shù)值時(shí)發(fā)現(xiàn)其值為0了。那么會(huì)進(jìn)入下面的分支。
? ? if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {? //之前強(qiáng)引用計(jì)數(shù)為0,或者為初始值
? ? ? ? ?bool allow;
? ? ? ? ?if (curCount == INITIAL_STRONG_VALUE) {??
? ? ? ? ? ??//該對(duì)象之前并沒(méi)有被強(qiáng)引用指向過(guò),那么只要生命周期不由弱引用控制,那么該對(duì)象就不可能被回收過(guò)。那么就可以提升為強(qiáng)指針
? ? ? ? ? ?//如果該對(duì)象生命周期由弱引用控制,顯然該對(duì)象也未被回收,那么就看調(diào)用該對(duì)象的onIncStrongAttempted方法看該對(duì)象是否允許被提升為強(qiáng)指針。
? ? ? ? ? //onIncStrongAttempted方法參數(shù)為FIRST_INC_STRONG時(shí)都是返回true的
? ? ? ? ? ? ?allow = (impl->mFlags&OBJECT_LIFETIME_WEAK)? != OBJECT_LIFETIME_WEAK || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
? ? ? ? ?} else {
? ? ? ? ? ?//該對(duì)象的強(qiáng)引用計(jì)數(shù)為0,那么只有生命周期由弱引用控制的時(shí)候,才可能沒(méi)有被回收,因?yàn)楫?dāng)前正有一個(gè)弱引用指向?qū)ο蟆?/p>
? ? ? ? ? //那么就調(diào)用該對(duì)象的onIncStrongAttempted方法看該對(duì)象是否允許被提升為強(qiáng)指針。
? ? ? ? ? //onIncStrongAttempted方法參數(shù)為FIRST_INC_STRONG時(shí)都是返回true的
? ? ? ? ? ? allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
? ? ? ? ?}
? ? ? ? if (!allow) {
? ? ? ? ? ? ?decWeak(id);
? ? ? ? ? ? ?return false;
? ? ? ? }
? ? ? ?curCount = android_atomic_inc(&impl->mStrong);? //如果允許的話就增加強(qiáng)引用計(jì)數(shù)
? ? }
? ??//修正mStrong 的值并且調(diào)用onFirstRef方法。
? ? if (curCount == INITIAL_STRONG_VALUE) {??
? ? ? ? ?android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);
? ? ? ? ?impl->mBase->onFirstRef();??
? ? }
? ? return true;
}
//可以看到當(dāng)參數(shù)為FIRST_INC_STRONG時(shí),是一直返回true的
bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id)?
{
? ? return (flags& FIRST_INC_STRONG) ? true : false;
}
所以結(jié)合這段將弱指針提升為強(qiáng)指針的代碼,我們可以看到:
attemptIncStrong的主要邏輯就是判斷該對(duì)象是不是已經(jīng)回收了,實(shí)際上只要是該對(duì)象沒(méi)有被回收,那么都是可以被提升到強(qiáng)指針的。(由于這段代碼里onIncStrongAttempted都是返回true,所以可以忽略)
如果強(qiáng)引用計(jì)數(shù)值大于0且不等于INITIAL_STRONG_VALUE,那么就肯定可以提升為強(qiáng)指針。這也是while那段邏輯所判斷的。
如果強(qiáng)引用計(jì)數(shù)等于0,那么只有生命周期由弱引用計(jì)數(shù)控制的時(shí)候才沒(méi)有被回收。
如果強(qiáng)引用計(jì)數(shù)等于INITIAL_STRONG_VALUE,那么也是可以被提升為強(qiáng)指針的。
以上的代碼和解說(shuō)基本上都是照搬《Android系統(tǒng)源代碼情景分析》中的,簡(jiǎn)化了某些不必要的代碼部分。但是仍然過(guò)于繁瑣。我覺(jué)得作為一個(gè)Android應(yīng)用層開(kāi)發(fā)的程序員,對(duì)于框架層的代碼,還是抱著不求甚解的態(tài)度去閱讀比較好。抓大放小,理解關(guān)鍵實(shí)現(xiàn)方式,而不應(yīng)該對(duì)其中的細(xì)節(jié)糾纏過(guò)多。因?yàn)榧?xì)節(jié)在你真正去實(shí)現(xiàn)的時(shí)候,你自然會(huì)發(fā)現(xiàn)也能夠解決,并不是阻擋你實(shí)現(xiàn)的關(guān)鍵障礙。
大致情況就是這樣子。如果以后找到更好的表述方式的話會(huì)再進(jìn)行改進(jìn)。