Boost和Loki是应用比较广泛的引用计数方案,Android提供了另外一个引用计数方案,就是sp、wp和Refbase组合。
强引用和弱引用区别
在Android里面,sp是强引用,它是应用最多的引用形式,而且后面的分析,我们将知道,强引用直接管理着对象的销毁;wp是弱引用,弱引用的用途是能够对某个对象进行引用,但是即使该对象弱引用还存在,这个对象也可能会被销毁。弱引用看上去更复杂一些,以下是在网上摘录的一句话,“弱引用适合那些数据成员特别多,而且重新创建又相对容易的类,也就是俗称的胖子类,建立弱引用可以引用对象,但也不阻止其被垃圾回收,在内存的使用方面取得一定的平衡”。还是看看实际的例子比较好,如Android Framework/base/services/camera/libcamera/cameraservice.cpp中的一段代码:
[html]
sp<Client> client;
if (cameraId < 0 || cameraId >= mNumberOfCameras) {
LOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).",
callingPid, cameraId);
return NULL;
}
Mutex::Autolock lock(mServiceLock);
if (mClient[cameraId] != 0) {
client = mClient[cameraId].promote();
if (client != 0) {
if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) {
LOG1("CameraService::connect X (pid %d) (the same client)",
callingPid);
return client;
} else {
LOGW("CameraService::connect X (pid %d) rejected (existing client).",
callingPid);
return NULL;
}
}
mClient[cameraId].clear();
}
代码说明
该代码是函数connect的一部分,connect函数是客户端camera.cpp连接cameraservice以获得获得服务。Android的cameraservice机制是可以支持多个摄像头同时供多个客户端使用,并且,每个摄像头可能会被多个客户端轮流使用(你可以想象,在不关闭摄像头的情况下,两个应用程序交替显示一个摄像头的图像,如果帧数足够多,那么这个摄像头采集的数据就可以同时供两个应用程序顺畅显示)。那么应用程序A和B是如何交替使用呢,就是分别调用service提供的connect函数,而mClient数组就是service部分将具体的某个摄像头和某个具体的应用程序挂钩的变量(该变量就是CameraService::Client类对象),从这也可以看出,一个时间点,一个摄像头只能同时供一个应用程序使用。所以,在交替切换过程中,就伴随着CameraService::Client类对象的创建和销毁。
这个时候之所以使用弱引用更方便,是因为如果程序A正在使用摄像头,如果按照强引用,程序B是无法从程序A抢到摄像头的;而如果是弱引用,即使程序A占用摄像头,程序B依然可以将程序A在CameraService的资源释放,然后创建自己的资源,然后将建立自己的弱引用。这确实很巧妙。
所以弱引用适合用在资源可以被多个程序同时交替使用,时刻可能被其他程序抢走资源的情况,而且资源可以自己负责,也可以自己不负责的情况。
sp如何管理强引用计数
sp指的是对象的强引用,一般定义如下,sp<Camera> mCamera,那么,mCamera对应的实际对象就交由sp进行管理了,当引用计数为0时,该实际对象就会被销毁。那么在什么情况要增加引用计数,在什么情况要减少引用计数呢?
以下是增加引用计数的情况:
1. 指针赋值拷贝
如:
sp<Camera> mCamera = new camera();
这是一个用得非常多的情况,当new一个对象时,就将该对象的指针交由sp进行管理了,那么这个时候sp就应该增加这个new对象的引用计数,当然这个时候应该是1;
Camera* c = new camera();
sp<Camera> mCamera = c;
该情况是将一个已经new好了的对象指针赋值给mCamera,那么这个时候sp就应该增加这个new对象的引用计数。
sp相应代码如下:
[html]
template<typename T>
sp<T>& sp<T>::operator = (T* other)
{
if (other) other->incStrong(this);
if (m_ptr) m_ptr->decStrong(this);
m_ptr = other;
return *this;
}
其中m_ptr是对象的指针,该代码的逻辑如下
# 如果被复制的对象存在,即other不为NULL,那么就将该对象的引用计数加1;
# 如果原来m_ptr已经指向了某个对象,那么就将某个对象的引用计数减1;
# 然后将other赋值给m_ptr,从此,m_ptr就指向了刚刚设置的对象;
2. 引用赋值拷贝
如:
sp<Camera> c = new camera();
sp<Camera> mCamera = c;
将一个对象c赋值给mCamera,sp对应代码如下:
[html]
template<typename T>
sp<T>& sp<T>::operator = (const sp<T>& other) {
T* otherPtr(other.m_ptr);
if (otherPtr) otherPtr->incStrong(this);
if (m_ptr) m_ptr->decStrong(this);
m_ptr = otherPtr;
return *this;
}
# 将被赋值对象other的对象引用计数加1;
# 将sp对应对象的引用计数减1;
# 将sp的对象改为other对象;
3. 指针构造拷贝
如:
sp<Camera> mCamera(new camera());
sp相应代码如下:
[html]
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other)
{
if (other) other->incStrong(this);
}
# 将被赋值对象other的对象指针m_ptr赋值给当前sp的m_ptr;
# 如果被赋值对象other的对象存在,则将该对象的引用计数加1;
4. 引用构造拷贝
如:
sp<Camera> c = new camera();
sp<Camera> mCamera(c);
sp相应代码如下:
[html]
template<typename T>
sp<T>::sp(const sp<T>& other)
: m_ptr(other.m_ptr)
{
if (m_ptr) m_ptr->incStrong(this);
}
# 将被赋值对象other的对象指针m_ptr赋值给当前sp的m_ptr;
# 如果被赋值对象other的对象存在,则将该对象的引用计数加1;
以下是减少引用计数的情况:
1. sp析构
如:
{
sp<Camera> c = new camera();
/* do something*/
}
由于c是局部变量,那么,在大括号结束前,c将被析构。
sp对应代码如下:
[html]
template<typename T>
sp<T>::~sp()
{
if (m_ptr) m_ptr->decStrong(this);
}
# 析构并不代表对象真的销毁,因为可能将该对象赋值给了其他sp管理,所以,析构函数只是将对象的引用计数减1;
2. 指针赋值拷贝和引用赋值拷贝
在上面的增加引用计数已经介绍,重新设置sp管理的对象,必须将现在管理的对象的引用计数减1
3. 清除
如:
sp<Camera> mCamera = new camera();