Android中的智能指針

Android中提供了三種智能指針:
① 輕量級指針(sp + LightRefBase):簡單的引用計數,只使用強引用計數。
② 強引用指針(sp + RefBase):使用強引用計數。
③ 弱引用指針(wp + RefBase):使用弱引用計數。android

1、輕量級指針

引用計數的最簡單的思路是:使用一個計數器來記錄一個對象被引用的次數。每當有引用指向對象時,計數器加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位的計數器
}

這個類封裝了計數器,提供了對計數器的操做接口(incStrongdecStrong)。其中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;
}

2、弱引用計數的引入

上述引用計數的方案雖然實現簡單,但存在一個很大的問題,就是循環引用。以下圖:

循環引用

因爲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類是雙向關聯關係:

  • RefBase對象包含weakref_impl對象,計數器在weakref_impl對象中。
  • weakref_impl對象包含RefBase對象,在弱引用計數方式下,當弱引用計數減爲0時,須要釋放RefBase對象。

3、強引用指針

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對象本身析構本身。

4、弱引用指針

上面的幾種方式,都是隻有強指針的狀況。若是隻有弱指針或強弱指針都有的狀況呢?

咱們先來看看弱指針的定義:

// 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的兩種值:

  • OBJECT_LIFETIME_STRONG:對象只受強引用控制,一旦強引用計數爲0,就能夠回收對象。
  • OBJECT_LIFETIME_WEAK:對象受弱引用控制,強引用計數爲0,而弱引用計數不爲0的時候,實際對象不會被回收;只有當強引用計數和弱引用計數同時爲0時,實際對象纔會被回收。
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 {
        ...
    }
}

5、既使用了強引用指針,又使用了弱引用指針

每建立一個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;
            }
        }
    }
}

參考

  • 《Android 系統源代碼情景分析》
  • 《深刻理解Android內核設計思想 第2版》
相關文章
相關標籤/搜索