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

来源:互联网 发布:国产手机推荐 知乎 编辑:程序博客网 时间:2024/04/29 15:03

在Android native编写代码时,会经常接触到sp、wp,sp并不是smart pointer的意思,而是strong point;wp就是weak pointer。这两个概念比较像JAVA中的强弱引用,使用sp和wp可以让编程人员不需要再关系内存的释放问题,防止内存泄露。下面先来看它们的类关系图:


要实现内存的自动释放,sp、wp必须结合RefBase这个类来使用,在Android中,大多数类的最上层基类都是RefBase类。我们举个简单的例子,然后顺着这个例子来分析RefBase、sp和wp四种不同的应用,并介绍其实现。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class A : public RefBase  
  2. {  
  3. }  

上面定义一个类A,继承与RefBase,下面我们首先来看RefBases的构造函数:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. RefBase::RefBase()  
  2.     : mRefs(new weakref_impl(this))  
  3. {  
  4. }  
  5.   
  6.     weakref_impl(RefBase* base)  
  7.         : mStrong(INITIAL_STRONG_VALUE)  
  8.         , mWeak(0)  
  9.         , mBase(base)  
  10.         , mFlags(0)  
  11.     {  
  12.     }  

在RefBase中,首先构造weakref_impl对象,在weakref_impl对mStong和mWeak进行强弱引用计数赋初始值,INITIAL_STRONG_VALUE是0X10000000,这里不直接赋初始值为0,是方便我们区分,0到底是初始化的值,还是在sp释放后再变为0,方便做不同的处理。


列举第一种应用:只有sp指针,没有wp指针的应用

{

    sp<A> spA(new A);

}

首先来看sp的构造函数:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. template<typename T>  
  2. sp<T>::sp(T* other)  
  3.         : m_ptr(other) {  
  4.     if (other)  
  5.         other->incStrong(this);  
  6. }  

首先将实际的A对象的指针赋给m_ptr,然后调用A对象的incStrong方法,由上面的类图关系,我们知道这里会调用RefBase的incStrong方法:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void RefBase::incStrong(const void* id) const  
  2. {  
  3.     weakref_impl* const refs = mRefs;  
  4.     refs->incWeak(id);  
  5.       
  6.     refs->addStrongRef(id);  
  7.     const int32_t c = android_atomic_inc(&refs->mStrong);  
  8.     ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);  
  9.   
  10.     if (c != INITIAL_STRONG_VALUE)  {  
  11.         return;  
  12.     }  
  13.   
  14.     android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);  
  15.     refs->mBase->onFirstRef();  
  16. }  

这里首先调用weakref_impl的incWeak方法来增加弱指针引用数;addStrongRef用于debug版本,正式版中没有实现;android_atomic_inc是一个原子操作,增加refs->mStrong的强指针引用数,并返回原值;如果是第一次引用改对象,则还会调用A对象的onFirstRef方法。首先来看incWeak的实现:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void RefBase::weakref_type::incWeak(const void* id)  
  2. {  
  3.     weakref_impl* const impl = static_cast<weakref_impl*>(this);  
  4.     impl->addWeakRef(id);  
  5.     const int32_t c = android_atomic_inc(&impl->mWeak);  
  6.     ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref"this);  
  7. }  

这里还是调用android_atomic_inc去增加weakref_impl的mWeak计数。经过构造函数,mStong和mWeak的计数都变成了1。当spA对象退出作用域以后,就会调用其析构函数来释放这个对象:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. template<typename T>  
  2. sp<T>::~sp() {  
  3.     if (m_ptr)  
  4.         m_ptr->decStrong(this);  
  5. }  
  6.   
  7. void RefBase::decStrong(const void* id) const  
  8. {  
  9.     weakref_impl* const refs = mRefs;  
  10.     refs->removeStrongRef(id);  
  11.     const int32_t c = android_atomic_dec(&refs->mStrong);  
  12.   
  13.     ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);  
  14.     if (c == 1) {  
  15.         refs->mBase->onLastStrongRef(id);  
  16.         if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {  
  17.             delete this;  
  18.         }  
  19.     }  
  20.     refs->decWeak(id);  
  21. }  

