Android智能指針sp wp詳解

 

轉自:http://www.linuxidc.com/Linux/2011-03/33674.htm

研究android的時候,經常會遇到sp、wp的東西,網上一搜,原來是android封裝了c++中對象回收機制。
說明:
1. 如果一個類想使用智能指針,那麼必須滿足下面兩個條件:
    a. 該類是虛基類RefBase的子類或間接子類
    b. 該類必須定義虛構造函數。如virtual ~MyClass();


2. 本文以類BBinder來進行說明,其餘類使用sp或wp的情況類似
3. 代碼路徑:frameworks/base/libs/utils/RefBase.cpp
       frameworks/base/include/utils/RefBase.h

一、calss BBinder類說明
      class RefBase
      class IBinder
 class BpBinder   class BBinder
 class BBinder : public IBinder
 {
 ...
 protected:
     virtual             ~BBinder();
 ...
 }
 class IBinder : public virtual RefBase
 {
 ...
 protected:
     inline virtual      ~IBinder() { }
 ...
 }
 由上,可以看出BBinder和IBinder都是以public的方式繼承於虛基類RefBase的。

二、sp wp對象的建立過程
 解析:sp<BBinder>  BB_ptr(new BBinder);
 這是一條定義sp指針BB_ptr的語句,他只想的對象是一個BBinder對象。
 如圖所示。
 


 1》首先看一下new BBinder時都做了什麼,特別是和該機制相關的初始化。
   c++中創建一個對象時,需要調用去構造函數,對於繼承類,則是先調用其父類的構造函數,然後纔會調用本身的
   構造函數。這裏new一個BBinder對象時,順序調用了:
    RefBase::RefBase() : mRefs(new weakref_impl(this)) {}
    inline   IBinder() {}
    BBinder::BBinder() : mExtras(NULL){}
   主要關注的是RefBase的構造函數,
   可以看出他是通過new weakref_impl(this)的結果來初始化私有成員mRefs
   這裏的this指向BBinder對象自身,class weakref_impl繼承於類RefBase的內嵌類weakref_type,然後該類
   weakref_impl又被類RefBase引用。類weakref_impl的構造函數如下:
   weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)    // 1 << 28
        , mWeak(0)
        , mBase(base)             // new BBinder指針
        , mFlags(0)
        , mStrongRefs(NULL)          // sp引用鏈表指針
        , mWeakRefs(NULL)           // wp引用鏈表指針
        , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) // 1
        , mRetain(false) {}
  
 2》new BBinder返回的是BBinder對象的指針,如:sp<BBinder>  BB_ptr(0x????????);
   sp實際上是一個類模板,這條語句最終是要建立一個sp的實例化對象,叫模板類BB_ptr
   這裏生成BB_ptr對象所調用的構造函數是:
   template<typename T>
   sp<T>::sp(T* other)
       : m_ptr(other)
   {
       if (other) other->incStrong(this);
   }
   BB_ptr對象的私有指針指向剛剛前面生成的BBinder對象。
   接着調用函數incStrong(),該函數是RefBase類的成員函數,在子類中沒有被重載,所以這裏
   other->incStrong(this)的調用實際上是調用基類成員函數incStrong(this),這個this值是指向sp對象
   BB_ptr的指針。現在轉去查看該成員函數的實現。
   
   void RefBase::incStrong(const void* id) const
   {
       weakref_impl* const refs = mRefs;
       /* 取得BBinder對象基類中的私有隻讀指針mRefs */
       refs->addWeakRef(id);
       /* 調用weakref_impl類定義時實現的成員函數addWeakRef, 見下注釋1*/
       refs->incWeak(id);
       /* 調用weakref_impl類的基類weakref_type成員函數incWeak, 見下注釋2*/
      
       refs->addStrongRef(id);
       // 調用weakref_impl類定義時實現的成員函數addStrongRef, 見下注釋1
       const int32_t c = android_atomic_inc(&refs->mStrong);
     /* 該函數實際將refs->mStrong值加1,也就是增加強引用計數值。但是返回值爲refs->mStrong-1 */
       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;
       }
     /* c = INITIAL_STRONG_VALUE, 第一個強引用產生的時候纔會出現這個情況 */
       android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
     /* 返回值爲INITIAL_STRONG_VALUE,refs->mStrong值變成1 */
       const_cast<RefBase*>(this)->onFirstRef();
   }
   
