Android中提供了三種智能指針:
① 輕量級指針(sp + LightRefBase):簡單的引用計數,只使用強引用計數。
② 強引用指針(sp + RefBase):使用強引用計數。
③ 弱引用指針(wp + RefBase):使用弱引用計數。android
引用計數的最簡單的思路是:使用一個計數器來記錄一個對象被引用的次數。每當有引用指向對象時,計數器加1;每當一個指向對象的引用斷開時,計數器減1。當計數器減爲0時,就自動釋放該對象的內存空間。函數
首先,每一個對象都須要關聯一個計數器,簡單的實現方案是將計數器定義在基類中,這樣全部的類繼承該基類的類都有了計數器。this
看看在android中是怎麼定義這個基類的:atom
// frameworks/base/include/utils/RefBase.h template <class T> class LightRefBase { public: inline LightRefBase() : mCount(0) { } // 計數器加1 inline void incStrong(const void* id) const { android_atomic_inc(&mCount); } // 計數器減1 inline void decStrong(const void* id) const { if (android_atomic_dec(&mCount) == 1) { // 當計數器減到0時,析構本身 delete static_cast<const T*>(this); } } protected: inline ~LightRefBase() { } private: mutable volatile int32_t mCount; // 32位的計數器 }
這個類封裝了計數器,提供了對計數器的操做接口(incStrong
和decStrong
)。其中android_atomic_inc()
和android_atomic_dec()
都是原子操做,返回的是加1或減1以前的計數值。debug
那誰去操做計數器加1仍是減1呢?sp類就是幹這個事的。設計
// frameworks/base/include/utils/StrongPointer.h template <typename T> class sp { public: sp(T* other); ~sp(); sp& operator = (T* other); sp& operator = (const sp<T>& other); inline T& operator* () const { return *m_ptr; } inline T* operator-> () const { return m_ptr; } ... private: T* m_ptr; }
能夠看到,sp類包含m_ptr指針成員,並重載了*運算符和->運算符,是一個智能指針。因爲它只用於強引用計數,所以這裏的sp的實際含義是強指針(Strong Pointer)。指針
下面咱們看下,sp是如何操做計數器加一、減1和釋放被引用對象的。code
template<typename T> sp<T>::sp(T* other) : m_ptr(other) { // 在sp構造函數中,對指向的對象的引用計數加1 if (other) other->incStrong(this); }
template<typename T> sp<T>::~sp() { // 在sp析構函數中,對指向的對象的引用計數減1 // 析構對象的邏輯在decStrong()裏 if (m_ptr) m_ptr->decStrong(this); }
有一個要注意的點就是,sp在被建立後,是能夠指向另外一個對象的。以下圖所示:對象
所以要重載賦值操做符blog
template<typename T> sp<T>& sp<T>::operator = (T* other) { // 對新引用的對象的計數器加1 if (other) other->incStrong(this); // 因爲再也不引用原來的對象,則原有對象的計數器減1 if (m_ptr) m_ptr->decStrong(this); m_ptr = other; return *this; }
上述引用計數的方案雖然實現簡單,但存在一個很大的問題,就是循環引用。以下圖:
因爲A和B相互引用對方,致使彼此的計數器值都是1,沒法被釋放,但A和B都沒有被其餘對象引用。
爲了解決循環引用,android中提供了弱引用指針。
基本思想是:
① 循環引用時,選擇一強一弱。
② 當強引用計數減爲0時,無論弱引用計數是否爲0,都釋放對象。
③ 不能使用弱引用指針直接操做對象的函數。(避免對象被釋放後,弱引用還持有對象的引用,這時操做對象就是錯誤的)
④ 弱引用指針要想操做對象,必須先提高爲強引用指針,若是提高成功,才能對對象進行訪問。(強引用指針重載了*和->運算符,而弱引用指針則沒有重載)
在這種方案中,每一個對象須要有兩個計數器,一個是強引用計數器,一個是弱引用計數器。看看android中的實現:
// frameworks/base/include/utils/RefBase.h class RefBase { public: // 強引用計數器加1 void incStrong(const void* id) const; // 強引用計數器減1 void decStrong(const void* id) const; weakref_type* createWeak(const void* id) const; class weakref_type { // 弱引用計數器加1 void incWeak(const void* id); // 弱引用計數器減1 void decWeak(const void* id); } protected: RefBase(); virtual ~RefBase(); private: weakref_impl* const mRefs; }
// frameworks/base/include/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; // 回收對象的標識 weakref_impl(RefBase* base) // FIRST_INC_STRONG = 0x0001 用來表示是否是第一次 : mStrong(INITIAL_STRONG_VALUE) , mWeak(0) , mBase(base) , mFlags(0) {} }
這幾個類之間的關係以下圖:
RefBase類和weakref_impl類是雙向關聯關係:
sp類前面已經提到了,當構造sp對象會調用其指向對象的incStrong()
函數,而析構sp對象時,會調用其指向對象的decStrong()
函數。 輕量級指針是使用sp + LightRefBase。而強引用指針則是使用sp + RefBase。在這種方式下,則會調用RefBase的incStrong()
和decStrong()
。
// frameworks/base/include/utils/RefBase.cpp void RefBase::incStrong(const void* id) const { weakref_impl* const refs = mRefs; refs->incWeak(id); // 弱引用計數加1 // 強引用計數加1 const int32_t c = android_atomic_inc(&refs->mStrong); if (c != INITIAL_STRONG_VALUE) { // 若是不是第一次增長計數值 return; } // 因爲mStrong的初始值是INITIAL_STRONG_VALUE, // 所以須要減去INITIAL_STRONG_VALUE計數值才能正確 android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); refs->mBase->onFirstRef(); // 第一次增長計數值會觸發這個調用 }
void RefBase::decStrong(const void* id) const { weakref_impl* const refs = mRefs; // 強引用計數減1 const int32_t c = android_atomic_dec(&refs->mStrong); if (c == 1) { // 若是強引用計數減爲0 refs->mBase->onLastStrongRef(id); // 默認模式是OBJECT_LIFETIME_STRONG if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { // 若是是OBJECT_LIFETIME_STRONG模式,則析構該對象 delete this; } } // 弱引用計數減1 refs->decWeak(id); }
上面代碼能夠看出,在RefBase對象被引用時,其強引用計數器和弱引用計數器都加1;當sp析構時,將所引用的RefBase對象的強引用計數器和弱引用計數器都減1。當強引用計數減到0時,會析構RefBase對象(OBJECT_LIFETIME_STRONG模式)。
使用sp + RefBase的這種方式,強引用計數值等於弱引用計數值。
RefBase對象的銷燬邏輯咱們清楚了,可是還有一個問題,weakref_impl對象是何時建立和析構的呢?
RefBase::RefBase() : mRefs(new weakref_impl(this)) // RefBase的構造函數中建立了weakref_impl { }
RefBase::~RefBase() { if (mRefs->mStrong == INITIAL_STRONG_VALUE) { // 沒有使用過任何強引用指向過它,則直接析構weakref_impl delete mRefs; } else { // 目前咱們討論的強引用指針不會走進下面的if語句裏 if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) { if (mRefs->mWeak == 0) { delete mRefs; } } } // for debugging purposes, clear this. const_cast<weakref_impl*&>(mRefs) = NULL; }
咱們發現,在有使用過強引用指向RefBase對象後,當強引用計數減爲0時,RefBase對象被析構,但它的析構函數中沒有看到析構weakref_impl對象。那麼weakref_impl對象是在哪裏被析構的呢?答案在RefBase::weakref_type::decWeak()
函數中。
void RefBase::weakref_type::decWeak(const void* id) { weakref_impl* const impl = static_cast<weakref_impl*>(this); // 弱引用計數減1 const int32_t c = android_atomic_dec(&impl->mWeak); 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 { ... } }
總結一下weakref_type對象被析構的時機和位置:
① 若是RefBase對象從未被sp對象引用,則在RefBase類的析構函數中析構weakref_type對象。
② 若是RefBase對象被sp對象引用過,則在RefBase對象的弱引用計數器減爲0時,weakref_type對象本身析構本身。
上面的幾種方式,都是隻有強指針的狀況。若是隻有弱指針或強弱指針都有的狀況呢?
咱們先來看看弱指針的定義:
// frameworks/base/include/utils/RefBase.h template <typename T> class wp { public: inline wp() : m_ptr(0) { } wp(T* other); wp(const wp<T>& other); ~wp(); wp& operator = (T* other); wp& operator = (const wp<T>& other); // 提高爲強指針 sp<T> promote() const; private: T* m_ptr; weakref_type* m_refs; }
下圖給出了 wp、RefBase和weakref_type,這三個類之間的關係:
template<typename T> wp<T>::wp(T* other) : m_ptr(other) { // 將wp所引用對象的弱引用計數加1 if (other) m_refs = other->createWeak(this); } RefBase::weakref_type* RefBase::createWeak(const void* id) const { // 將弱引用計數加1 mRefs->incWeak(id); return mRefs; // 返回構造函數時建立的weakref_impl對象 } template<typename T> wp<T>::~wp() { // 析構wp時,將所引用對象的弱引用計數減1 if (m_ptr) m_refs->decWeak(this); }
由上面代碼可知,使用wp + RefBase,只會增長或減小對象的弱引用計數。
若是隻有弱引用,RefBase何時被析構?weakref_impl對象何時被析構? 這裏須要先說明mFlags的兩種值:
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; if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { if (impl->mStrong == INITIAL_STRONG_VALUE) { // 說明對象沒有被強引用引用過,只有弱引用引用過它 // 這種狀況下,弱引用計數減爲0時,析構RefBase對象 delete impl->mBase; } else { delete impl; } } else { if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) { // 對象受弱引用控制,當弱引用計數爲0時,析構RefBase對象 delete impl->mBase; } } }
而在RefBase對象的析構函數中,在沒有被強引用指針引用過的狀況下,會析構weakref_impl對象。
RefBase::~RefBase() { if (mRefs->mStrong == INITIAL_STRONG_VALUE) { // 沒有使用過任何強引用指向過它,則直接析構weakref_impl delete mRefs; } else { ... } }
每建立一個sp指向RefBase對象,對象的強引用計數會加1,弱引用計數也會加1。
每建立一個wp指向RefBase對象,只會對對象的弱引用計數加1。
由此可知,當同時使用了強引用指針和弱引用指針,弱引用計數的值 > 強引用計數的值。
而析構RefBase對象和weakref_impl對象的時機和位置分兩種狀況:
1. 對象回收受強引用控制(mFlags 爲 OBJECT_LIFETIME_STRONG)
void RefBase::decStrong(const void* id) const { weakref_impl* const refs = mRefs; const int32_t c = android_atomic_dec(&refs->mStrong); if (c == 1) { refs->mBase->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { // 對象回收受強引用控制 // 當強引用計數減爲0時,無論此時弱引用計數是否爲0,都析構RefBase對象 delete this; } } refs->decWeak(id); // 弱引用計數減1 }
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); // 若是強引用計數已經減爲0,RefBase被析構,但弱引用計數此時>0, // 這時decWeak()並不析構weakref_impl對象 // 即weakref_impl對象能夠比RefBase對象生命週期長 if (c != 1) return; // 當已有的wp對象析構後,最終會將弱引用計數減爲0 if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { if (impl->mStrong == INITIAL_STRONG_VALUE) { // 不會走這裏,這裏是只使用弱引用指針的狀況 delete impl->mBase; } else { // 對象回收受強引用控制 // 弱引用計數減爲0,則析構weakref_impl對象 delete impl; } } else { ... } }
2. 對象回收受弱引用控制(mFlags 爲 OBJECT_LIFETIME_WEAK)
void RefBase::decStrong(const void* id) const { weakref_impl* const refs = mRefs; // 就算強引用計數減爲0,也不會析構RefBase對象 const int32_t c = android_atomic_dec(&refs->mStrong); ... refs->decWeak(id); // 弱引用計數減1 }
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; // 當已有的wp對象析構後,最終會將弱引用計數減爲0 if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { ... } else { // 對象回收受弱引用控制 // 當弱引用計數減爲0時,纔去析構RefBase對象,調用RefBase的析構函數 delete impl->mBase; } }
RefBase::~RefBase() { if (mRefs->mStrong == INITIAL_STRONG_VALUE) { ... } else { if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) { if (mRefs->mWeak == 0) { // 在RefBase的析構函數中,纔去析構weakref_impl對象 delete mRefs; } } } }