Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析(3)-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析(3)

简介:

 提供引用计数器的类RefBase我们就暂时介绍到这里,后面我们再结合智能指针类一起分析,现在先来看看强指针类和弱指针类的定义。强指针类的定义我们在前面介绍轻量级指针的时候已经见到了,就是sp类了,这里就不再把它的代码列出来了。我们来看看它的构造函数的实现:


  1. template<typename T>   
  2. sp<T>::sp(T* other)   
  3.     : m_ptr(other)   
  4. {   
  5.     if (other) other->incStrong(this);   
  6. }   

  这里传进来的参数other一定是继承于RefBase类的,因此,在函数的内部,它调用的是RefBase类的incStrong函数,它定义在frameworks/base/libs/utils/RefBase.cpp文件中: 


  1. void RefBase::incStrong(const void* id) const   
  2. {   
  3.     weakref_impl* const refs = mRefs;   
  4.     refs->addWeakRef(id);   
  5.     refs->incWeak(id);   
  6.     refs->addStrongRef(id);    
  7.    
  8.     const int32_t c = android_atomic_inc(&refs->mStrong);    
  9.     LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);   
  10.    
  11.     #if PRINT_REFS    
  12.     LOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);   
  13.     #endif    
  14.    
  15.     if (c != INITIAL_STRONG_VALUE) {    
  16.         return;    
  17.     }    
  18.    
  19.     android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);    
  20.     const_cast<RefBase*>(this)->onFirstRef();   
  21. }   

  成员变量mRefs是在RefBase类的构造函数中创建的:


  1. RefBase::RefBase()   
  2.     : mRefs(new weakref_impl(this))   
  3. {   
  4. //    LOGV("Creating refs %p with RefBase %p\n", mRefs, this);   
  5. }   

 在这个incStrong函数中,主要做了三件事情:

 一是增加弱引用计数:


  1. refs->addWeakRef(id);   
  2. refs->incWeak(id);   

 二是增加强引用计数:


  1. refs->addStrongRef(id);   
  2. const int32_t c = android_atomic_inc(&refs->mStrong);   

   三是如果发现是首次调用这个对象的incStrong函数,就会调用一个这个对象的onFirstRef函数,让对象有机会在对象被首次引用时做一些处理逻辑:


  1. if (c != INITIAL_STRONG_VALUE)  {   
  2.     return;   
  3. }   
  4.    
  5. android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);   
  6. const_cast<RefBase*>(this)->onFirstRef();   

 这里的c返回的是refs->mStrong加1前的值,如果发现等于INITIAL_STRONG_VALUE,就说明这个对象的强引用计数是第一次被增加,因此,refs->mStrong就是初始化为INITIAL_STRONG_VALUE的,它的值为:#define INITIAL_STRONG_VALUE (1<<28)  
   这个值加1后等于1<<28 + 1,不等于1,因此,后面要再减去-INITIAL_STRONG_VALUE,于是,refs->mStrong就等于1了,就表示当前对象的强引用计数值为1了,这与这个对象是第一次被增加强引用计数值的逻辑是一致的。
        回过头来看弱引用计数是如何增加的,首先是调用weakref_impl类的addWeakRef函数,我们知道,在Release版本中,这个函数也不做,而在Debug版本中,这个函数增加了一个ref_entry对象到了weakref_impl对象的mWeakRefs列表中,表示此weakref_impl对象的弱引用计数被增加了一次。接着又调用了weakref_impl类的incWeak函数,真正增加弱引用计数值就是在这个函数实现的了,weakref_impl类的incWeak函数继承于其父类weakref_type的incWeak函数:


  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.     LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);   
  7. }   

  增加弱引用计数是下面语句执行的:const int32_t c = android_atomic_inc(&impl->mWeak);     但是前面为什么又调用了一次addWeakRef函数呢?前面不是已经调用过了吗?在Release版本中,因为weakref_impl类的addWeakRef函数是空实现,这里再调用一次没有什么害处,但是如果在Debug版本,岂不是冗余了吗?搞不清,有人问过负责开发Android系统Binder通信机制模块的作者Dianne Hackborn这个问题,他是这样回答的: 

 http://groups.google.com/group/android-platform/browse_thread/thread/cc641db8487dd83

        Ah I see.  Well the debug code may be broken, though I wouldn't leap to that 
        conclusion without actually testing it; I know it has been used in the 
        past.  Anyway, these things get compiled out in non-debug builds, so there 
        is no reason to change them unless you are actually trying to use this debug 
        code and it isn't working and need to do this to fix it. 

        既然他也不知道怎么回事,我们也不必深究了,知道有这么回事就行。

        这里总结一下强指针类sp在其构造函数里面所做的事情就是分别为目标对象的强引用计数和弱引和计数增加了1。

        再来看看强指针类的析构函数的实现:


  1. template<typename T>   
  2. sp<T>::~sp()   
  3. {   
  4.     if (m_ptr) m_ptr->decStrong(this);   
  5. }   

     同样,这里的m_ptr指向的目标对象一定是继承了RefBase类的,因此,这里调用的是RefBase类的decStrong函数,这也是定义在frameworks/base/libs/utils/RefBase.cpp文件中:


  1. void RefBase::decStrong(const void* id) const   
  2. {   
  3.     weakref_impl* const refs = mRefs;   
  4.     refs->removeStrongRef(id);   
  5.     const int32_t c = android_atomic_dec(&refs->mStrong);   
  6. #if PRINT_REFS   
  7.     LOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);   
  8. #endif   
  9.     LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);   
  10.     if (c == 1) {   
  11.         const_cast<RefBase*>(this)->onLastStrongRef(id);   
  12.         if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {   
  13.             delete this;   
  14.         }   
  15.     }   
  16.     refs->removeWeakRef(id);   
  17.     refs->decWeak(id);   
  18. }   

  这里的refs->removeStrongRef函数调用语句是对应前面在RefBase::incStrong函数里的refs->addStrongRef函数调用语句的,在Release版本中,这也是一个空实现函数,真正实现强引用计数减1的操作是下面语句:


  1. const int32_t c = android_atomic_dec(&refs->mStrong);   

   如果发现减1前,此对象的强引用计数为1,就说明从此以后,就再没有地方引用这个目标对象了,这时候,就要看看是否要delete这个目标对象了:


  1. if (c == 1) {   
  2.     const_cast<RefBase*>(this)->onLastStrongRef(id);   
  3.     if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {   
  4.         delete this;   
  5.     }   
  6. }   

  在强引用计数为0的情况下,如果对象的标志位OBJECT_LIFETIME_WEAK被设置了,就说明这个对象的生命周期是受弱引用计数所控制的,因此,这时候就不能delete对象,要等到弱引用计数也为0的情况下,才能delete这个对象。

 

        接下来的ref->removeWeakRef函数调用语句是对应前面在RefBase::incStrong函数里的refs->addWeakRef函数调用语句的,在Release版本中,这也是一个空实现函数,真正实现强引用计数减1的操作下面的refs->decWeak函数,weakref_impl类没有实现自己的decWeak函数,它继承了weakref_type类的decWeak函数:


  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.     LOG_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_WEAK) {   
  10.         if (impl->mStrong == INITIAL_STRONG_VALUE)   
  11.             delete impl->mBase;   
  12.         else {   
  13. //            LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);   
  14.             delete impl;   
  15.         }   
  16.     } else {   
  17.         impl->mBase->onLastWeakRef(id);   
  18.         if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {   
  19.             delete impl->mBase;   
  20.         }   
  21.     }   
  22. }   

    这里又一次调用了weakref_impl对象的removeWeakRef函数,这也是和RefBase::weakref_type::incWeak函数里面的impl->addWeakRef语句所对应的,实现弱引用计数减1的操作是下面语句:const int32_t c = android_atomic_dec(&impl->mWeak);  

 减1前如果发现不等于1,那么就什么也不用做就返回了,如果发现等于1,就说明当前对象的弱引用计数值为0了,这时候,就要看看是否要delete这个对象了:


  1. if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {   
  2.     if (impl->mStrong == INITIAL_STRONG_VALUE)   
  3.         delete impl->mBase;   
  4.     else {   
  5. //      LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);   
  6.         delete impl;   
  7.     }   
  8. else {   
  9.     impl->mBase->onLastWeakRef(id);   
  10.     if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {   
  11.         delete impl->mBase;   
  12.     }   
  13. }   

  如果目标对象的生命周期是不受弱引用计数控制的,就执行下面语句:


  1. if (impl->mStrong == INITIAL_STRONG_VALUE)   
  2.     delete impl->mBase;   
  3. else {   
  4. //  LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);   
  5.     delete impl;   
  6. }   

     这个代码段是什么意思呢?这里是减少对象的弱引用计数的地方,如果调用到这里,那么就说明前面一定有增加过此对象的弱引用计数,而增加对象的弱引用计数有两种场景的,一种场景是增加对象的强引用计数的时候,会同时增加对象的弱引用计数,另一种场景是当我们使用一个弱指针来指向对象时,在弱指针对象的构造函数里面,也会增加对象的弱引用计数,不过这时候,就只是增加对象的弱引用计数了,并没有同时增加对象的强引用计数。因此,这里在减少对象的弱引用计数时,就要分两种情况来考虑。

 

        如果是前一种场景,这里的impl->mStrong就必然等于0,而不会等于INITIAL_STRONG_VALUE值,因此,这里就不需要delete目标对象了(impl->mBase),因为前面的RefBase::decStrong函数会负责delete这个对象。这里唯一需要做的就是把weakref_impl对象delete掉,但是,为什么要在这里delete这个weakref_impl对象呢?这里的weakref_impl对象是在RefBase的构造函数里面new出来的,理论上说应该在在RefBase的析构函数里delete掉这个weakref_impl对象的。在RefBase的析构函数里面,的确是会做这件事情:


  1. RefBase::~RefBase()   
  2. {   
  3. //    LOGV("Destroying RefBase %p (refs %p)\n", this, mRefs);   
  4.     if (mRefs->mWeak == 0) {   
  5. //        LOGV("Freeing refs %p of old RefBase %p\n", mRefs, this);   
  6.         delete mRefs;   
  7.     }   
  8. }   

   但是不要忘记,在这个场景下,目标对象是前面的RefBase::decStrong函数delete掉的,这时候目标对象就会被析构,但是它的弱引用计数值尚未执行减1操作,因此,这里的mRefs->mWeak == 0条件就不成立,于是就不会delete这个weakref_impl对象,因此,就延迟到执行这里decWeak函数时再执行。 

如果是后一种情景,这里的impl->mStrong值就等于INITIAL_STRONG_VALUE了,这时候由于没有地方会负责delete目标对象,因此,就需要把目标对象(imp->mBase)delete掉了,否则就会造成内存泄漏。在delete这个目标对象的时候,就会执行RefBase类的析构函数,这时候目标对象的弱引用计数等于0,于是,就会把weakref_impl对象也一起delete掉了。





本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966562,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享: