Android智能指針RefBase、sp、wp解析

[TOC]
在Android系統(tǒng)中,Native層的代碼基本都是C++寫的,C++跟Java不一樣,C++沒有垃圾回收機制,C++代碼中難于管理new出來對象的釋放,稍有不慎就造成內(nèi)存泄漏。針對此問題,Android中提出了一套類似Java垃圾回收機制的智能指針,采用強指針sp(Strong Pointer)和弱指針wp(Weak Pointer)對目標對象進行應(yīng)用,實現(xiàn)對象的自動回收。下面我們將從C++的基礎(chǔ)知識入手,對Android的智能指針展開逐步的分析。

1 相關(guān)基礎(chǔ)知識

Android的智能指針,巧妙的運用C++的基礎(chǔ)特性,實現(xiàn)對象的自動釋放,我們就先來看看都用了C++的什么特性。

1.1作用域

標記變量的有效范圍。從作用域上來看,可以將對象分為全局對象、局部對象、靜態(tài)全局對象和靜態(tài)局部對象。

一般來說,局部變量的有效作用域從它的定義點開始,到和定義變量之前最鄰近的開括號配對的第一個閉括號,也就是說,作用域由變量所在的最近一對{}括號確定。

演示代碼1

void testScope() {
  Sheep bigSheep; //局部對象
  {
    Sheep smallSheep; // 局部對象
  }// smallSheep的作用域結(jié)束
}// bigSheep的作用域結(jié)束

1.2對象內(nèi)存空間的分配、釋放

從內(nèi)存分配空間來看,可將對象分為棧對象和堆對象。棧對象在作用域結(jié)束后會自動釋放,而堆對象需要手動顯示的釋放。

演示代碼2

void testMemoryMap() {
  Sheep sheep; // 棧對象,testMemoryMap調(diào)用結(jié)束后自動釋放
  Sheep* pSheep; // 堆對象,需要手動釋放
  delete pSheep; // 釋放pSheep指向的對象
  pSheep = 0; //將pSheep指向NULL,防止造成野指針
}

圖1-1是內(nèi)存空間的分配示意圖。

圖 1?內(nèi)存空間的分配、釋放

1.3原子操作函數(shù)

定義在system/core/libcutils/Atomic.c,依賴于具體的芯片平臺。原子操作函數(shù)特點:線程安全,返回舊值。如下,Android原子操作函數(shù)一覽表。

函數(shù)名 功能
int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr) 加函數(shù),返回舊值,*ptr = *ptr + increment
int32_t android_atomic_inc(volatile int32_t *addr) 自增操作,返回舊值,*ptr = *ptr + 1
int32_t android_atomic_dec(volatile int32_t *addr) 自減操作, 返回舊值,*ptr = *ptr - 1
int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) 位與操作,返回舊值,*ptr = *ptr & value
int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) 位或操作,返回舊值,*ptr = *ptr or value
int android_atomic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t *ptr) 如果addr == oldvalue,就會執(zhí)行addr = new_value的操作,然后返回0,否則返回1

1.4引用計數(shù)的原理

棧對象在生命周期,即作用域結(jié)束后自動釋放,所以我們這里討論的是堆對象的引用,也就是指針對象。

圖1-2是指針引用時,利用引用數(shù)管理實際對象釋放的原理圖。

圖?2 引用計數(shù)原理

引用計數(shù)的原理很簡單,當(dāng)引用某個對象時,使其引用數(shù)+1;引用結(jié)束時,使其引用數(shù)-1;當(dāng)引用數(shù)為0時,delete掉實際對象。
根據(jù)前面的原理,引出兩個問題,帶著這兩個問題,我們來看看Android是怎么實現(xiàn)的。

  • 怎么管理引用數(shù)?
  • 怎么判斷引用開始和結(jié)束,怎么增減引用數(shù)?

2 Android智能指針原理

Android設(shè)計了基類RefBase,用以管理引用數(shù),所有類必須從RefBase派生,RefBase是所有對象的始祖。

設(shè)計模板類sp、wp,用以引用實際對象,sp強引用和wp弱引用。sp、wp聲明為棧對象,作用域結(jié)束時,自動釋放,自動調(diào)用析構(gòu)函數(shù)。因此,可以在sp、wp的構(gòu)造函數(shù)中,增引用數(shù);在析構(gòu)函數(shù)中,減少引用計數(shù)。

專門設(shè)計weakref_impl類,該類是RefBase的內(nèi)部類,用來做真正引用數(shù)管理,創(chuàng)建實際對象時,同時創(chuàng)建一個mRefs對象。不管是強引用和弱應(yīng)用,都由mRefs來管理。

圖2-1 展示Android智能指針的關(guān)系類圖。


圖 2-1 Android智能指針關(guān)系圖

看了智能指針的實現(xiàn)原理,我們來看看具體的實現(xiàn)是什么樣的。

2 智能指針的實現(xiàn)

根據(jù)前面的原理,Android設(shè)計了強引用sp和弱引用wp,故實際對象的釋放,可分為強引用控制和弱引用控制。所謂強引用控制,指的是強引用數(shù)mStrong為0時,釋放實際對象;弱引用控制,則指的是弱引用數(shù)mWeak為0時,才釋放實際對象。

下面將結(jié)合一些實例,分析具體的實現(xiàn)。我們先來看一段代碼實例。

代碼實例1

class Sheep: public RefBase { // 羊年,定義Sheep從RefBase派生
public:
    Sheep() : RefBase() { }// 可顯示調(diào)用RefBase的構(gòu)造,也可以不用
    virtual ~Sheep() { }// 最好聲明為virtual,以便從Sheep派生
}; 
void testSheep() {
    Sheep* pSheep = new Sheep(); // new一個Sheep對象,這個是一個堆對象
    { // 限定sp的作用域
        sp<Sheep> spSheep(pSheep); // spSheep是一個棧對象
        { // 限定wp的作用域
            wp<Sheep> wpSheep(pSheep);
        } // 調(diào)用wp的析構(gòu)函數(shù)
    } // 調(diào)用sp的析構(gòu)函數(shù),實際對象pSheep已釋放,若再使用pSheep將會出錯
}

3.1 RefBase構(gòu)造和mRefs

在實例代碼中,我們先定義了一個類Sheep,從RefBase派生,創(chuàng)建了一個實際對象,pSheep 指向?qū)嶋H對象。

在構(gòu)造Sheep的實際對象時,將調(diào)RefBase的構(gòu)造函數(shù)。RefBase的構(gòu)造函數(shù)如下,在構(gòu)造函數(shù)中創(chuàng)建mRefs。

weakref_impl從weakref_type派生,mRefs才是真正的“管家”。
[system/core/libutils/RefBase.cpp]

RefBase::RefBase()
    : mRefs(new weakref_impl(this)) // 真正管理引用計數(shù)
{}
weakref_impl(RefBase* base)
    : mStrong(INITIAL_STRONG_VALUE) // 1<<28(268435456),為什么不是0?
    , mWeak(0)
    , mBase(base) // mBase指向?qū)嶋H對象
    , mFlags(0) // 這個標識很重要,指定是強應(yīng)用控制還是弱引用控制
{}

請注意這里的mFlags,默認值為0,可通過修改這個標志來設(shè)置是強引用控制,還是弱引用控制,代碼如下:

system/core/include/utils/RefBase.h]

    enum {

        OBJECT_LIFETIME_STRONG  = 0x0000,

        OBJECT_LIFETIME_WEAK    = 0x0001,

        OBJECT_LIFETIME_MASK    = 0x0001

    };

mFlags默認為0,即OBJECT_LIFETIME_STRONG,強引用控制。設(shè)置為OBJECT_LIFETIME_WEAK時,為弱引用控制??梢酝ㄟ^extendObjectLifetime函數(shù)修改,代碼如下:

[system/core/libutils/RefBase.cpp]

void RefBase::extendObjectLifetime(int32_t mode)
{
    android_atomic_or(mode, &mRefs->mFlags);
}

3.2 sp構(gòu)造

接下來,我們創(chuàng)建了一個sp對象spSheep,這是一個棧對象,在其作用域結(jié)束后將自動釋放,調(diào)用sp的析構(gòu)函數(shù)。

[system/core/include/utils/StrongPointer.h]

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

other指向真正的Sheep對象,在sp的構(gòu)造函數(shù)中,將other賦值給了sp的m_ptr,m-ptr就指向了真正的Sheep對象。

因而,other->incStrong(this),實際就是Sheep的父類RefBase的incStrong函數(shù),代碼如下:

[system/core/libutils/RefBase.cpp]

void RefBase::incStrong(const void* id) const

{

    weakref_impl* const refs = mRefs;

    refs->incWeak(id); // 調(diào)用incWeak函數(shù)

    refs->addStrongRef(id); // 由DEBUG_REFS控制,release版本什么也不做

    const int32_t c = android_atomic_inc(&refs->mStrong); // 強引用數(shù)+1,c為舊值

    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);

    if (c != INITIAL_STRONG_VALUE)  { //判斷是否是第一次引用

        return;

    } // 第一次引用,refs->mStrong為1<<28 +1 (268435457)

    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); 

    // refs->mStrong為1

    refs->mBase->onFirstRef(); //第一次引用時調(diào)用

}

在incStrong函數(shù)中調(diào)用refs 的incWeak函數(shù),incWeak的代碼如下:

[system/core/libutils/RefBase.cpp]

void RefBase::weakref_type::incWeak(const void* id)

{

    weakref_impl* const impl = static_cast<weakref_impl*>(this);

    impl->addWeakRef(id); // 由DEBUG_REFS控制,release版本什么也不做

    const int32_t c __unused = android_atomic_inc(&impl->mWeak); //弱引用數(shù)+1

    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);

}

OK, sp構(gòu)造完成,增加一次強引用。sp構(gòu)造完成后,mRefs的強引用數(shù)變?yōu)?,弱引用數(shù)也變?yōu)?;第一次強引用時,回調(diào)onFirstRef()。

3.3 wp構(gòu)造

接下來,我們創(chuàng)建了一個wp對象wpSheep,這是一個棧對象,在其作用域結(jié)束后將自動釋放,調(diào)用wp的析構(gòu)函數(shù)。

[system/core/include/utils/RefBase.h]

template<typename T>

wp<T>::wp(T* other)

    : m_ptr(other)

{

    if (other) m_refs = other->createWeak(this);

}

other指向真正的Sheep對象,在wp的構(gòu)造函數(shù)中,將other賦值給了wp的m_ptr,m-ptr就指向了真正的Sheep對象。

因而,other-> createWeak (this),實際就是Sheep的父類RefBase的createWeak函數(shù),代碼如下:

[system/core/libutils/RefBase.cpp]

RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id); // incWeak函數(shù)前面分析過,最終的結(jié)果就是弱引用數(shù)+1
    return mRefs;
}

createWeak時,調(diào)用incWeak,最終的影響是弱引用數(shù)+1?,F(xiàn)在,我們的實例中,強引用數(shù)為1,弱引用數(shù)為2。

返回值為mRefs,也就是m_refs和mRefs指向同一個weakref_impl對象,而mRefs的mBase指向真正的對象Sheep。因此此處的spSheep和wpSheep都是管理同一個真正的對象。

3.4 wp析構(gòu)

繼續(xù)看我們的實例代碼,現(xiàn)在wpSheep的作用域結(jié)束,將調(diào)wp的析構(gòu)函數(shù),wp析構(gòu)函數(shù)的代碼如下:

[system/core/include/utils/RefBase.h]

template<typename T>
wp<T>::~wp()
{
    if (m_ptr) m_refs->decWeak(this); // 調(diào)用decWeak函數(shù)
}

在wp的析構(gòu)函數(shù)中調(diào)用m_refs的decWeak函數(shù)。m_refs和mRef指向同一個weakref_impl對象,decWeak代碼如下:

[system/core/libutils/RefBase.cpp]

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); // 弱引用數(shù)-1,c為舊值

   ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);

   if (c != 1) return; //c為舊值,判斷是否是最后一次弱引用

   // 記得前面我們說的,mFlags為0,我們并沒有改變它

   if ((impl->mFlags&OBJECT_LIFETIME_WEAK) ==

           OBJECT_LIFETIME_STRONG) {

// 強引用控制,是否釋放實際對象是根據(jù)強引用數(shù)

       if (impl->mStrong == INITIAL_STRONG_VALUE) {

delete impl->mBase; // 根本就沒有強引用引用實際對象,釋放實際對象

       } else {

           delete impl; // 釋放mRefs

       }

   } else {

       impl->mBase->onLastWeakRef(id); //最后一次弱引用時調(diào)用

       if ((impl->mFlags&OBJECT_LIFETIME_MASK) ==

                       OBJECT_LIFETIME_WEAK) {

           delete impl->mBase; //弱引用控制,釋放實際對象

       }

   }
}

