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)對目標(biāo)對象進行應(yīng)用云茸,實現(xiàn)對象的自動回收是目。下面我們將從C++的基礎(chǔ)知識入手,對Android的智能指針展開逐步的分析标捺。

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

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

1.1作用域

標(biāo)記變量的有效范圍。從作用域上來看闺兢,可以將對象分為全局對象茂缚、局部對象、靜態(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) // 這個標(biāo)識很重要火架,指定是強應(yīng)用控制還是弱引用控制
{}

請注意這里的mFlags鉴象,默認(rèn)值為0忙菠,可通過修改這個標(biāo)志來設(shè)置是強引用控制,還是弱引用控制纺弊,代碼如下:

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

    enum {

        OBJECT_LIFETIME_STRONG  = 0x0000,

        OBJECT_LIFETIME_WEAK    = 0x0001,

        OBJECT_LIFETIME_MASK    = 0x0001

    };

mFlags默認(rèn)為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》跹樱現(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指定是強引用控制资厉,還是弱引用控制,默認(rèn)為強引用控制蔬顾。
  • 在析構(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)系作者
禁止轉(zhuǎn)載灌灾,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末悲柱,一起剝皮案震驚了整個濱河市锋喜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌豌鸡,老刑警劉巖嘿般,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異涯冠,居然都是意外死亡炉奴,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進店門蛇更,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瞻赶,“玉大人,你說我怎么就攤上這事派任≡已罚” “怎么了?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵掌逛,是天一觀的道長师逸。 經(jīng)常有香客問我,道長豆混,這世上最難降的妖魔是什么篓像? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮皿伺,結(jié)果婚禮上员辩,老公的妹妹穿的比我還像新娘。我一直安慰自己鸵鸥,他們只是感情好屈暗,可當(dāng)我...
    茶點故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著脂男,像睡著了一般。 火紅的嫁衣襯著肌膚如雪种呐。 梳的紋絲不亂的頭發(fā)上宰翅,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天,我揣著相機與錄音爽室,去河邊找鬼汁讼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嘿架。 我是一名探鬼主播瓶珊,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼耸彪!你這毒婦竟也來了伞芹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤蝉娜,失蹤者是張志新(化名)和其女友劉穎唱较,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體召川,經(jīng)...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡南缓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了荧呐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汉形。...
    茶點故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖倍阐,靈堂內(nèi)的尸體忽然破棺而出概疆,到底是詐尸還是另有隱情,我是刑警寧澤收捣,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布届案,位于F島的核電站,受9級特大地震影響罢艾,放射性物質(zhì)發(fā)生泄漏楣颠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一咐蚯、第九天 我趴在偏房一處隱蔽的房頂上張望童漩。 院中可真熱鬧,春花似錦春锋、人聲如沸矫膨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侧馅。三九已至,卻和暖如春呐萌,著一層夾襖步出監(jiān)牢的瞬間馁痴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工肺孤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留罗晕,地道東北人济欢。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像小渊,于是被迫代替她去往敵國和親法褥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,566評論 2 349

推薦閱讀更多精彩內(nèi)容

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