5.2.3 第三板斧–破解生死魔咒
最后更新于:2022-04-02 05:49:47
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
{
publicA()
{
extendObjectLifetime(OBJECT_LIFETIME_WEAK);//在构造函数中调用
}
}
int main()
{
A *pA =new A();
wp wpA(A);//弱引用计数加1
{
spspA(pA) //sp后,结果是强引用计数为1,弱引用计数为2
}
....
}
~~~
sp的析构将直接调用RefBase的decStrong,它的代码如下所示:
**RefBase.cpp**
~~~
void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->removeStrongRef(id);
constint32_t c = android_atomic_dec(&refs->mStrong);
if (c== 1) { //上面原子操作后,强引用计数为0
const_cast(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_impl* const impl = static_cast(this);
impl->removeWeakRef(id);
constint32_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,对象将长生不老,彻底摆脱强弱引用计数的控制。所以你要在适当的时候杀死这些老妖精,免得她祸害“人间”。
';