wp析構(gòu),情況比較復(fù)雜,總的說來做了以下幾件事:

? 弱引用數(shù)減1。

? 最后一次弱引用時,強引用控制,釋放mRefs,若沒有強引用,釋放實際對象

? 最后一次弱引用時,弱引用控制,釋放實際對象

就我們的實例來看,此時,強引用數(shù)為1,弱引用數(shù)為1,并沒有任何釋放。

3.5 sp析構(gòu)

在我們的實例代碼中,wp析構(gòu)完后,sp的作用域也就結(jié)束了。此時,會調(diào)用sp的析構(gòu)函數(shù),代碼如下:

[system/core/include/utils/StrongPointer.h]

template<typename T>
sp<T>::~sp() {
    if (m_ptr)
        m_ptr->decStrong(this);
}

在析構(gòu)函數(shù)中調(diào)用m_ptr的decStrong函數(shù),m_ptr指向?qū)嶋H對象。此處為Sheep的父類RefBase的decStrong函數(shù),代碼如下:

[system/core/libutils/RefBase.cpp]

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id); // 由DEBUG_REFS控制,release版本什么也不做
    const int32_t c = android_atomic_dec(&refs->mStrong); // 強引用數(shù)-1
    ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
    if (c == 1) { // c為舊值,c為1時,即強引用數(shù)為0
        refs->mBase->onLastStrongRef(id); //最后一次強引用結(jié)束時調(diào)用
        if ((refs->mFlags&OBJECT_LIFETIME_MASK) ==
                        OBJECT_LIFETIME_STRONG) {
            delete this; // 若是強引用控制,釋放實際對象,調(diào)實際對象的析構(gòu)函數(shù)
        }
    }
    refs->decWeak(id);
}

refs的decWeak函數(shù),前面wp析構(gòu)的時候分析過,這里不再重復(fù)。sp析構(gòu)完成,主要完成以下工作:

? 強引用數(shù)減1,弱引用數(shù)據(jù)減1。

? 最后一次強引用時,若是強引用控制,釋放實際對象,釋放mRefs,調(diào)用onLastStrongRef函數(shù)。

在我們的代碼中,此時強引用數(shù)為0,弱引用數(shù)為0,實際對象的析構(gòu)函數(shù)將被調(diào)用,mRefs將被釋放。下面我看看實際對象的析構(gòu)函數(shù)。

3.6 RefBase析構(gòu)

實際對象的析構(gòu),先析構(gòu)RefBase,RefBase的析構(gòu)函數(shù)如下:

[system/core/libutils/RefBase.cpp]

RefBase::~RefBase()
{
    if (mRefs->mStrong == INITIAL_STRONG_VALUE) {
        delete mRefs; // 沒有強引用引用實際對象,釋放mRefs
    } else {
        if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) !=
                        OBJECT_LIFETIME_STRONG) {
            if (mRefs->mWeak == 0) {
                delete mRefs; // 釋放mRefs指向的對象
            }
        }
    }
    // for debugging purposes, clear this.
    const_cast<weakref_impl*&>(mRefs) = NULL; // mRefs指向0,避免野指針
}

OK,RefBase析構(gòu)分析完了,在RefBase的析構(gòu)函數(shù)中主要的工作就是釋放mRefs指向的weakref_impl的對象。

到此,我們的實例代碼分析完成,我們首先構(gòu)造一個Sheep對象,pSheep指向?qū)嶋H對象。再分別構(gòu)造一個強引用sp和一個弱引用wp,用以引用實際對象,實際對象的釋放就由sp和wp控制,我們并沒有顯示的釋放構(gòu)造的pSheep指向的實際對象。

我們來看看實例代碼1中,對象的構(gòu)造和析構(gòu)Log:

[示例代碼1的Log]

D/        (13624): Sheep::------------------testSheep start--------------------------
D/        (13624): Sheep::Sheep constructor invoked this=0xb6301080
D/        (13624): Sheep:: No refs, strong count=268435456, weak count=0
D/        (13624): Sheep::in sp scope ------------
D/        (13624): Sheep::onFirstRef, object=0xb6301080
D/        (13624): Sheep:: After strong ref, strong count=1, weak count=1
D/        (13624): Sheep::in wp scope ------------
D/        (13624): Sheep:: After weak ref, strong count=1, weak count=2
D/        (13624): Sheep::out wp scope ------------
D/        (13624): Sheep:: release weak ref, strong count=1, weak count=1
D/        (13624): Sheep::out sp scope ------------
D/        (13624): Sheep::onLastStrongRef, id=0xbec42884
D/        (13624): Sheep::Sheep destructor invoked this=0xb6301080
D/        (13624): Sheep::--------------------testSheep end--------------------------