sp对象的析构函数调用RefBase的decStrong来减少强弱引用指针计数。weakref_impl的removeStrongRef用于debug版本;调用android_atomic_dec减少mStrong计数并返回原值,如果mStrong之前为1了,这是再减少,说明已经没有其它sp指针引用了,这时候首先调用A对象的onLastStrongRef方法,如果Flag设定的是当前对象的生命周期由sp指针决定(这也是default的设定),则释放掉A对象;然后调用weakref_impl的decWeak去减少弱引用指针计数:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void RefBase::weakref_type::decWeak(const void* id)  
  2. {  
  3.     weakref_impl* const impl = static_cast<weakref_impl*>(this);  
  4.     impl->removeWeakRef(id);  
  5.     const int32_t c = android_atomic_dec(&impl->mWeak);  
  6.     ALOG_ASSERT(c >= 1, "decWeak called on %p too many times"this);  
  7.     if (c != 1) return;  
  8.   
  9.     if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {  
  10.         if (impl->mStrong == INITIAL_STRONG_VALUE) {  
  11.             delete impl->mBase;  
  12.         } else {  
  13.             delete impl;  
  14.         }  
  15.     } else {  
  16.         impl->mBase->onLastWeakRef(id);  
  17.         if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {  
  18.             delete impl->mBase;  
  19.         }  
  20.     }  
  21. }  


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的系统函数:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. RefBase::~RefBase()  
  2. {  
  3.     if (mRefs->mStrong == INITIAL_STRONG_VALUE) {  
  4.         delete mRefs;  
  5.     } else {  
  6.         if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {  
  7.             if (mRefs->mWeak == 0) {  
  8.                 delete mRefs;  
  9.             }  
  10.         }  
  11.     }  
  12.     const_cast<weakref_impl*&>(mRefs) = NULL;  
  13. }  

如果没有初始化过sp对象,则删除mRefs对象;如果Flag设定当前对象的生命周期由wp指针决定并且mWeak计数为0,也删除mRefs对象。


列举第二种应用:只有wp指针,没有sp指针的应用


{

    wp<A> wpA(new A);

}

首先来看wp的构造方法:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. template<typename T>  
  2. wp<T>::wp(const sp<T>& other)  
  3.     : m_ptr(other.m_ptr)  
  4. {  
  5.     if (m_ptr) {  
  6.         m_refs = m_ptr->createWeak(this);  
  7.     }  
  8. }  

首先将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的指针,所以在下面的代码返回时要注意类型的转型。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. RefBase::weakref_type* RefBase::createWeak(const void* id) const  
  2. {  
  3.     mRefs->incWeak(id);  
  4.     return mRefs;  
  5. }  
  6.   
  7. void RefBase::weakref_type::incWeak(const void* id)  
  8. {  
  9.     weakref_impl* const impl = static_cast<weakref_impl*>(this);  
  10.     impl->addWeakRef(id);  
  11.     const int32_t c = android_atomic_inc(&impl->mWeak);  
  12.     ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref"this);  
  13. }  

这里只会增加mWeak 计数,这是mStrong等于INITIAL_STRONG_VALUE,mWeak等于1。当wpA退出作用域后,调用wp的析构函数:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. template<typename T>  
  2. wp<T>::~wp()  
  3. {  
  4.     if (m_ptr) m_refs->decWeak(this);  
  5. }  

decWeak函数我们上面讲过,如果Flag设定当前对象的生命周期由sp指针决定,并且之前没有初始化过任何sp对象,则直接删除A对象;并在RefBase的析构函数中取释放mRefs对象。


列举第三种应用:既有sp指针,又有wp指针的应用

{

   sp<A> spA(new A)

    wp<A> 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方法实现的:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. template<typename T>  
  2. sp<T> wp<T>::promote() const  
  3. {  
  4.     sp<T> result;  
  5.     if (m_ptr && m_refs->attemptIncStrong(&result)) {  
  6.         result.set_pointer(m_ptr);  
  7.     }  
  8.     return result;  
  9. }  

这里调用weakref_type的attemptIncStrong方法去尝试增加mStrong计数:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. bool RefBase::weakref_type::attemptIncStrong(const void* id)  
  2. {  
  3.     incWeak(id);  
  4.       
  5.     weakref_impl* const impl = static_cast<weakref_impl*>(this);  
  6.     int32_t curCount = impl->mStrong;  
  7.   
  8.     while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {  
  9.         if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {  
  10.             break;  
  11.         }  
  12.         curCount = impl->mStrong;  
  13.     }  
  14.       
  15.     if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {  
  16.         if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {  
  17.             if (curCount <= 0) {  
  18.                 decWeak(id);  
  19.                 return false;  
  20.             }  
  21.   
  22.             while (curCount > 0) {  
  23.                 if (android_atomic_cmpxchg(curCount, curCount + 1,  
  24.                         &impl->mStrong) == 0) {  
  25.                     break;  
  26.                 }  
  27.                 curCount = impl->mStrong;  
  28.             }  
  29.   
  30.             if (curCount <= 0) {  
  31.                 decWeak(id);  
  32.                 return false;  
  33.             }  
  34.         } else {  
  35.             if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {  
  36.                 decWeak(id);  
  37.                 return false;  
  38.             }  
  39.             curCount = android_atomic_inc(&impl->mStrong);  
  40.         }  
  41.   
  42.         if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {  
  43.             impl->mBase->onLastStrongRef(id);  
  44.         }  
  45.     }  
  46.       
  47.     impl->addStrongRef(id);  
  48.   
  49.     curCount = impl->mStrong;  
  50.     while (curCount >= INITIAL_STRONG_VALUE) {  
  51.         if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,  
  52.                 &impl->mStrong) == 0) {  
  53.             break;  
  54.         }  
  55.         curCount = impl->mStrong;  
  56.     }  
  57.   
  58.     return true;  
  59. }  

首先调用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调用相关的方法了。

0 0
原创粉丝点击