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,对象将长生不老,彻底摆脱强弱引用计数的控制。所以你要在适当的时候杀死这些“老妖精”,免得她祸害“人间”。