3.7 實際對象的狀態(tài)

通過前面的分析,我們可以繪制出實際對象的狀態(tài)圖,如下如所示:

圖 3-1 實際對象的狀態(tài)圖

4 智能指針的使用

前面我們通過示例代碼1,知道了智能指針是怎么管理實際對象的,怎么控制實際對象的釋放的。但是我們只是分析了其中的構(gòu)造函數(shù)和析構(gòu)函數(shù),下面我們將對智能指針做全面的了解。

4.1 RefBase的特性

我們先看看RefBase的類圖,如圖4-1所示。

圖 4-1 RefBase類圖
  • 所有類須從RefBase派生,只有一個無參構(gòu)造函數(shù),RefBase析構(gòu)函數(shù)需申明為virtual。
  • 在構(gòu)造函數(shù)中創(chuàng)建mRefs對象,為weakref_impl類型。
  • 可以在派生類中通過函數(shù)extendObjectLifetime指定是強引用控制,還是弱引用控制,默認為強引用控制。
  • 在析構(gòu)函數(shù)中,判斷是否釋放mRefs。
  • 私有的構(gòu)造函數(shù)和賦值運算重載,不允許子類使用。
  • 獲取實際對象的強引用數(shù)getStrongCount
  • 子類可派生virtual成員函數(shù),獲知自身的引用情況。

[system/core/include/utils/StrongPointer.h]

// 第一次強引用時回調(diào)
virtual void onFirstRef();
// 最后一次強引用時調(diào)用
virtual void onLastStrongRef(const void* id); 
//由弱到強時調(diào)用,稍候介紹
virtual bool onIncStrongAttempted(uint32_t flags, const void* id); 
// 最后一次弱引用時調(diào)用
virtual void onLastWeakRef(const void* id); 

mRefs指向一個weakref_impl對象,是RefBase的應(yīng)用計數(shù)管家,其類圖如下圖4-2:

圖 4-2 mRefs的類圖
  • 可以通過getWeakRefs()->getStrongCount()獲取實際對象的弱引用數(shù)

4.2 sp模板類的特性

圖 4-3 sp的類圖
  • 提供多種形式的構(gòu)造方式
  • 定義多種形式的賦值運算操作
  • 重載操作運算符*,可以獲取實際對象
  • 重載操作運算符->,可以獲取指向?qū)嶋H對象的指針
  • 可通過get函數(shù),獲取實際對象的指針
  • force_set函數(shù)可以指定sp引用的實際對象,該函數(shù)設(shè)計有點缺點,若sp當(dāng)前已經(jīng)引用其他的對象,則可能造成其他對象無法釋放。稍后我們單獨介紹。

4.3 wp模板類特性

圖 4-4 wp類圖
  • 提供多種形式的構(gòu)造方式
  • 定義多種形式的賦值運算操作
  • 可通過unsafe_get函數(shù),獲取實際對象的指針,但是可能獲取到的是空的或是野指針
  • 可以通過promote函數(shù)將弱引用變?yōu)閺娨?,這個是一個比較重要的函數(shù),我們通過一個實例來看看是怎么由弱變強的。

[實例代碼2]

void testPromote() {
  {
    Sheep* pSheep = new Sheep();
    wp<Sheep> wpSheep(pSheep);
    sp<Sheep> spSheep = wpSheep.promote();
    }
}

Promote函數(shù)如下:

[system/core/include/utils/RefBase.h]

template<typename T>
sp<T> wp<T>::promote() const
{
    sp<T> result;
    if (m_ptr && m_refs->attemptIncStrong(&result)) {
        result.set_pointer(m_ptr);
    }
    return result;
}

[system/core/libutils/RefBase.cpp]

bool RefBase::weakref_type::attemptIncStrong(const void* id)

{

    incWeak(id); // 前面分析過,弱引用數(shù)+1;我們的實例中,此時弱引用數(shù)為2

    weakref_impl* const impl = static_cast<weakref_impl*>(this);

    int32_t curCount = impl->mStrong;

    while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {

        if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {

            break;

        }

        curCount = impl->mStrong;

    }

    if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {

        if ((impl->mFlags&OBJECT_LIFETIME_WEAK) ==

                        OBJECT_LIFETIME_STRONG) {

                … …

            while (curCount > 0) {

                if (android_atomic_cmpxchg(curCount, curCount + 1,

                        &impl->mStrong) == 0) { // 強引用控制,強引用數(shù)+1

                    break;

                }

                curCount = impl->mStrong;

            }

             … …

        } else {

            … … // 弱引用控制,強引用數(shù) +1

            curCount = android_atomic_inc(&impl->mStrong); 

        }

    }

    … …

    return true; // 由弱變強成功

}

promote函數(shù)成功后,強引用數(shù)+1,弱引用數(shù)+1。在我們的實例3中,此時弱引用數(shù)為2,強引用數(shù)為1。我們來看一下Log:

[實例代碼2的Log信息]

D/        (13742): Sheep:: -------------testPromote begin------------
D/        (13742): Sheep::Sheep constructor invoked this=0xb6301080
D/        (13742): Sheep:: No refs, strong count=268435456, weak count=0
D/        (13742): Sheep:: After weak ref, strong count=268435456, weak count=1
D/        (13742): Sheep:: After promote, strong count=1, weak count=2
D/        (13742): Sheep::onLastStrongRef, id=0xbed1c884
D/        (13742): Sheep::Sheep destructor invoked this=0xb6301080
D/        (13742): Sheep:: -------------testPromote   end------------

為什么要由弱生強?我們通過弱指針wp,不能獲取實際的對象,wp并沒有提供sp那個的存取操作*和->的重載,由弱生強后,可以sp獲取實際的對象。

5 輕引用 LightRefBase

前面介紹的RefBase和mRefs比較復(fù)雜,Android還提供了一個輕型的引用管理LightRefBase。LightRefBase的代碼比較少,直接看代碼:

[system/core/include/utils/RefBase.h]

template <class T>
class LightRefBase
{
public:
    inline LightRefBase() : mCount(0) { }
    inline void incStrong(__attribute__((unused)) const void* id) const {
        android_atomic_inc(&mCount);
    }

    inline void decStrong(__attribute__((unused)) const void* id) const {
        if (android_atomic_dec(&mCount) == 1) {
            delete static_cast<const T*>(this);
        }
    }

    inline int32_t getStrongCount() const {
        return mCount;
    }

    typedef LightRefBase<T> basetype;
protected:
    inline ~LightRefBase() { }
… …
private:
    mutable volatile int32_t mCount;
};

LightRefBase的類圖如下:

圖 5-1 LightRefBase類圖

LightRefBase比較簡單,直接用mCount控制引用數(shù),從LightRefBase的特性看來,LightRefBase只支持sp控制,不支持wp。

下面通過一個實例代碼,看看LightRefBase的使用,代碼如下:

[實例代碼3]

class Goat: public LightRefBase<Goat> {

public:

Goat() {

ALOGD("Goat::Goat constructor invoked this=%p \n", this);

}

private:

friend class LightRefBase<Goat> ;

~Goat() {

ALOGD("Goat::Goat destructor invoked this=%p \n", this);

}

};

void testGoat() {

Goat* pGoat = new Goat(); //初始時mCount為0

ALOGD("Goat:: before sp ref, mCount=%d", pGoat->getStrongCount());

{

sp<Goat> spGoat(pGoat); // 調(diào)用pGoat的incStrong函數(shù),mCount為1

}

// spGoat的作用域結(jié)束,調(diào)用pGoat的decStrong函數(shù),mCount為0,delete掉pGoat

}

實例代碼也非常簡單,只是和羊桿上了…今年羊年。下面是Goat的構(gòu)造和析構(gòu)情況,情況Log。

[實例代碼3的Log信息]

D/        (14539): Goat::--------------------testGoat end--------------------------
D/        (14539): Goat::Goat constructor invoked this=0xb6301080
D/        (14539): Goat:: before sp ref, mCount=0
D/        (14539): Sheep::in sp scope ------------
D/        (14539): Goat:: After sp ref, mCount=1
D/        (14539): Sheep::out sp scope ------------
D/        (14539): Goat::Goat destructor invoked this=0xb6301080
D/        (14539): Goat::--------------------testGoat end--------------------------

