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();
mCamera.clear();
意思設置mCamera不再管理任何對象。
sp代碼如下:
[html]
template<typename T>
void sp<T>::clear()
{
if (m_ptr) {
m_ptr->decStrong(this);
m_ptr = 0;
}
}
# 將管理的對象引用計數減1;
# 將m_ptr置空;
wp如何管理弱引用計數
對於wp的處理和sp基本相同,代碼部分就不做細致分析了。
我們可以記住,弱引用計數可以通過promote函數升級為強引用。
Refbase機制
我們在上面一節可以看到,sp和wp就是調用Refbase提供的incStrong、decStrong、incWeak和decWeak設置對象的引用計數,那麼Refbase是如何實現的呢,以及什麼情況下才會將對象銷毀呢?
如:
{
sp<Camera> mCamera = new camera();
}
上面如此簡單的代碼,sp先將camera對象的引用計數加1,然後由於作用域結束,所以mCamera被析構,析構時會將camera對象的引用計數減1,那麼這個時候,這個對象的引用計數恰好是0,那麼這個時候對象會被銷毀嗎?我們還是看看Refbase是如何被構造,如何管理引用計數,如何管理對象的。
構造
[html]
RefBase::RefBase()
: mRefs(new weakref_impl(this))
{
// LOGV("Creating refs %p with RefBase %p\n", mRefs, this);
}
很簡單,只是又創建了一個weakref_impl,從這個名字就可以看出它是處理弱引用的類,其實除了弱引用,它還處理強引用。我們還是根據《心得 - 如何讀大型代碼和寫代碼》所描述的方法,我們只需要從宏觀上了解weakref_impl的作用,我們現在正在研究Refbase,所以,只要在調用weakref_impl的地方,我們只要能夠猜出它完成了什麼功能即可,不進行深入分析。
# 創建了weakref_impl對象,並將this指針傳遞過去,看來weakref_impl對象要通過this指針來回調Refbase的某些方法;
增加強引用計數
mCamera指針賦值拷貝時會增加引用計數,代碼如下:
[html]
void RefBase::incStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->addWeakRef(id);
refs->incWeak(id);
refs->addStrongRef(id);
const int32_t c = android_atomic_inc(&refs->mStrong);
LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
LOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
if (c != INITIAL_STRONG_VALUE) {
return;
}
android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
const_cast<RefBase*>(this)->onFirstRef();
}
# 增加弱引用,可見,使用強引用時,還伴隨著一個弱引用;
# 增加強引用;
# 如果是第一次增加強引用計數,那麼就會調用onFirstRef函數,我們在代碼經常會看到onFirstRef函數,通過該函數,經常會做些初始化方面的工作;
減少強引用計數
當mCamera析構時就會減少強引用計數,代碼如下:
[html]
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 PRINT_REFS
LOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
if (c == 1) {
const_cast<RefBase*>(this)->onLastStrongRef(id);
if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
delete this;
}
}
refs->removeWeakRef(id);
refs->decWeak(id);
}
# 減小強引用計數;
# 銷毀對象,如果減小強引用計數之前,強引用計數是1,也就是減小後,強引用計數是0,那麼這個時候就delete this了;
# 減小弱引用計數,看來弱引用計數和對象實際存不存在沒有必然的關系;
weakref_type功能
我們通過Refbase看出,weakref_type實際管理著強引用和弱引用兩個計數。
構造
[html]
weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
, mFlags(0)
{
}
# 初始化,mStrong(強引用計數)、mWeak(弱引用計數)、mBase(管理的Refbase)
另外,和構造函數一起的還有
void addStrongRef(const void* /*id*/) { }
void removeStrongRef(const void* /*id*/) { }
void addWeakRef(const void* /*id*/) { }
void removeWeakRef(const void* /*id*/) { }
void printRefs() const { }
void trackMe(bool, bool) { }
它們什麼都沒有做。
增加弱引用計數
[html]
void RefBase::weakref_type::incWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->addWeakRef(id);
const int32_t c = android_atomic_inc(&impl->mWeak);
LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
# 通過原子操作增加mWeak;
減少弱引用計數
[html]
void RefBase::weakref_type::decWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->removeWeakRef(id);
const int32_t c = android_atomic_dec(&impl->mWeak);
LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
if (c != 1) return;
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
if (impl->mStrong == INITIAL_STRONG_VALUE)
delete impl->mBase;
else {
// LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
delete impl;
}
} else {
impl->mBase->onLastWeakRef(id);
if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
delete impl->mBase;
}
}
}
# 通過原子操作減少弱引用計數
# 當弱引用計數為0時,那麼就會根據某種條件delete this或者delete Refbase
我們在減少強引用和弱引用,看到了OBJECT_LIFETIME_FOREVER和OBJECT_LIFETIME_WEAK兩個標志位,那麼他們是干什麼的呢。www.2cto.com
OBJECT_LIFETIME_FOREVER可能代碼裡面沒有設置為這個標識符的類,而OBJECT_LIFETIME_WEAK只有Binder設置了,我們從名字上看出,OBJECT_LIFETIME_FOREVER指的是對象的生命是永遠,而OBJECT_LIFETIME_WEAK可能是生命時間按照弱引用計數來決定(一般都是通過強引用計數來決定)。
由於用得很少,所以,這兩個標志位對我們的應用而言沒有多大關系,但是,我們還是看看代碼怎麼特殊處理這兩個標志位。
在decStrong函數中,
if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
delete this;
}
意思是當標志位不是OBJECT_LIFETIME_WEAK時,才會銷毀對象。也就是如果標志位是OBJECT_LIFETIME_WEAK,那麼如果強引用為0時,也不會銷毀對象,看來銷毀對象的任務交給其他地方執行了。
還是看上面的decWeak函數,當標志位是OBJECT_LIFETIME_WEAK時,只有當標志位不是OBJECT_LIFETIME_FOREVER才會銷毀對象;當標志不是OBJECT_LIFETIME_WEAK時,如果強引用計數也為0,那麼就將對象銷毀,否則,將weakref_impl銷毀。