Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析(4)
回到外层的if语句中,如果目标对象的生命周期是受弱引用计数控制的,就执行下面语句: impl->mBase->onLastWeakRef(id); if((impl->mFlags&OBJECT_LIFETIME_FOREVER)!=OBJECT_LIFETIME_FOREVER){ deleteimpl->mBase; } 理论上说,如果目标对象的生命周期是受弱引用计数控制的,那么当强引用计数和弱引用计数都为0的时候,这时候就应该delete目标对象了,但是这里还有另外一层控制,我们可以设置目标对象的标志值为OBJECT_LIFETIME_FOREVER,即目标对象的生命周期完全不受强引用计数和弱引用计数控制,在这种情况下,即使目标对象的强引用计数和弱引用计数都同时为0,这里也不能delete这个目标对象,那么,由谁来delete掉呢?当然是谁new出来的,就谁来delete掉了,这时候智能指针就完全退化为普通指针了,这里的智能指针设计的非常强大。 分析到这里,有必要小结一下: A. 如果对象的标志位被设置为0,那么只要发现对象的强引用计数值为0,那就会自动delete掉这个对象; B. 如果对象的标志位被设置为OBJECT_LIFETIME_WEAK,那么只有当对象的强引用计数和弱引用计数都为0的时候,才会自动delete掉这个对象; C. 如果对象的标志位被设置为OBJECT_LIFETIME_FOREVER,那么对象就永远不会自动被delete掉,谁new出来的对象谁来delete掉。 到了这里,强指针就分析完成了,最后来分析弱指针。 4. 弱指针 弱指针所使用的引用计数类与强指针一样,都是RefBase类,因此,这里就不再重复介绍了,我们直接来弱指针的实现,它定义在frameworks/base/include/utils/RefBase.h文件中: template<typenameT> classwp { public: typedeftypenameRefBase::weakref_typeweakref_type; inlinewp():m_ptr(0){} wp(T*other); wp(constwp<T>&other); wp(constsp<T>&other); template<typenameU>wp(U*other); template<typenameU>wp(constsp<U>&other); template<typenameU>wp(constwp<U>&other); ~wp(); //Assignment wp&operator=(T*other); wp&operator=(constwp<T>&other); wp&operator=(constsp<T>&other); template<typenameU>wp&operator=(U*other); template<typenameU>wp&operator=(constwp<U>&other); template<typenameU>wp&operator=(constsp<U>&other); voidset_object_and_refs(T*other,weakref_type*refs); //promotiontosp sp<T>promote()const; //Reset voidclear(); //Accessors inlineweakref_type*get_refs()const{returnm_refs;} inlineT*unsafe_get()const{returnm_ptr;} //Operators COMPARE_WEAK(==) COMPARE_WEAK(!=) COMPARE_WEAK(>) COMPARE_WEAK(<) COMPARE_WEAK(<=) COMPARE_WEAK(>=) inlinebooloperator==(constwp<T>&o)const{ return(m_ptr==o.m_ptr)&&(m_refs==o.m_refs); } template<typenameU> inlinebooloperator==(constwp<U>&o)const{ returnm_ptr==o.m_ptr; } inlinebooloperator>(constwp<T>&o)const{ return(m_ptr==o.m_ptr)?(m_refs>o.m_refs):(m_ptr>o.m_ptr); } template<typenameU> inlinebooloperator>(constwp<U>&o)const{ return(m_ptr==o.m_ptr)?(m_refs>o.m_refs):(m_ptr>o.m_ptr); } inlinebooloperator<(constwp<T>&o)const{ return(m_ptr==o.m_ptr)?(m_refs<o.m_refs):(m_ptr<o.m_ptr); } template<typenameU> inlinebooloperator<(constwp<U>&o)const{ return(m_ptr==o.m_ptr)?(m_refs<o.m_refs):(m_ptr<o.m_ptr); } inlinebooloperator!=(constwp<T>&o)const{returnm_refs!=o.m_refs;} template<typenameU>inlinebooloperator!=(constwp<U>&o)const{return!operator==(o);} inlinebooloperator<=(constwp<T>&o)const{return!operator>(o);} template<typenameU>inlinebooloperator<=(constwp<U>&o)const{return!operator>(o);} inlinebooloperator>=(constwp<T>&o)const{return!operator<(o);} template<typenameU>inlinebooloperator>=(constwp<U>&o)const{return!operator<(o);} private: template<typenameY>friendclasssp; template<typenameY>friendclasswp; T*m_ptr; weakref_type*m_refs; }; 与强指针类相比,它们都有一个成员变量m_ptr指向目标对象,但是弱指针还有一个额外的成员变量m_refs,它的类型是weakref_type指针,下面我们分析弱指针的构造函数时再看看它是如果初始化的。这里我们需要关注的仍然是弱指针的构造函数和析构函数。 先来看构造函数: template<typenameT> wp<T>::wp(T*other) :m_ptr(other) { if(other)m_refs=other->createWeak(this); } 这里的参数other一定是继承了RefBase类,因此,这里调用了RefBase类的createWeak函数,它定义在frameworks/base/libs/utils/RefBase.cpp文件中: RefBase::weakref_type*RefBase::createWeak(constvoid*id)const { mRefs->incWeak(id); returnmRefs; } 这里的成员变量mRefs的类型为weakref_impl指针,weakref_impl类的incWeak函数我们在前面已经看过了,它的作用就是增加对象的弱引用计数。函数最后返回mRefs,于是,弱指针对象的成员变量m_refs就指向目标对象的weakref_impl对象了。 再来看析构函数: template<typenameT> wp<T>::~wp() { if(m_ptr)m_refs->decWeak(this); } 这里,弱指针在析构的时候,与强指针析构不一样,它直接就调用目标对象的weakref_impl对象的decWeak函数来减少弱引用计数了,当弱引用计数为0的时候,就会根据在目标对象的标志位(0、OBJECT_LIFETIME_WEAK或者OBJECT_LIFETIME_FOREVER)来决定是否要delete目标对象,前面我们已经介绍过了,这里就不再介绍了。 分析到这里,弱指针还没介绍完,它最重要的特性我们还没有分析到。前面我们说过,弱指针的最大特点是它不能直接操作目标对象,这是怎么样做到的呢?秘密就在于弱指针类没有重载*和->操作符号,而强指针重载了这两个操作符号。但是,如果我们要操作目标对象,应该怎么办呢,这就要把弱指针升级为强指针了: template<typenameT> sp<T>wp<T>::promote()const { returnsp<T>(m_ptr,m_refs); } 升级的方式就使用成员变量m_ptr和m_refs来构造一个强指针sp,这里的m_ptr为指目标对象的一个指针,而m_refs则是指向目标对象里面的weakref_impl对象。 我们再来看看这个强指针的构造过程: template<typenameT> sp<T>::sp(T*p,weakref_type*refs) :m_ptr((p&&refs->attemptIncStrong(this))?p:0) { } 主要就是初始化指向目标对象的成员变量m_ptr了,如果目标对象还存在,这个m_ptr就指向目标对象,如果目标对象已经不存在,m_ptr就为NULL,升级成功与否就要看refs->attemptIncStrong函数的返回结果了: boolRefBase::weakref_type::attemptIncStrong(constvoid*id) { incWeak(id); weakref_impl*constimpl=static_cast<weakref_impl*>(this); int32_tcurCount=impl->mStrong; LOG_ASSERT(curCount>=0,"attemptIncStrongcalledon%pafterunderflow", this); 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){ boolallow; if(curCount==INITIAL_STRONG_VALUE){ //Attemptingtoacquirefirststrongreference...thisisallowed //iftheobjectdoesNOThavealongerlifetime(meaningthe //implementationdoesn'tneedtoseethis),oriftheimplementation //allowsittohappen. allow=(impl->mFlags&OBJECT_LIFETIME_WEAK)!=OBJECT_LIFETIME_WEAK ||impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG,id); }else{ //Attemptingtorevivetheobject...thisisallowed //iftheobjectDOEShavealongerlifetime(sowecansafely //calltheobjectwithonlyaweakref)andtheimplementation //allowsittohappen. allow=(impl->mFlags&OBJECT_LIFETIME_WEAK)==OBJECT_LIFETIME_WEAK &&impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG,id); } if(!allow){ decWeak(id); returnfalse; } curCount=android_atomic_inc(&impl->mStrong); //Ifthestrongreferencecounthasalreadybeenincrementedby //someoneelse,theimplementorofonIncStrongAttempted()isholding //anunneededreference.SocallonLastStrongRef()heretoremoveit. //(No,thisisnotpretty.)NotethatweMUSTNOTdothisifwe //areinfactacquiringthefirstreference. if(curCount>0&&curCount<INITIAL_STRONG_VALUE){ impl->mBase->onLastStrongRef(id); } } impl->addWeakRef(id); impl->addStrongRef(id); #ifPRINT_REFS LOGD("attemptIncStrongof%pfrom%p:cnt=%d\n",this,id,curCount); #endif if(curCount==INITIAL_STRONG_VALUE){ android_atomic_add(-INITIAL_STRONG_VALUE,&impl->mStrong); impl->mBase->onFirstRef(); } returntrue; } 这个函数的作用是试图增加目标对象的强引用计数,但是有可能会失败,失败的原因可能是因为目标对象已经被delete掉了,或者是其它的原因,下面会分析到。前面我们在讨论强指针的时候说到,增加目标对象的强引用计数的同时,也会增加目标对象的弱引用计数,因此,函数在开始的地方首先就是调用incWeak函数来先增加目标对象的引用计数,如果后面试图增加目标对象的强引用计数失败时,会调用decWeak函数来回滚前面的incWeak操作。 这里试图增加目标对象的强引用计数时,分两种情况讨论,一种情况是此时目标对象正在被其它强指针引用,即它的强引用计数大于0,并且不等于INITIAL_STRONG_VALUE,另一种情况是此时目标对象没有被任何强指针引用,即它的强引用计数小于等于0,或者等于INITIAL_STRONG_VALUE。 第一种情况比较简单,因为这时候说明目标对象一定存在,因此,是可以将这个弱指针提升为强指针的,在这种情况下,只要简单地增加目标对象的强引用计数值就行了: while(curCount>0&&curCount!=INITIAL_STRONG_VALUE){ if(android_atomic_cmpxchg(curCount,curCount+1,&impl->mStrong)==0){ break; } curCount=impl->mStrong; } 当我们在这里对目标对象的强引用计数执行加1操作时,要保证原子性,因为其它地方也有可能正在对这个目标对象的强引用计数执行加1的操作,前面我们一般是调用android_atomic_inc函数来完成,但是这里是通过调用android_atomic_cmpxchg函数来完成,android_atomic_cmpxchg函数是体系结构相关的函数,在提供了一些特殊的指令的体系结构上,调用android_atomic_cmpxchg函数来执行加1操作的效率会比调用android_atomic_inc函数更高一些。函数android_atomic_cmpxchg是在system/core/include/cutils/atomic.h文件中定义的一个宏: intandroid_atomic_release_cas(int32_toldvalue,int32_tnewvalue, volatileint32_t*addr); #defineandroid_atomic_cmpxchgandroid_atomic_release_cas 它实际执行的函数是android_atomic_release_cas,这个函数的工作原理大概是这样的:如果它发现*addr == oldvalue,就会执行*addr = newvalue的操作,然后返回0,否则什么也不做,返回1。在我们讨论的这个场景中,oldvalue等于curCount,而newvalue等于curCount + 1,于是,在*addr == oldvalue的条件下,就相当于是对目标对象的强引用计数值增加了1。什么情况下*addr != oldvalue呢?在调用android_atomic_release_cas函数之前,oldvalue和值就是从地址addr读出来的,如果在执行android_atomic_release_cas函数的时候,有其它地方也对地址addr进行操作,那么就会有可能出现*addr != oldvalue的情况,这时候就说明其它地方也在操作目标对象的强引用计数了,因此,这里就不能执行增加目标对象的强引用计数的操作了,它必须要等到其它地方操作完目标对象的强引用计数之后再重新执行,这就是为什么要通过一个while循环来执行了。 第二种情况比较复杂一点,因为这时候目标对象可能还存在,也可能不存了,这要根据实际情况来判断。如果此时目标对象的强引用计数值等于INITIAL_STRONG_VALUE,说明此目标对象还从未被强指针引用过,这时候弱指针能够被提升为强指针的条件就为: allow=(impl->mFlags&OBJECT_LIFETIME_WEAK)!=OBJECT_LIFETIME_WEAK ||impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG,id); 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966569,如需转载请自行联系原作者