注意,LightRefBase的析構(gòu)函數(shù)不是virtual的,對象釋放時,可能會造成子類的析構(gòu)函數(shù)調(diào)不到,因此,Android對LightRefBase做了一個簡單的包裹,提供了VirtualLightRefBase類,VirtualLightRefBase的析構(gòu)函數(shù)是virtual的,使用時,優(yōu)先使用VirtualLightRefBase。

6 Android原始設(shè)計缺陷

任何設(shè)計都不敢保證沒有缺陷。前面說sp的時候我們說到sp的force_set函數(shù),下面我將具體來分析。

6.1 **force_set會破壞 sp 設(shè)計的初衷

force_set函數(shù)的原型:

[system/core/include/utils/StrongPointer.h]

template<typename T>
void sp<T>::force_set(T* other) {
    other->forceIncStrong(this);
    m_ptr = other; // 若m-ptr之前指向其他的對象,則其他對象的m-ptr不為0
}

我們來舉例說明一下,請看示例代碼4:

[實例代碼4]

void testForceSet()
{
  {
    sp<Sheep> spSheep(new Sheep());
    spSheep.force_set(new Sheep ());
  }
}

我們看看Sheep的釋放情況:

[實例代碼4的Log信息]

D/        (13385): Sheep:: -------------testForceSet begin------------
D/        (13385): Sheep::Sheep constructor invoked this=0xb6301080
D/        (13385): Sheep::onFirstRef, object=0xb6301080
D/        (13385): Sheep::Sheep constructor invoked this=0xb6301088
D/        (13385): Sheep::onFirstRef, object=0xb6301088
D/        (13385): Sheep::onLastStrongRef, id=0xbea5a894
D/        (13385): Sheep::Sheep destructor invoked this=0xb6301088
D/        (13385): Sheep:: -------------testForceSet begin------------

可見,我們構(gòu)造了兩個對象0xb6301080和0xb6301088,但是只有0xb6301088釋放掉了,0xb6301080沒有并釋放。所以使用force_set函數(shù)時一定要注意,但是如下的代碼是沒有問題的。

[演示代碼3]

void testForceSet()
{
  {
    sp<Sheep> spSheep; // spSheep的m_ptr并沒有指向?qū)嶋H的對象
    spSheep.force_set(new Sheep ());
  }
}

6.2 force_set的改進方案

在force_set函數(shù)時,先將原引用的對象的引用數(shù)-1,sp再引用新對象,這這樣可避免原實際對象不能釋放的問題。

[force_set的改進方案]

--- a/include/utils/StrongPointer.h
+++ b/include/utils/StrongPointer.h
@@ -187,7 +187,12 @@ sp<T>& sp<T>::operator =(U* other) {
 template<typename T>
 void sp<T>::force_set(T* other) {
-    other->forceIncStrong(this);
+    if (m_ptr) {
+        m_ptr->decStrong(this);
+    }
+    if (other) {
+        other->forceIncStrong(this);
+    }
     m_ptr = other;
 }

7 智能指針小結(jié)

我們從C++的特性出發(fā),逐步展開Android中智能指針的相關(guān)知識,從原理,到實現(xiàn),再到具體的使用。
Android設(shè)計sp和wp對實際對象進行引用,而實際對象類需要從基類RefBase或LightRefBase派生。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

  • 指針 在傳統(tǒng)的C++編程中,指針的使用一直是一把雙刃劍。指針賦予了我們直接操作硬件地址的能力,但同時也帶來了諸多問...
    passerbywhu閱讀 3,075評論 0 2
  • 本文主要內(nèi)容 進程通信概念 智能指針概念 強指針 弱指針 進程通信概念 同一個程序中兩個函數(shù)能互相調(diào)用的根本原因是...
    某昆閱讀 1,161評論 0 5
  • 引言:由于未來需要深入android底層進行系統(tǒng)級別的開發(fā),所以最近在看老羅的《Android系統(tǒng)源代碼情景分析》...
    拿破輪閱讀 2,311評論 0 9
  • Java和C/C++的一個重大區(qū)別,就是它沒有"指針"的概念,這并不代表Java不需要指針,而是將這個"超級武器隱...
    Sophia_dd35閱讀 612評論 0 1
  • 前言 Java 和 C/C++ 的一個重大區(qū)別,就是它沒有"指針"的概念,這并不代表 Java 不需要只用指針,而...
    seraphzxz閱讀 2,588評論 0 54

友情鏈接更多精彩內(nèi)容