/************************註釋1********************************/
void addWeakRef(const void* id)
{
    addRef(&mWeakRefs, id, mWeak);
}
void addStrongRef(const void* id)
{
    addRef(&mStrongRefs, id, mStrong);
}
addRef()是類weakref_impl的私有成員函數,addWeakRef()函數引用的是public成員變量,而addRef()函數可以操作私有數據。

    struct ref_entry
    {
        ref_entry* next;
        const void* id;
        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;
            ref->ref = mRef;
            ref->id = id;
           
            ref->next = *refs;
            *refs = ref;
   /*
   新出現的ref_entry結構體加入到鏈表頭上,如果有n個sp指針指向同一個目標對象
   那麼這裏就有n個ref_entry結構體加入到這個單鏈表中,該結構體記錄着如下數據
   1. id域記錄着對應的sp強指針類對象的this值
   2. ref域記錄的是當前sp強指針類對象是第幾個引用目標對象的指針
   3. next域指向下一個指向目標對象的sp強指針對應的ref_entry結構體
   
   類RefBase的嵌套類weakref_type的子類的私有數據mRefs的私有二級指針成員mWeakRefs指向的是
   最後一個sp強指針對應的ref_entry結構體指針。

   總結一下:
   一個目標對象,可能被n個sp強指針指向,那麼就存在n個class sp對象,同時每一個sp
   對象在目標對象的虛基類對象的成員類mRefs的私有二級指針成員mWeakRefs登記了一個
   ref_entry結構體,這些ref_entry結構體的地址都是由該鏈表管理,每一個
   ref_entry結構體和哪一個sp對象對應,也由該鏈表管理。同時鏈接數就是該鏈表節點的
   個數
   */
        }
    }
/************************註釋1********************************/

/************************註釋2********************************/
void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    // 強制類型轉換,將基類指針轉換成子類指針
    impl->addWeakRef(id); 
    // 調用類weakref_impl成員函數addWeakRef(),產生一個ref_entry結構體掛載mWeakRefs鏈表上
    const int32_t c = android_atomic_inc(&impl->mWeak);
  /* impl->mWeak加1,表示已存在一個weak引用。但返回值c爲操作前的結果 */
    LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
/************************註釋2********************************/

 3》上面是定義一個sp指針,下面看看定義一個wp指針式如何實現的。
     wp<BBinder>  BB_wp_ptr(BB_ptr);
    下面是wp類對應上面定義類型的構造函數
   template<typename T>
   wp<T>::wp(const sp<T>& other)
       : m_ptr(other.m_ptr)
   {
       if (m_ptr) {
           m_refs = m_ptr->createWeak(this);
       }
   }
   this指針是指向wp對象的。createWeak()函數是RefBase類的成員函數。
   RefBase::weakref_type* RefBase::createWeak(const void* id) const
   {
       mRefs->incWeak(id);
       return mRefs;
   }
   mRefs指向的是第二步驟中產生的weakref_impl對象,調用基類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);
     /* impl->mWeak有加1,但返回值爲操作前的結果 */
       LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
   }
   


三、sp、wp釋放過程
  sp<BBinder> BB_SP_ptr(BB_ptr);
  實際上BB_SP_ptr和前面的BB_ptr一樣,指向的是同一個BBinder對象。另外需要注意的時,調用sp構造函數:
  template<typename T>
  sp<T>::sp(const sp<T>& other)
      : m_ptr(other.m_ptr)
  {
      if (m_ptr) m_ptr->incStrong(this);
  }
  同樣是需要調用BBinder對象的incStrong()函數,使用weakref_impl對象來管理新添加進來的強引用,同時增加一個
  ref_entry結構體到weakref_impl對象的mStrongRefs,增加2個ref_entry結構體到weakref_impl對象的mWeakRefs。
  如上圖所示。
  
  現在來看看釋放sp、wp指針的情況。
  delete BB_SP_ptr;
  將會調用如下形式的sp析構函數:
  template<typename T>
  sp<T>::~sp()
  {
      if (m_ptr) m_ptr->decStrong(this);
  }
  m_ptr指向的是前面生成的BBinder對象,調用其基類函數decStrong(this),this值是指向BB_SP_ptr對象。
  
  void RefBase::decStrong(const void* id) const
  {
      weakref_impl* const refs = mRefs;
      refs->removeStrongRef(id); // 註釋3,移除mStrongRefs鏈表中和該sp對應的ref_entry結構體
      const int32_t c = android_atomic_dec(&refs->mStrong);
    /* 強引用計數減1, 但返回的是操作之前的引用計數值 */
      LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
      if (c == 1) {
       /*  c == 1說明剛剛removeStrongRef之前,整個系統中只存在一個sp對象引用目標對象,現在的情況就是
          系統中沒有任何強指針對象來引用目標對象了,此時目標對象就會被刪除釋放
        */
          const_cast<RefBase*>(this)->onLastStrongRef(id);
          if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
              delete this; // mFlags =0 ,條件成立,刪除目標對象,這裏就會刪除前面new出來的BBinder對象
          }
      }// 如果此時還有其他指向該目標對象的sp指針存在的話,就不會刪除目標對象
     
      refs->removeWeakRef(id);
      refs->decWeak(id);
      /* 刪除新建目標對象sp指針時在mWeakRefs鏈表上增加的兩個ref_entry結構體 */
  }
  /*********************************註釋3*********************************/
  void removeStrongRef(const void* id)
    {
        if (!mRetain) // mRetain 初始化成 flase
            removeRef(&mStrongRefs, id); 
            /* 刪除mStrongRefs鏈表中對應id的ref_entry一項 */
      /* 也就是取消了該sp對象和目標對象的聯繫 */
        else
            addRef(&mStrongRefs, id, -mStrong);
    }
  
  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;
            }
        }
    }
  /*********************************註釋3*********************************/
  
  delete BB_wp_ptr;
  這是刪除目標對象的一個wp指針,會調用wp的析構函數:
  template<typename T>
  wp<T>::~wp()
  {
      if (m_ptr) m_refs->decWeak(this);
  }
  調用weakref_type類的decWeak()函數,如下:
  void RefBase::weakref_type::decWeak(const void* id)
  {
      weakref_impl* const impl = static_cast<weakref_impl*>(this);
      impl->removeWeakRef(id);// 移除weakref_impl對象mWeakRefs鏈表中對應id的ref_entry結構體
      const int32_t c = android_atomic_dec(&impl->mWeak);// 引用計數減1
      LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
      if (c != 1) return; // c == 1, 說明這是系統中存在的指向目標對象的最後一個wp指針
     
      if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
          if (impl->mStrong == INITIAL_STRONG_VALUE)
              delete impl->mBase;
              // delete impl; 是不是應該加上這麼一句,防止用戶新建了wp後,不用,馬上又刪除的情況呢?
        /* 當目標對象的最後一個wp被析構時,如果目標對象還沒有建立任何一個sp,那麼目標對象被刪除 */
          else {
              delete impl;
        /* 當目標對象的最後一個wp被析構時,但此時和目標對象相關的sp全部被析構,那麼impl->mStrong = 0
            在最後一個sp被析構的時候,目標對象也被釋放,所以此時只需要釋放weakref_impl對象即可
        */
          }
      } else {
          impl->mBase->onLastWeakRef(id);
          if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
              delete impl->mBase;
          }
      }
  }

四、wp升級爲sp的過程
  wp的定義包含了:sp<T> promote() const;
  template<typename T>
  sp<T> wp<T>::promote() const
  {
      return sp<T>(m_ptr, m_refs);
  }
  wp,sp互爲友元類,這裏promote就是以友元身份調用了sp<Binder>類的構造函數: sp(T* p, weakref_type* refs);
  template<typename T>
  sp<T>::sp(T* p, weakref_type* refs)
      : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0)
  {
  }
  這裏如果升級成功,那麼將會產生一個sp對象指向目標對象,原來的wp仍然存在。
  如果升級不成功,返回NULL
  看看關鍵函數refs->attemptIncStrong(this)

  bool RefBase::weakref_type::attemptIncStrong(const void* id)
  {
      incWeak(id);
     
      weakref_impl* const impl = static_cast<weakref_impl*>(this);
     
      int32_t curCount = impl->mStrong;
      LOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow",
                 this);
      while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
          if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
              break;
          }
          curCount = impl->mStrong;
      }// 系統中還有其他sp指向目標對象的情況
     
      if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
          bool allow;
          if (curCount == INITIAL_STRONG_VALUE) {
          // 發現該目標對象還沒有一個sp對象與之相關聯的話,那麼將會新建一個對目標對象的強引用
              allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK
                    || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
          } else {
          /*
       發現系統中原來指向目標對象的sp全部被釋放,最後一次sp釋放也將目標對象釋放了
      */
              allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK
                    && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
          }
          if (!allow) {
              decWeak(id); // 目標對象已經不存在了,釋放前面incWeak(id)產生的ref_entry結構體
              return false; 
          }
          curCount = android_atomic_inc(&impl->mStrong);
  
          if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {
              impl->mBase->onLastStrongRef(id);
          }
      }
      // 走完生成一個sp的必要過程,和前面介紹的是一樣
      impl->addWeakRef(id);
      impl->addStrongRef(id);
  
      if (curCount == INITIAL_STRONG_VALUE) {
          android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);
          impl->mBase->onFirstRef();
      }
     
      return true; // 返回true
  }
  
 

五、總結:
  1. weakref_impl對象會隨着目標對象的生成而產生,但不一定會隨着目標對象的釋放而釋放。例如:如果目標對象被
    1個sp引用,但是同時被2個wp引用,那麼在sp被刪除的時候,刪除了目標對象,但沒有刪除weakref_impl對象,
    只有在最後一個wp釋放時,weakref_impl對象會被釋放。
  2. 一個目標對象被多個sp指針引用,沒有wp引用的情況下。釋放這些sp的時候,delete會調用sp析構函數,
    然後調用RefBase類的成員函數decStrong(), 最後一個sp被釋放時,weakref_impl對象數據成員mStrong會
    從1減到0(注意mStrong的初始化值爲1<<28, 從這個值可以判斷出該目標對象有沒有被sp指針引用過),
    同時釋放目標對象。
  3. 一個目標對象被多個wp指針引用,沒有sp引用的情況下。delete這些wp的時候,會調用wp的析構函數,該函數會
    調用函數decWeak()。當刪除最後一個wp的時候,代碼中只是刪除了目標對象,而沒有釋放weakref_impl對象,
    暫時沒發現在哪裏釋放了它。
  4. 一個目標對象既有sp,又有wp來引用。如果sp先被刪除光,那麼最後一個sp刪除的時候會釋放掉目標對象,那麼此時
    mStrong = 0。在後續最後一個wp的釋放過程中,在decWeak()函數中就會判斷出impl->mStrong !=
    INITIAL_STRONG_VALUE,而釋放掉剩下的weakref_impl對象了。如果先所以的wp刪除光,此時mWeak還等於剩餘的sp
    的個數,所以此時的釋放情況,同第2小點的說明。
  5. 從wp定義來看,wp是不能直接操作對象的,必須先升級爲sp才行。這個升級的過程是依靠函數promote()來完成的。
    升級成功,返回新生成的sp對象指針,升級失敗,返回NULL。需要注意的是,如果目標對象之前有過sp指向,但後來
    將所有的sp釋放完之後,此時目標對象是不存在的,那麼此時用戶還想將指向該目標對象的wp升級爲sp的話,
    此時就返回NULL。那麼這個時候我們應該delete這些剩下的wp。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章