1. 程式人生 > >Android系統的智慧指標(輕量級指標、強指標和弱指標)的實現原理分析

Android系統的智慧指標(輕量級指標、強指標和弱指標)的實現原理分析

        Android系統的執行時庫層程式碼是用C++來編寫的,用C++來寫程式碼最容易出錯的地方就是指標了,一旦使用不當,輕則造成記憶體洩漏,重則造成系統崩潰。不過系統為我們提供了智慧指標,避免出現上述問題,本文將系統地分析Android系統智慧指標(輕量級指標、強指標和弱指標)的實現原理。

《Android系統原始碼情景分析》一書正在進擊的程式設計師網(http://0xcc0xcd.com)中連載,點選進入!

        在使用C++來編寫程式碼的過程中,指標使用不當造成記憶體洩漏一般就是因為new了一個物件並且使用完之後,忘記了delete這個物件,而造成系統崩潰一般就是因為一個地方delete了這個物件之後,其它地方還在繼續使原來指向這個物件的指標。為了避免出現上述問題,一般的做法就是使用引用計數的方法,每當有一個指標指向了一個new出來的物件時,就對這個物件的引用計數增加1,每當有一個指標不再使用這個物件時,就對這個物件的引用計數減少1,每次減1之後,如果發現引用計數值為0時,那麼,就要delete這個物件了,這樣就避免了忘記delete物件或者這個物件被delete之後其它地方還在使用的問題了。但是,如何實現這個物件的引用計數呢?肯定不是由開發人員來手動地維護了,要開發人員時刻記住什麼時候該對這個物件的引用計數加1,什麼時候該對這個物件的引用計數減1,一來是不方便開發,二來是不可靠,一不小心哪裡多加了一個1或者多減了一個1,就會造成災難性的後果。這時候,智慧指標就粉墨登場了。首先,智慧指標是一個物件,不過這個物件代表的是另外一個真實使用的物件,當智慧指標指向實際物件的時候,就是智慧指標物件建立的時候,當智慧指標不再指向實際物件的時候,就是智慧指標物件銷燬的時候,我們知道,在C++中,物件的建立和銷燬時會分別自動地呼叫物件的建構函式和解構函式,這樣,負責對真實物件的引用計數加1和減1的工作就落實到智慧指標物件的建構函式和解構函式的身上了,這也是為什麼稱這個指標物件為智慧指標的原因。

        在電腦科學領域中,提供垃圾收集(Garbage Collection)功能的系統框架,即提供物件託管功能的系統框架,例如Java應用程式框架,也是採用上述的引用計數技術方案來實現的,然而,簡單的引用計數技術不能處理系統中物件間迴圈引用的情況。考慮這樣的一個場景,系統中有兩個物件A和B,在物件A的內部引用了物件B,而在物件B的內部也引用了物件A。當兩個物件A和B都不再使用時,垃圾收集系統會發現無法回收這兩個物件的所佔據的記憶體的,因為系統一次只能收集一個物件,而無論系統決定要收回物件A還是要收回物件B時,都會發現這個物件被其它的物件所引用,因而就都回收不了,這樣就造成了記憶體洩漏。這樣,就要採取另外的一種引用計數技術了,即物件的引用計數同時存在強引用和弱引用兩種計數,例如,Apple公司提出的

Cocoa框架,當父物件要引用子物件時,就對子物件使用強引用計數技術,而當子物件要引用父物件時,就對父物件使用弱引用計數技術,而當垃圾收集系統執行物件回收工作時,只要發現物件的強引用計數為0,而不管它的弱引用計數是否為0,都可以回收這個物件,但是,如果我們只對一個物件持有弱引用計數,當我們要使用這個物件時,就不直接使用了,必須要把這個弱引用升級成為強引用時,才能使用這個物件,在轉換的過程中,如果物件已經不存在,那麼轉換就失敗了,這時候就說明這個物件已經被銷燬了,不能再使用了。

       瞭解了這些背景知識後,我們就可以進一步學習Android系統的智慧指標的實現原理了。Android系統提供了強大的智慧指標技術供我們使用,這些智慧指標實現方案既包括簡單的引用計數技術,也包括了複雜的引用計數技術,即物件既有強引用計數,也有弱引用計數,對應地,這三種智慧指標分別就稱為輕量級指標(Light Pointer)、強指標(Strong Pointer)和弱指標(Weak Pointer)。無論是輕量級指標,還是強指標和弱指標,它們的實現框架都是一致的,即由物件本身來提供引用計數器,但是它不會去維護這個引用計數器的值,而是由智慧指標來維護,就好比是物件提供素材,但是具體怎麼去使用這些素材,就交給智慧指標來處理了。由於不管是什麼型別的物件,它都需要提供引用計數器這個素材,在C++中,我們就可以把這個引用計數器素材定義為一個公共類,這個類只有一個成員變數,那就是引用計數成員變數,其它提供智慧指標引用的物件,都必須從這個公共類繼承下來,這樣,這些不同的物件就天然地提供了引用計數器給智慧指標使用了。總的來說就是我們在實現智慧指會的過程中,第一是要定義一個負責提供引用計數器的公共類,第二是我們要實現相應的智慧指標物件類,後面我們會看到這種方案是怎麼樣實現的。

        接下來,我們就先介紹輕量級指標的實現原理,然後再接著介紹強指標和弱指標的實現原理。

        1. 輕量級指標

        先來看一下實現引用計數的類LightRefBase,它定義在frameworks/base/include/utils/RefBase.h檔案中:

template <class T>
class LightRefBase
{
public:
	inline LightRefBase() : mCount(0) { }
	inline void incStrong(const void* id) const {
		android_atomic_inc(&mCount);
	}
	inline void decStrong(const void* id) const {
		if (android_atomic_dec(&mCount) == 1) {
			delete static_cast<const T*>(this);
		}
	}
	//! DEBUGGING ONLY: Get current strong ref count.
	inline int32_t getStrongCount() const {
		return mCount;
	}

protected:
	inline ~LightRefBase() { }

private:
	mutable volatile int32_t mCount;
};
         這個類很簡單,它只一個成員變數mCount,這就是引用計數器了,它的初始化值為0,另外,這個類還提供兩個成員函式incStrong和decStrong來維護引用計數器的值,這兩個函式就是提供給智慧指標來呼叫的了,這裡要注意的是,在decStrong函式中,如果當前引用計數值為1,那麼當減1後就會變成0,於是就會delete這個物件。

         前面說過,要實現自動引用計數,除了要有提供引用計數器的基類外,還需要有智慧指標類。在Android系統中,配合LightRefBase引用計數使用的智慧指標類便是sp了,它也是定義在frameworks/base/include/utils/RefBase.h檔案中:

template <typename T>
class sp
{
public:
	typedef typename RefBase::weakref_type weakref_type;

	inline sp() : m_ptr(0) { }

	sp(T* other);
	sp(const sp<T>& other);
	template<typename U> sp(U* other);
	template<typename U> sp(const sp<U>& other);

	~sp();

	// Assignment

	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);

	//! Special optimization for use by ProcessState (and nobody else).
	void force_set(T* other);

	// Reset

	void clear();

	// Accessors

	inline  T&      operator* () const  { return *m_ptr; }
	inline  T*      operator-> () const { return m_ptr;  }
	inline  T*      get() const         { return m_ptr; }

	// Operators

	COMPARE(==)
		COMPARE(!=)
		COMPARE(>)
		COMPARE(<)
		COMPARE(<=)
		COMPARE(>=)

private:
	template<typename Y> friend class sp;
	template<typename Y> friend class wp;

	// Optimization for wp::promote().
	sp(T* p, weakref_type* refs);

	T*              m_ptr;
};
        這個類的內容比較多,但是這裡我們只關注它的成員變數m_ptr、建構函式和解構函式。不難看出,成員變數m_ptr就是指向真正的物件了,它是在建構函式裡面初始化的。接下來我們就再看一下它的兩個建構函式,一個是普通建構函式,一個拷貝建構函式:
template<typename T>
sp<T>::sp(T* other)
    : m_ptr(other)
{
    if (other) other->incStrong(this);
}

template<typename T>
sp<T>::sp(const sp<T>& other)
    : m_ptr(other.m_ptr)
{
    if (m_ptr) m_ptr->incStrong(this);
}
        這兩個建構函式都會首先初始化成員變數m_ptr,然後再呼叫m_ptr的incStrong函式來增加物件的引用計數,在我們這個場景中,就是呼叫LightRefBase類的incStrong函數了。

        最後,看一下解構函式:

template<typename T>
sp<T>::~sp()
{
    if (m_ptr) m_ptr->decStrong(this);
}
        解構函式也很簡單,只是呼叫m_ptr的成員函式decStrong來減少物件的引用計數值,這裡就是呼叫LightRefBase類的decStrong函數了,前面我們看到,當這個引用計數減1後變成0時,就會自動delete這個物件了。

        輕量級智慧指標的實現原理大概就是這樣了,比較簡單,下面我們再用一個例子來說明它的用法。

        2. 輕量級指標的用法

        參考在Ubuntu上為Android系統內建C可執行程式測試Linux核心驅動程式一文,我們在external目錄下建立一個C++工程目錄lightpointer,它裡面有兩個檔案,一個lightpointer.cpp檔案,另外一個是Android.mk檔案。

        原始檔lightpointer.cpp的內容如下:

#include <stdio.h>
#include <utils/RefBase.h>

using namespace android;

class LightClass : public LightRefBase<LightClass>
{
public:
        LightClass()
        {
                printf("Construct LightClass Object.");
        }

        virtual ~LightClass()
        {
                printf("Destory LightClass Object.");
        }
};

int main(int argc, char** argv)
{
        LightClass* pLightClass = new LightClass();
        sp<LightClass> lpOut = pLightClass;

        printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());

        {
                sp<LightClass> lpInner = lpOut;

                printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());
        }

        printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());

        return 0;
}
        我們建立一個自己的類LightClass,繼承了LightRefBase模板類,這樣類LightClass就具有引用計數的功能了。在main函式裡面,我們首先new一個LightClass物件,然後把這個物件賦值給智慧指標lpOut,這時候通過一個printf語句來將當前物件的引用計數值打印出來,從前面的分析可以看出,如果一切正常的話,這裡打印出來的引用計數值為1。接著,我們又在兩個大括號裡面定義了另外一個智慧指標lpInner,它通過lpOut間接地指向了前面我們所建立的物件,這時候再次將當前物件的引用計數值打印出來,從前面 的分析也可以看出,如果一切正常的話,這裡打印出來的引用計數值應該為2。程式繼承往下執行,當出了大括號的範圍的時候,智慧指標物件lpInner就被析構了,從前面的分析可以知道,智慧指標在析構的時候,會減少當前物件的引用計數值,因此,最後一個printf語句打印出來的引用計數器值應該為1。當main函式執行完畢後,智慧指標lpOut也會被析構,被析構時,它會再次減少當前物件的引用計數,這時候,物件的引用計數值就為0了,於是,它就會被delete了。

        編譯指令碼檔案Android.mk的內容如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := lightpointer
LOCAL_SRC_FILES := lightpointer.cpp
LOCAL_SHARED_LIBRARIES := \
        libcutils \
        libutils
include $(BUILD_EXECUTABLE)
        最後,我們參照如何單獨編譯Android原始碼中的模組一文,使用mmm命令對工程進行編譯:
[email protected]:~/Android$ mmm ./external/lightpointer
        編譯之後,就可以打包了:
[email protected]:~/Android$ make snod
        最後得到可執行程式lightpointer就位於裝置上的/system/bin/目錄下。啟動模擬器,通過adb shell命令進入到模擬器終端,進入到/system/bin/目錄,執行lightpointer可執行程式,驗證程式是否按照我們設計的邏輯執行:
[email protected]:~/Android$ adb shell
[email protected]:/ # cd system/bin/        
[email protected]:/system/bin # ./lightpointer                                      
Construct LightClass Object.
Light Ref Count: 1.
Light Ref Count: 2.
Light Ref Count: 1.
Destory LightClass Object.

       這裡可以看出,程式一切都是按照我們的設計來執行,這也驗證了我們上面分析的輕量級智慧指標的實現原理。

       3. 強指標

       強指標所使用的引用計數類為RefBase,它LightRefBase類要複雜多了,所以才稱後者為輕量級的引用計數基類吧。我們先來看看RefBase類的實現,它定義在frameworks/base/include/utils/RefBase.h檔案中:

class RefBase
{
public:
	void            incStrong(const void* id) const;
	void            decStrong(const void* id) const;

	void            forceIncStrong(const void* id) const;

	//! DEBUGGING ONLY: Get current strong ref count.
	int32_t         getStrongCount() const;

	class weakref_type
	{
	public:
		RefBase*            refBase() const;

		void                incWeak(const void* id);
		void                decWeak(const void* id);

		bool                attemptIncStrong(const void* id);

		//! This is only safe if you have set OBJECT_LIFETIME_FOREVER.
		bool                attemptIncWeak(const void* id);

		//! DEBUGGING ONLY: Get current weak ref count.
		int32_t             getWeakCount() const;

		//! DEBUGGING ONLY: Print references held on object.
		void                printRefs() const;

		//! DEBUGGING ONLY: Enable tracking for this object.
		// enable -- enable/disable tracking
		// retain -- when tracking is enable, if true, then we save a stack trace
		//           for each reference and dereference; when retain == false, we
		//           match up references and dereferences and keep only the 
		//           outstanding ones.

		void                trackMe(bool enable, bool retain);
	};

	weakref_type*   createWeak(const void* id) const;

	weakref_type*   getWeakRefs() const;

	//! DEBUGGING ONLY: Print references held on object.
	inline  void            printRefs() const { getWeakRefs()->printRefs(); }

	//! DEBUGGING ONLY: Enable tracking of object.
	inline  void            trackMe(bool enable, bool retain)
	{
		getWeakRefs()->trackMe(enable, retain);
	}

protected:
	RefBase();
	virtual                 ~RefBase();

	//! Flags for extendObjectLifetime()
	enum {
		OBJECT_LIFETIME_WEAK    = 0x0001,
		OBJECT_LIFETIME_FOREVER = 0x0003
	};

	void            extendObjectLifetime(int32_t mode);

	//! Flags for onIncStrongAttempted()
	enum {
		FIRST_INC_STRONG = 0x0001
	};

	virtual void            onFirstRef();
	virtual void            onLastStrongRef(const void* id);
	virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);
	virtual void            onLastWeakRef(const void* id);

private:
	friend class weakref_type;
	class weakref_impl;

	RefBase(const RefBase& o);
	RefBase&        operator=(const RefBase& o);

	weakref_impl* const mRefs;
};
        RefBase類和LightRefBase類一樣,提供了incStrong和decStrong成員函式來操作它的引用計數器;而RefBase類與LightRefBase類最大的區別是,它不像LightRefBase類一樣直接提供一個整型值(mutable volatile int32_t mCount)來維護物件的引用計數,前面我們說過,複雜的引用計數技術同時支援強引用計數和弱引用計數,在RefBase類中,這兩種計數功能是通過其成員變數mRefs來提供的。

        RefBase類的成員變數mRefs的型別為weakref_impl指標,它實現在frameworks/base/libs/utils/RefBase.cpp檔案中:

class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
	volatile int32_t    mStrong;
	volatile int32_t    mWeak;
	RefBase* const      mBase;
	volatile int32_t    mFlags;


#if !DEBUG_REFS

	weakref_impl(RefBase* base)
		: mStrong(INITIAL_STRONG_VALUE)
		, mWeak(0)
		, mBase(base)
		, mFlags(0)
	{
	}

	void addStrongRef(const void* /*id*/) { }
	void removeStrongRef(const void* /*id*/) { }
	void addWeakRef(const void* /*id*/) { }
	void removeWeakRef(const void* /*id*/) { }
	void printRefs() const { }
	void trackMe(bool, bool) { }

#else
	weakref_impl(RefBase* base)
		: mStrong(INITIAL_STRONG_VALUE)
		, mWeak(0)
		, mBase(base)
		, mFlags(0)
		, mStrongRefs(NULL)
		, mWeakRefs(NULL)
		, mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
		, mRetain(false)
	{
		//LOGI("NEW weakref_impl %p for RefBase %p", this, base);
	}

	~weakref_impl()
	{
		LOG_ALWAYS_FATAL_IF(!mRetain && mStrongRefs != NULL, "Strong references remain!");
		LOG_ALWAYS_FATAL_IF(!mRetain && mWeakRefs != NULL, "Weak references remain!");
	}

	void addStrongRef(const void* id)
	{
		addRef(&mStrongRefs, id, mStrong);
	}

	void removeStrongRef(const void* id)
	{
		if (!mRetain)
			removeRef(&mStrongRefs, id);
		else
			addRef(&mStrongRefs, id, -mStrong);
	}

	void addWeakRef(const void* id)
	{
		addRef(&mWeakRefs, id, mWeak);
	}
	void removeWeakRef(const void* id)
	{
		if (!mRetain)
			removeRef(&mWeakRefs, id);
		else
			addRef(&mWeakRefs, id, -mWeak);
	}

	void trackMe(bool track, bool retain)
	{
		mTrackEnabled = track;
		mRetain = retain;
	}

	......

private:
	struct ref_entry
	{
		ref_entry* next;
		const void* id;
#if DEBUG_REFS_CALLSTACK_ENABLED
		CallStack stack;
#endif
		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;
			// Reference count at the time of the snapshot, but before the
			// update.  Positive value means we increment, negative--we
			// decrement the reference count.
			ref->ref = mRef;
			ref->id = id;
#if DEBUG_REFS_CALLSTACK_ENABLED
			ref->stack.update(2);
#endif

			ref->next = *refs;
			*refs = ref;
		}
	}

	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;
			}

			LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p (weakref_type %p) that doesn't exist!",
				id, mBase, this);
		}
	}

	......

	Mutex mMutex;
	ref_entry* mStrongRefs;
	ref_entry* mWeakRefs;

	bool mTrackEnabled;
	// Collect stack traces on addref and removeref, instead of deleting the stack references
	// on removeref that match the address ones.
	bool mRetain;

	......
#endif
};
        這個類看起來實現得很複雜,其實不然,這個類的實現可以分成兩部分:
#if !DEBUG_REFS

......

#else
        編譯指令之間的這部分原始碼是Release版本的原始碼,它的成員函式都是空實現;
#else 

......

#endif
        編譯指令之間的部分原始碼是Debug版本的原始碼,它的成員函式都是有實現的,實現這些函式的目的都是為了方便開發人員除錯引用計數用的,除此之外,還在內部實現了一個結構體ref_entry:
struct ref_entry
{
	ref_entry* next;
	const void* id;
#if DEBUG_REFS_CALLSTACK_ENABLED
	CallStack stack;
#endif
	int32_t ref;
};
        這個結構體也是為了方便除錯而使用的,我們可以不關注這部分用於除錯的程式碼。

        總的來說,weakref_impl類只要提供了以下四個成員變數來維護物件的引用計數:

volatile int32_t    mStrong;
volatile int32_t    mWeak;
RefBase* const      mBase;
volatile int32_t    mFlags;
        其中mStrong和mWeak分別表示物件的強引用計數和弱引用計數;RefBase類包含了一個weakref_impl類指標mRefs,而這裡的weakref_impl類也有一個成員變數mBase來指向它的宿主類RefBase;mFlags是一個標誌位,它指示了維護物件引用計數所使用的策略,後面我們將會分析到,它的取值為0,或者以下的列舉值:
//! Flags for extendObjectLifetime()
    enum {
        OBJECT_LIFETIME_WEAK    = 0x0001,
        OBJECT_LIFETIME_FOREVER = 0x0003
    };
        這裡我們還需要注意的一點的是,從weakref_impl的類名來看,它應該是一個實現類,那麼,就必然有一個對應的介面類,這個對應的介面類的就是RefBase類內部定義的weakref_type類了,這是一種把類的實現與介面定義分離的設計方法。學習過設計模式的讀者應該知道,在設計模式裡面,非常強調類的介面定義和類的實現分離,以便利於後續擴充套件和維護,這裡就是用到了這種設計思想。

        說了這多,RefBase類給人的感覺還是挺複雜的,不要緊,我們一步步來,先通過下面這個圖來梳理一下這些類之間的關係:


        從這個類圖可以看出,每一個RefBase物件包含了一個weakref_impl物件,而weakref_impl物件實現了weakref_type介面,同時它可以包含多個ref_entry物件,前面說過,ref_entry是除錯用的一個結構體,實際使用中可以不關注。

        提供引用計數器的類RefBase我們就暫時介紹到這裡,後面我們再結合智慧指標類一起分析,現在先來看看強指標類和弱指標類的定義。強指標類的定義我們在前面介紹輕量級指標的時候已經見到了,就是sp類了,這裡就不再把它的程式碼列出來了。我們來看看它的建構函式的實現:

template<typename T>
sp<T>::sp(T* other)
    : m_ptr(other)
{
    if (other) other->incStrong(this);
}

        這裡傳進來的引數other一定是繼承於RefBase類的,因此,在函式的內部,它呼叫的是RefBase類的incStrong函式,它定義在frameworks/base/libs/utils/RefBase.cpp檔案中:

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->addWeakRef(id);
    refs->incWeak(id);
    refs->addStrongRef(id); 

    const int32_t c = android_atomic_inc(&refs->mStrong); 
    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; 
    } 

    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); 
    const_cast<RefBase*>(this)->onFirstRef();
}
        成員變數mRefs是在RefBase類的建構函式中建立的:
RefBase::RefBase()
    : mRefs(new weakref_impl(this))
{
//    LOGV("Creating refs %p with RefBase %p\n", mRefs, this);
}
        在這個incStrong函式中,主要做了三件事情:

        一是增加弱引用計數:

refs->addWeakRef(id);
refs->incWeak(id);
        二是增加強引用計數:
refs->addStrongRef(id);
const int32_t c = android_atomic_inc(&refs->mStrong);
        三是如果發現是首次呼叫這個物件的incStrong函式,就會呼叫一個這個物件的onFirstRef函式,讓物件有機會在物件被首次引用時做一些處理邏輯:
if (c != INITIAL_STRONG_VALUE)  {
    return;
}

android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
const_cast<RefBase*>(this)->onFirstRef();
       這裡的c返回的是refs->mStrong加1前的值,如果發現等於INITIAL_STRONG_VALUE,就說明這個物件的強引用計數是第一次被增加,因此,refs->mStrong就是初始化為INITIAL_STRONG_VALUE的,它的值為:
#define INITIAL_STRONG_VALUE (1<<28)
        這個值加1後等於1<<28 + 1,不等於1,因此,後面要再減去-INITIAL_STRONG_VALUE,於是,refs->mStrong就等於1了,就表示當前物件的強引用計數值為1了,這與這個物件是第一次被增加強引用計數值的邏輯是一致的。

        回過頭來看弱引用計數是如何增加的,首先是呼叫weakref_impl類的addWeakRef函式,我們知道,在Release版本中,這個函式也不做,而在Debug版本中,這個函式增加了一個ref_entry物件到了weakref_impl物件的mWeakRefs列表中,表示此weakref_impl物件的弱引用計數被增加了一次。接著又呼叫了weakref_impl類的incWeak函式,真正增加弱引用計數值就是在這個函式實現的了,weakref_impl類的incWeak函式繼承於其父類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);
    LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
       增加弱引用計數是下面語句執行的:
const int32_t c = android_atomic_inc(&impl->mWeak);
        但是前面為什麼又呼叫了一次addWeakRef函式呢?前面不是已經呼叫過了嗎?在Release版本中,因為weakref_impl類的addWeakRef函式是空實現,這裡再呼叫一次沒有什麼害處,但是如果在Debug版本,豈不是冗餘了嗎?搞不清,有人問過負責開發Android系統Binder通訊機制模組的作者Dianne Hackborn這個問題,他是這樣回答的:

        Ah I see.  Well the debug code may be broken, though I wouldn't leap to that 
        conclusion without actually testing it; I know it has been used in the 
        past.  Anyway, these things get compiled out in non-debug builds, so there 
        is no reason to change them unless you are actually trying to use this debug 
        code and it isn't working and need to do this to fix it. 

        既然他也不知道怎麼回事,我們也不必深究了,知道有這麼回事就行。

        這裡總結一下強指標類sp在其建構函式裡面所做的事情就是分別為目標物件的強引用計數和弱引和計數增加了1。

        再來看看強指標類的解構函式的實現:

template<typename T>
sp<T>::~sp()
{
    if (m_ptr) m_ptr->decStrong(this);
}
       同樣,這裡的m_ptr指向的目標物件一定是繼承了RefBase類的,因此,這裡呼叫的是RefBase類的decStrong函式,這也是定義在frameworks/base/libs/utils/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 PRINT_REFS
    LOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
    LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
    if (c == 1) {
        const_cast<RefBase*>(this)->onLastStrongRef(id);
        if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
            delete this;
        }
    }
    refs->removeWeakRef(id);
    refs->decWeak(id);
}
        這裡的refs->removeStrongRef函式呼叫語句是對應前面在RefBase::incStrong函式裡的refs->addStrongRef函式呼叫語句的,在Release版本中,這也是一個空實現函式,真正實現強引用計數減1的操作是下面語句:
const int32_t c = android_atomic_dec(&refs->mStrong);
        如果發現減1前,此物件的強引用計數為1,就說明從此以後,就再沒有地方引用這個目標物件了,這時候,就要看看是否要delete這個目標物件了:
if (c == 1) {
    const_cast<RefBase*>(this)->onLastStrongRef(id);
    if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
        delete this;
    }
}
        在強引用計數為0的情況下,如果物件的標誌位OBJECT_LIFETIME_WEAK被設定了,就說明這個物件的生命週期是受弱引用計數所控制的,因此,這時候就不能delete物件,要等到弱引用計數也為0的情況下,才能delete這個物件。

        接下來的ref->removeWeakRef函式呼叫語句是對應前面在RefBase::incStrong函式裡的refs->addWeakRef函式呼叫語句的,在Release版本中,這也是一個空實現函式,真正實現強引用計數減1的操作下面的refs->decWeak函式,weakref_impl類沒有實現自己的decWeak函式,它繼承了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);
    LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
    if (c != 1) return;

    if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
        if (impl->mStrong == INITIAL_STRONG_VALUE)
            delete impl->mBase;
        else {
//            LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
            delete impl;
        }
    } else {
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
            delete impl->mBase;
        }
    }
}
       這裡又一次呼叫了weakref_impl物件的removeWeakRef函式,這也是和RefBase::weakref_type::incWeak函式裡面的impl->addWeakRef語句所對應的,實現弱引用計數減1的操作是下面語句:
const int32_t c = android_atomic_dec(&impl->mWeak);
       減1前如果發現不等於1,那麼就什麼也不用做就返回了,如果發現等於1,就說明當前物件的弱引用計數值為0了,這時候,就要看看是否要delete這個物件了:
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
    if (impl->mStrong == INITIAL_STRONG_VALUE)
        delete impl->mBase;
    else {
//      LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
        delete impl;
    }
} else {
    impl->mBase->onLastWeakRef(id);
    if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
        delete impl->mBase;
    }
}
        如果目標物件的生命週期是不受弱引用計數控制的,就執行下面語句:
if (impl->mStrong == INITIAL_STRONG_VALUE)
    delete impl->mBase;
else {
//  LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
    delete impl;
}
        這個程式碼段是什麼意思呢?這裡是減少物件的弱引用計數的地方,如果呼叫到這裡,那麼就說明前面一定有增加過此物件的弱引用計數,而增加物件的弱引用計數有兩種場景的,一種場景是增加物件的強引用計數的時候,會同時增加物件的弱引用計數,另一種場景是當我們使用一個弱指標來指向物件時,在弱指標物件的建構函式裡面,也會增加物件的弱引用計數,不過這時候,就只是增加物件的弱引用計數了,並沒有同時增加物件的強引用計數。因此,這裡在減少物件的弱引用計數時,就要分兩種情況來考慮。

        如果是前一種場景,這裡的impl->mStrong就必然等於0,而不會等於INITIAL_STRONG_VALUE值,因此,這裡就不需要delete目標物件了(impl->mBase),因為前面的RefBase::decStrong函式會負責delete這個物件。這裡唯一需要做的就是把weakref_impl物件delete掉,但是,為什麼要在這裡delete這個weakref_impl物件呢?這裡的weakref_impl物件是在RefBase的建構函式裡面new出來的,理論上說應該在在RefBase的解構函式裡delete掉這個weakref_impl物件的。在RefBase的解構函式裡面,的確是會做這件事情:

RefBase::~RefBase()
{
//    LOGV("Destroying RefBase %p (refs %p)\n", this, mRefs);
    if (mRefs->mWeak == 0) {
//        LOGV("Freeing refs %p of old RefBase %p\n", mRefs, this);
        delete mRefs;
    }
}

        但是不要忘記,在這個場景下,目標物件是前面的RefBase::decStrong函式delete掉的,這時候目標物件就會被析構,但是它的弱引用計數值尚未執行減1操作,因此,這裡的mRefs->mWeak == 0條件就不成立,於是就不會delete這個weakref_impl物件,因此,就延遲到執行這裡decWeak函式時再執行。

        如果是後一種情景,這裡的impl->mStrong值就等於INITIAL_STRONG_VALUE了,這時候由於沒有地方會負責delete目標物件,因此,就需要把目標物件(imp->mBase)delete掉了,否則就會造成記憶體洩漏。在delete這個目標物件的時候,就會執行RefBase類的解構函式,這時候目標物件的弱引用計數等於0,於是,就會把weakref_impl物件也一起delete掉了。

        回到外層的if語句中,如果目標物件的生命週期是受弱引用計數控制的,就執行下面語句:

impl->mBase->onLastWeakRef(id);
if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
    delete impl->mBase;
}
        理論上說,如果目標物件的生命週期是受弱引用計數控制的,那麼當強引用計數和弱引用計數都為0的時候,這時候就應該delete目標物件了,但是這裡還有另外一層控制,我們可以設定目標物件的標誌值為OBJECT_LIFETIME_FOREVER,即目標物件的生命週期完全不受強引用計數和弱引用計數控制,在這種情況下,即使目標物件的強引用計數和弱引用計數都同時為0,這裡也不能delete這個目標物件,那麼,由誰來delete掉呢?當然是誰new出來的,就誰來delete掉了,這時候智慧指標就完全退化為普通指標了,這裡的智慧指標設計的非常強大。

        分析到這裡,有必要小結一下:

        A. 如果物件的標誌位被設定為0,那麼只要發現物件的強引用計數值為0,那就會自動delete掉這個物件;

        B. 如果物件的標誌位被設定為OBJECT_LIFETIME_WEAK,那麼只有當物件的強引用計數和弱引用計數都為0的時候,才會自動delete掉這個物件;

        C. 如果物件的標誌位被設定為OBJECT_LIFETIME_FOREVER,那麼物件就永遠不會自動被delete掉,誰new出來的物件誰來delete掉。