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

來源:互聯網
上載者:User

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


要實現記憶體的自動釋放,sp、wp必須結合RefBase這個類來使用,在Android中,大多數類的最上層基類都是RefBase類。我們舉個簡單的例子,然後順著這個例子來分析RefBase、sp和wp四種不同的應用,並介紹其實現。<喎?http://www.bkjia.com/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的建構函式:

templatesp::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對象退出範圍以後,就會調用其解構函式來釋放這個對象:

templatesp::~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的構造方法:

templatewp::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的解構函式:

templatewp::~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方法實現的:

templatesp 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調用相關的方法了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.