Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android智能指針分析(sp、wp)

Android智能指針分析(sp、wp)

編輯:關於Android編程

在Android native編寫代碼時,會經常接觸到sp、wp,sp並不是smart pointer的意思,而是strong point;wp就是weak pointer。這兩個概念比較像JAVA中的強弱引用,使用sp和wp可以讓編程人員不需要再關系內存的釋放問題,防止內存洩露。下面先來看它們的類關系圖:

\

要實現內存的自動釋放,sp、wp必須結合RefBase這個類來使用,在Android中,大多數類的最上層基類都是RefBase類。我們舉個簡單的例子,然後順著這個例子來分析RefBase、sp和wp四種不同的應用,並介紹其實現。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+PC9wPgo8cHJlIGNsYXNzPQ=="brush:java;">class A : public RefBase { }
上面定義一個類A,繼承與RefBase,下面我們首先來看RefBases的構造函數:

RefBase::RefBase()
    : mRefs(new weakref_impl(this))
{
}

    weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)
        , mWeak(0)
        , mBase(base)
        , mFlags(0)
    {
    }

在RefBase中,首先構造weakref_impl對象,在weakref_impl對mStong和mWeak進行強弱引用計數賦初始值,INITIAL_STRONG_VALUE是0X10000000,這裡不直接賦初始值為0,是方便我們區分,0到底是初始化的值,還是在sp釋放後再變為0,方便做不同的處理。


列舉第一種應用:只有sp指針,沒有wp指針的應用

{

sp spA(new A);

}

首先來看sp的構造函數:

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

首先將實際的A對象的指針賦給m_ptr,然後調用A對象的incStrong方法,由上面的類圖關系,我們知道這裡會調用RefBase的incStrong方法:

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);
    
    refs->addStrongRef(id);
    const int32_t c = android_atomic_inc(&refs->mStrong);
    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);

    if (c != INITIAL_STRONG_VALUE)  {
        return;
    }

    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
    refs->mBase->onFirstRef();
}

這裡首先調用weakref_impl的incWeak方法來增加弱指針引用數;addStrongRef用於debug版本,正式版中沒有實現;android_atomic_inc是一個原子操作,增加refs->mStrong的強指針引用數,並返回原值;如果是第一次引用改對象,則還會調用A對象的onFirstRef方法。首先來看incWeak的實現:

void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast(this);
    impl->addWeakRef(id);
    const int32_t c = android_atomic_inc(&impl->mWeak);
    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}

這裡還是調用android_atomic_inc去增加weakref_impl的mWeak計數。經過構造函數,mStong和mWeak的計數都變成了1。當spA對象退出作用域以後,就會調用其析構函數來釋放這個對象:

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

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);
    const int32_t c = android_atomic_dec(&refs->mStrong);

    ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
    if (c == 1) {
        refs->mBase->onLastStrongRef(id);
        if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            delete this;
        }
    }
    refs->decWeak(id);
}

sp對象的析構函數調用RefBase的decStrong來減少強弱引用指針計數。weakref_impl的removeStrongRef用於debug版本;調用android_atomic_dec減少mStrong計數並返回原值,如果mStrong之前為1了,這是再減少,說明已經沒有其它sp指針引用了,這時候首先調用A對象的onLastStrongRef方法,如果Flag設定的是當前對象的生命周期由sp指針決定(這也是default的設定),則釋放掉A對象;然後調用weakref_impl的decWeak去減少弱引用指針計數:

void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast(this);
    impl->removeWeakRef(id);
    const int32_t c = android_atomic_dec(&impl->mWeak);
    ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
    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 {
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
            delete impl->mBase;
        }
    }
}


weakref_impl的removeWeakRef方法也是用於debug版本;然後調用android_atomic_dec去減少mWeak計數,如果mWeak之前不等於1,表示還有其它的wp引用,這裡就直接返回。如果這裡的mWeak等於1,說明已經沒有其它sp和wp的引用了,所以這裡要去釋放A對象和weakref_impl對象。

來看判斷是否釋放的邏輯,如果Flag設定當前對象的生命周期由sp指針決定,並且之前沒有初始化過任何sp對象,則直接刪除A對象;如果之前由初始化過sp對象,則刪除weakref_impl本身,A對象會在RefBase的decStrong中被釋放。如果Flag設定當前對象的生命周期由wp指針決定,則首先調用A對象的onLastWeakRef方法,然後刪除對象A。在刪除對象A的時候,都會調用RefBase的析構函數,我們再來分析RefBase的系統函數:

RefBase::~RefBase()
{
    if (mRefs->mStrong == INITIAL_STRONG_VALUE) {
        delete mRefs;
    } else {
        if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {
            if (mRefs->mWeak == 0) {
                delete mRefs;
            }
        }
    }
    const_cast(mRefs) = NULL;
}

如果沒有初始化過sp對象,則刪除mRefs對象;如果Flag設定當前對象的生命周期由wp指針決定並且mWeak計數為0,也刪除mRefs對象。


列舉第二種應用:只有wp指針,沒有sp指針的應用


{

wp wpA(new A);

}

首先來看wp的構造方法:

template
wp::wp(const sp& other)
    : m_ptr(other.m_ptr)
{
    if (m_ptr) {
        m_refs = m_ptr->createWeak(this);
    }
}

首先將A對象的指針賦予給m_ptr,可見在sp和wp中都保存有A對象的實際指針,但wp中並沒有重載"->",所以wp並不能直接調用A對象的方法,並且由前面sp的知識,我們知道,在decStrong的時候,有可能A對象會被釋放,並不會care 是否存在wp的引用。然後調用A對象的createWeak方法,實際上是調用RefBase的這個方法。注意的是在wp中,m_refs是weakref_type的指針;而在RefBase中,mRefs是weakref_impl的指針,所以在下面的代碼返回時要注意類型的轉型。

RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id);
    return mRefs;
}

void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast(this);
    impl->addWeakRef(id);
    const int32_t c = android_atomic_inc(&impl->mWeak);
    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}

這裡只會增加mWeak 計數,這是mStrong等於INITIAL_STRONG_VALUE,mWeak等於1。當wpA退出作用域後,調用wp的析構函數:

template
wp::~wp()
{
    if (m_ptr) m_refs->decWeak(this);
}

decWeak函數我們上面講過,如果Flag設定當前對象的生命周期由sp指針決定,並且之前沒有初始化過任何sp對象,則直接刪除A對象;並在RefBase的析構函數中取釋放mRefs對象。


列舉第三種應用:既有sp指針,又有wp指針的應用

{

sp spA(new A)

wp wpA(spA);

}


從上面它們的構造函數我們知道,這是mStong等於1,mWeak等於2。在spA和wpA退出作用域時,首先調用wp的析構函數,再調用sp的析構函數。在wp析構函數中,只會減少mWeak計數為1,然後就然後了。再到sp的析構函數中,就和我們前面介紹的第一種應用一樣了。


列舉第四種應用:wp指針如果調用對象的方法

前面說過在wp中並沒有重載"->",所以wp並不能直接調用A對象的方法,並且由前面sp的知識,我們知道,在decStrong的時候,有可能A對象會被釋放,所以在wp中想要調用A對象的方法,必須獲得sp指針,這是通過wp的promote方法實現的:

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

這裡調用weakref_type的attemptIncStrong方法去嘗試增加mStrong計數:

bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
    incWeak(id);
    
    weakref_impl* const impl = static_cast(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) {
            if (curCount <= 0) {
                decWeak(id);
                return false;
            }

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

            if (curCount <= 0) {
                decWeak(id);
                return false;
            }
        } else {
            if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
                decWeak(id);
                return false;
            }
            curCount = android_atomic_inc(&impl->mStrong);
        }

        if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {
            impl->mBase->onLastStrongRef(id);
        }
    }
    
    impl->addStrongRef(id);

    curCount = impl->mStrong;
    while (curCount >= INITIAL_STRONG_VALUE) {
        if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,
                &impl->mStrong) == 0) {
            break;
        }
        curCount = impl->mStrong;
    }

    return true;
}

首先調用incWeak來增加mWeak計數,因為這裡需要獲取sp指針,在sp的構造函數我們知道,會同時增加mWeak和mStrong值。然後根據mStong值分兩種情況討論:

1. 當前面存在sp的引用,即curCount > 0 && curCount != INITIAL_STRONG_VALUE,這時直接讓mStrong加1。

2.當前面不存在sp的引用,需要結合Flag去判斷。又分為以下幾種情況:

一. Flag = OBJECT_LIFETIME_STRONG,並且curCount等於0。說明之前的sp對象已經釋放,由前面的知識我們知道,在釋放sp對象的同時也會釋放對象A,所以這裡調用decWeak來釋放前面增加的一次mWeak值並返回false

二.Flag = OBJECT_LIFETIME_STRONG,並且curCount = INITIAL_STRONG_VALUE,說明前面沒有sp引用,這時我們可以增加mStrong值。

三.Flag = OBJECT_LIFETIME_WEAK,並且curCount <= 0 || curCount == INITIAL_STRONG_VALUE,則調用RefBase的onIncStrongAttempted去嘗試增加mStrong值

當上面任何一種情況增加了mStrong值以後,mSrong的值可能大於INITIAL_STRONG_VALUE,我們需要去修正mStrong,就是通過減去INITIAL_STRONG_VALUE計算。當attemptIncStrong返回true時,promote方法就會調用sp的set_pointer方法去設置StrongPointer中的實際A對象的指針。接下來就可以通過sp調用相關的方法了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved