一、
RefBase是引用计数的类,它封装了引用计数的技术细节。包括增加强引用计数,增加弱引用计数,减少强引用计数,减少弱引用计数等等。
但是对RefBase的具体操作,却是委托给了weakref_impl这个类。就好比RefBase是一块农田,对农田的操作由weakref_impl这个农夫来操作。weakref_impl是RefBase内部定义的一个类weakref_type的派生类。强和弱引用计数的实际数据也是放在weakref_impl中的.下面是 weakref_impl这个类的定义
class RefBase::weakref_impl 表示它是RefBase的内部类
public RefBase::weakref_type 表示它继承RefBase的内部类weakref_type
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
std::atomic<int32_t> mStrong; //强引用计数
std::atomic<int32_t> mWeak; //弱引用计数
RefBase* const mBase; //实际对象的影子
std::atomic<int32_t> mFlags;
...
...
...
};
注意RefBase* const mBase这个成员. 任意一个类,当他需要使用引用计数时,都需要继承RefBase这个类。在weakref_impl中设置这么一个RefBase类型的成员是什么用意?我们先看一下RefBase的构造函数,因为实际对象在构造时
父类构造必须被率先调用,c++的多态!RefBase::RefBase():mRefs(new weakref_impl(this))
{
//代码略
}
请看初始化代码mRefs(new weakref_impl(this))也就是说父类在构造的时候,先new了一个new weakref_impl,去初始化mRefs成员,这说明,RefBase这块农田内置了一个农夫weakref_impl,且这个农田的农夫在构造时诞生了!
并且用的是this作为参数,这个this可是实际对象哦,子类构造导致基类构造执行,传递的this自然是子类,也就是实际对象!既然实际对象作为农夫的构造参数,那么我们去看看这个weakref_impl的构造参数,他是怎么处理this的。
weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
, mFlags(0)
{
}
weakref_impl的mBase成员保存的就是传递的this啊,前面说过这个this就是实际对象哦。所以,可以说weakref_impl保存了一个实际对象的引用.
现在可以看sp。sp使用了模板技术,先看一下sp的构造函数
template<typename T>
sp<T>::sp(T* other):m_ptr(other)
{
if(other)other->incStrong(this);
}
T就代表着实际对象,已知实际对象是继承了RefBase的。m_ptr是sp的成员,类型就是T*,由此可见,强引用类sp内部保存了一个实际对象的引用,由成员m_ptr保存。且sp构造时调用了other->incStrong(this);
实际上就是RefBase的方法incStrong(),不就是多态吗!other是实际对象,它继承RefBase自然就调用RefBase的incStrong()!
但是强和弱引用计数的实际数据是放在weakref_impl中的,那么RefBase的方法incStrong()一旦被调用,又是如何影响到强和弱引用计数的数值变化呢?我们看看这个incStrong()函数的具体实现
void RefBase::incStrong(const void *id)
{
weakref_impl *const refs = mRefs;
refs->addWeakRef(id); //空函数
refs->incWeak(id);
refs->addStrongRef(id);
...
...
...
}
原来在IncStrong()中生成了一个weakref_impl的指针,而这个指针等价于RefBase的成员mRefs. 前面说过了,mRefs就是指向着weakref_impl的。从而通过这个指针调用weakref_impl内部的方法实现了实际数值的改变。由此可见确实是weakref_impl在实际操作引用计数的数值。
RefBase::RefBase():mRefs(new weakref_impl(this))
{
//再看一眼RefBase的构造把,再次确定一下mRefs就是指向weakref_impl
}
现在脉络有点清晰了。sp在构造的时候,首先将实际对象给保存起来了。
而实际对象因为它继承自RefBase, RefBase内部又保存着weakref_impl这个农夫的对象指针(农夫负责操作RefBase这块农田,农夫内部保存着实际的引用计数数值和实际对象). 所有虽然sp只保存了一个实际对象,但是只要sp愿意,它也可以定位到农夫,不是吗?小知识点:weakref_impl在修改引用计数数值的时候,都是使用了原子级操作,android_atomic_inc() 或者 android_atomic_dec(),这2个原子级操作的函数,返回值都是旧值,这一点要注意。比如android_atomic_inc()操作前旧值是1,则函数成功新值是2,但是返回值是旧值1.
根据incStrong()代码中的实际代码,其实不光令强引用计数+1也令弱引用计数+1。其他的暂时不关注了,只记住一点,sp的构造函数令强引用计数+1,弱引用计数同时+1;
顺便再来看一下wp的构造,wp是弱引用计数,构造好几个,随便挑一个来看看
templete<typename T>
wp<T>::wp(const sp<T>& other):m_ptr(other.m_ptr)
{
if(m_ptr)
m_refs = m_ptr->createWeak(this);
}
m_ptr是T* 类型,同上面介绍sp时细述的一样,那这个createWeak()不出意外就是RefBase的方法咯,过去看看这个createWeak的实现
RefBase::weakref_type* RefBase::createWeak(const void*id) const
{
mRef->incWeak(id);
return mRef;
}
哈,基类RefBase的mRef就是weakref_impl*类型嘛,mRef->incWeak()不就是调用weakref_impl的方法吗?绕一圈还是调用weakref_impl的incWeak()去给弱引用计数+1,并且返回weakref_impl* 给wp的m_Refs成员。
现在对sp和wp能看出一点区别了:
sp 有T* m_ptr 成员,这个成员就是实际对象的引用,但是这个m_ptr继承自RefBase,等价于m_ptr也包含了weakref_impl*;
wp 有weakref_impl m_Refs成员,也有T* m_ptr,已知m_ptr是保存了实际对象的引用的,m_Refs代表着weakref_impl;
sp会令强引用计数+1,弱引用计数+1
wp会令弱引用计数+1
构造看完了,来看看wp的析构函数
wp<T>::~wp()
{
if(m_ptr)m_refs->decWeak(this);
}
去调用weakref_impl的decWeak(),参数是wp的this,
void RefBase::weakref_type::decWeak(const void* id)
{
//原子级操作减少弱引用计数,代码略
if(弱引用!=1)return; //弱引用没到0,就直接返回了
//如果走到这里,弱引用计数应该是为0,
if(xxx)//如果对象不受弱引用控制
{
if(强引用==初始值)
delete impl->mBase; //干掉实际对象
else
delete impl; //农夫给干掉了
}
else
{
if(xxx) //如果既不受强引用控制也不受弱引用控制
delete impl->mBase; //干掉实际对象
}
}
可以看出来,在函数先进行一次原子级操作,将弱引用计数-1之后。当弱引用计数==0的情况下,函数才回继续干活,否则函数立刻返回。wp的析构时是有可能干掉实际对象或者农夫impl的。
template<typename T>
sp<T>::~sp()
{
if(m_ptr)m_ptr->decStrong(this);
}
根据多态,实际上调用的是RefBase的decStrong()
void RefBase::decStrong(const void *id)
{
//原子级操作,减少强引用计数,代码略过
if(xx) //如果强引用计数==0
{
if(不受弱引用控制)
delete this; //干掉实际对象,因为m_pter调用的decStrong().this就是实际对象
}
refs->decWeak();
}
如果干掉实际对象,已知实际对象T是继承RefBase的类,则RefBase这个父类会率先析构,我们继续看看整个的析构流程
RefBase::~RefBase()
{
if(mRefs->mWeak==0)
{
delete mRefs;
}
}
RefBase的析构函数中身,在满足条件的情况下将weakref_impl对象mRefs也一块干掉了。
这个满足条件为什么是mRefs->mWeak==0,即弱引用计数为0呢?已知sp会令强引用+1&弱引用+1,而wp会令弱引用+1,
当sp和wp一块使用时,有可能令弱引用数值 > 强引用数值,所以要判断一下,当强引用==0时,且弱引用==0才可以释放weakref_impl对象mRefs.
此外在强引用==0的时候,依然有可能存在其他的弱指针通过weakref_impl在引用实际对象,所以必须弱引用==0才可以析构weakref_impl
在decStrong()中为何又调用refs->decWeak()呢?
我们再捋一下流程:
因为如果对象生命周期仅受到强引用计数控制,在decStrong()中会先干掉实际对象,而实际对象析构时跑到RefBase的析构中,顺带着把weakref_impl也给干掉了,
皆大欢喜,农田农夫都没了,sp也没有了,实际对象也没有了,清理的很干净。虽然decWeak()在函数底部被调用,但是它不做事,进去就返回了。如果对象生命周期受到弱引用计数控制,则decStrong()中干掉实际对象的代码就不会得到执行,流程向下跑到decWeak()。 这时候decWeak()就不会立刻返回啦,它要做事,一旦发现弱引用也是0,强应用也是0,就会干掉实际对象,同上实际对象析构会顺带 析构RefBase,RefBase析构顺带析构weakref_impl,此时也是农田农夫没有了,wp自身也被析构没有了,实际对象也没有了,清理的也很干净。而 如果弱引用=0,而强引用有效,则仅仅干掉weakref_impl,实际对象的清理工作显然交给sp去操心了
很复杂很绕,分析的应该还不是很完整,但是学到不少,尤其是之前学类的时候,完全没有注意到的细节,很有帮助!