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