Android中的智能指針

指針

在傳統(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ù),必須用spwp去指向一個(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)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容