5.2.3 第三板斧——破解生死魔咒
1.延长生命的魔咒
RefBase为我们提供了一个这样的函数:
extendObjectLifetime(int32_t mode)
另外还定义了一个枚举:
enum{
OBJECT_LIFETIME_WEAK=0x0001,
OBJECT_LIFETIME_FOREVER=0x0003
};
注意:FOREVER的值是3,用二进制表示是B11,而WEAK的二进制是B01,也就是说FOREVER包括了WEAK的情况。
上面这两个枚举值,是破除强弱引用计数作用的魔咒。先观察flags为OBJECT_LIFETIME_WEAK的情况,见下面的例子。
[—>例子3]
class A:public RefBase
{
public A()
{
extendObjectLifetime(OBJECT_LIFETIME_WEAK);//在构造函数中调用。
}
}
int main()
{
A*pA=new A();
wp<A>wpA(pA);//弱引用计数加1。
{
sp<A>spA(pA)//sp后,结果是强引用计数为1,弱引用计数为2。
}
……
}
sp的析构将直接调用RefBase的decStrong,它的代码如下所示:
[—>RefBase.cpp]
void RefBase:decStrong(const void*id)const
{
weakref_impl*const refs=mRefs;
refs->removeStrongRef(id);
const int32_t c=android_atomic_dec(&refs->mStrong);
if(c==1){//上面进行原子操作后,强引用计数为0
const_cast<RefBase*>(this)->onLastStrongRef(id);
//注意这句话。如果flags不是WEAK或FOREVER的话,将delete数据对象。
//现在我们的flags是WEAK,所以不会delete它。
if((refs->mFlags&OBJECT_LIFETIME_WEAK)!=OBJECT_LIFETIME_WEAK){
delete this;
}
}
refs->removeWeakRef(id);
refs->decWeak(id);//调用前弱引用计数是2。
}
然后调用影子对象的decWeak。再来看它的处理,代码如下所示:
[—>RefBase.cpp:weakref_type的decWeak()函数]
void RefBase:weakref_type:decWeak(const void*id)
{
weakref_implconst impl=static_cast<weakref_impl>(this);
impl->removeWeakRef(id);
const int32_t c=android_atomic_dec(&impl->mWeak);
if(c!=1)return;//c为2,弱引用计数为1,直接返回。
/*
假设我们现在到了例子中的wp析构之处,这时也会调用decWeak,在调用上面的原子减操作后c=1,弱引用计数变为0,此时会继续往下运行。由于mFlags为WEAK,所以不满足if的条件。
*/
if((impl->mFlags&OBJECT_LIFETIME_WEAK)!=OBJECT_LIFETIME_WEAK){
if(impl->mStrong==INITIAL_STRONG_VALUE)
delete impl->mBase;
else{
delete impl;
}
}else{//flag为WEAK,满足else分支的条件。
impl->mBase->onLastWeakRef(id);
/*
由于flags值满足下面这个条件,所以实际对象会被delete,根据前面的分析可知,实际对象的delete会检查影子对象的弱引用计数,如果它为0,则会把影子对象也delete掉。
由于影子对象的弱引用计数此时已经为0,所以影子对象也会被delete。
*/
if((impl->mFlags&OBJECT_LIFETIME_FOREVER)!=OBJECT_LIFETIME_FOREVER){
delete impl->mBase;
}
}
}
2.LIFETIME_WEAK的魔力
看完上面的例子,我们发现什么了?
在LIFETIME_WEAK的魔法下,强引用计数为0,而弱引用计数不为0的时候,实际对象没有被delete!只有当强引用计数和弱引用计数同时为0时,实际对象和影子对象才会被delete。
3.魔咒大揭秘
至于LIFETIME_FOREVER的破解,就不用再来一斧子了,我直接给出答案:
flags为0,强引用计数控制实际对象的生命周期,弱引用计数控制影子对象的生命周期。强引用计数为0后,实际对象被delete。所以对于这种情况,应记住的是,使用wp时要由弱生强,以免收到segment fault信号。
flags为LIFETIME_WEAK,强引用计数为0,弱引用计数不为0时,实际对象不会被delete。当弱引用计数减为0时,实际对象和影子对象会同时被delete。这是功德圆满的情况。
flags为LIFETIME_FOREVER,对象将长生不老,彻底摆脱强弱引用计数的控制。所以你要在适当的时候杀死这些“老妖精”,免得她祸害“人间”。