Android smart pointer analysis (sp, wp)

Source: Internet
Author: User

When writing code in Android native, it is often exposed to sp and wp. sp does not mean smart pointer, but strong point; wp is weak pointer. These two concepts are similar to the strong and weak references in JAVA. Using sp and wp can eliminate the need for programmers to release memory and prevent memory leakage. Let's first look at their class relationship diagram:


To enable automatic memory release, sp and wp must be used in combination with the RefBase class. In Android, the top base class of most classes is the RefBase class. Let's take a simple example. Next we will use this example to analyze the RefBase, sp, and wp applications and introduce their implementation. <喎?http: www.bkjia.com kf ware vc " target="_blank" class="keylink"> VcD4KPHA + PC9wPgo8cHJlIGNsYXNzPQ = "brush: java;"> class A: public RefBase {}
The above defines A class A, inheritance and RefBase. Next we will first look at the RefBases constructor:

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

In RefBase, The weakref_impl object is constructed first. In weakref_impl, the strength of mStong and mWeak is referenced and counted with the initial value. INITIAL_STRONG_VALUE is 0X10000000. The initial value is not assigned here, it is convenient for us to differentiate whether 0 is the initial value or whether it is changed to 0 after the sp is released, so it is convenient for different processing.


List the first application: an application with only sp pointers and no wp pointers

{

Sp spA (new );

}

First, let's look at the sp constructor:

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

First, assign the pointer of the actual object A to m_ptr, and then call the incStrong method of object A. From the class graph relationship above, we know that the incStrong method of RefBase will be called here:

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();}

Here, we first call weakref_impl's incWeak method to increase the number of weak pointer references. addStrongRef is used in the debug version, which is not implemented in the official version. android_atomic_inc is an atomic operation, increase the number of strong pointer references of refs-> mStrong and return the original value. If this is the first time the object is referenced and modified, the onFirstRef method of object A is also called. First, let's look at the implementation of 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);}
 

Here we still call android_atomic_inc to increase the weakref_impl mWeak count. After the constructor, the counts of mStong and mWeak are changed to 1. After the spA Object exits the scope, it will call its destructor to release the object:

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);}
  
 

The destructor of the sp object calls the decStrong of RefBase to reduce the number of strong and weak reference pointers. RemoveStrongRef of weakref_impl is used for the debug version. android_atomic_dec is called to reduce the mStrong count and return the original value. If mStrong is set to 1, this is reduced. It indicates that no other sp pointer has been referenced, at this time, the onlastrongref method of object A is called first. If the Flag sets the lifecycle of the current object determined by the sp pointer (this is also the default setting), the object A is released; then call the weakref_impl decWeak to reduce the weak reference pointer count:

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;        }    }}
 


The removeWeakRef method of weakref_impl is also used for the debug version. Then, android_atomic_dec is called to reduce the mWeak count. If the mWeak is not equal to 1, other wp references are available, and the result is returned directly here. If mWeak is equal to 1, it indicates that no reference has been made to other sp and wp. Therefore, the and weakref_impl objects will be released here.

Let's see whether to release the logic. If the Flag sets the lifecycle of the current object to be determined by the sp pointer, and no sp object has been initialized before, the object will be deleted directly; if the sp object has been initialized before, the weakref_impl itself will be deleted, and object A will be released in decStrong of RefBase. If the Flag sets the lifecycle of the current object to be determined by the wp pointer, The onLastWeakRef method of object A is called first, and object A is deleted. When deleting object A, the RefBase destructor will be called. Let's analyze the RefBase system functions:

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;}
 

If the sp object has not been initialized, The mRefs object is deleted. If the Flag sets the lifecycle of the current object to be determined by the wp pointer and the mWeak count is 0, the mRefs object is also deleted.


List the second application: Only wp pointer, no sp pointer


{

Wp wpA (new );

}

First, let's look at the wp construction method:

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

First, assign the pointer of object A to m_ptr. It can be seen that the actual pointer of object A is saved in sp and wp, but "->" is not overloaded in wp ", therefore, wp cannot directly call the method of object A. Based on the knowledge of sp, we know that in decStrong, object A may be released, it does not care whether wp references exist. Then the createWeak method of object A is called, which is actually the RefBase method. Note that in wp, m_refs is the weakref_type pointer, while in RefBase, mRefs is the weakref_impl pointer, so pay attention to the type transformation when the following code returns.

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);}
 

Here, only the mWeak count will be increased, which is that mStrong is equal to INITIAL_STRONG_VALUE, and mWeak is equal to 1. After wpA exits the scope, call the wp destructor:

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

As mentioned above, if the Flag sets the lifecycle of the current object to be determined by the sp pointer, and no sp object has been initialized before, the object will be deleted directly; and release the mRefs object in the RefBase destructor.


List the third application: sp pointer and wp pointer.

{

Sp spA (new)

Wp wpA (spA );

}


From their constructor above, we know that mStong is equal to 1, and mWeak is equal to 2. When spA and wpA exit the scope, first call the wp destructor and then the sp destructor. In the wp destructor, only the mWeak count is reduced to 1, and then the number is increased. In the sp destructor, it is the same as the first application we introduced earlier.


List the fourth application: wp pointer if the method of the object is called

As mentioned above, "->" is not reloaded in wp. Therefore, wp cannot directly call the method of object A. Based on the knowledge of sp, we know that in decStrong, it is possible that object A will be released, so to call the method of object A in wp, you must obtain the sp pointer, which is implemented through the promote method of wp:

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

Here we call the attemptIncStrong method of weakref_type to try to increase the mStrong count:

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;}
 

First, call incWeak to increase the mWeak count, because the sp pointer needs to be obtained here. We know in the sp constructor that mWeak and mStrong values will be added at the same time. Then, based on the mStong value, we will discuss it in two ways:

1. sp references exist on the current surface, that is, curCount> 0 & curCount! = INITIAL_STRONG_VALUE. Then, let mStrong Add 1 directly.

2. There is no reference to sp on the current surface, which must be determined by Flag. It is divided into the following situations:

1. Flag = OBJECT_LIFETIME_STRONG, and curCount is equal to 0. It indicates that the previous sp object has been released. We know from the previous knowledge that object A will be released when the sp object is released, therefore, decWeak is called here to release the previously added mWeak value and false is returned.

Ii. Flag = OBJECT_LIFETIME_STRONG and curCount = INITIAL_STRONG_VALUE indicate that no sp reference is provided before. In this case, we can increase the mStrong value.

3. Flag = OBJECT_LIFETIME_WEAK, and curCount <= 0 | curCount = INITIAL_STRONG_VALUE, call onIncStrongAttempted of RefBase to increase the mStrong Value

When the mStrong value is added in any of the above cases, the mSrong value may be greater than INITIAL_STRONG_VALUE. We need to modify mStrong by subtracting INITIAL_STRONG_VALUE. When attemptIncStrong returns true, the promote method calls the set_pointer method of sp to set the pointer of the actual A object in StrongPointer. Next, you can call related methods through sp.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.