[TOC]
在Android系統(tǒng)中浆劲,Native層的代碼基本都是C++寫的,C++跟Java不一樣妙蔗,C++沒有垃圾回收機制茁彭,C++代碼中難于管理new出來對象的釋放,稍有不慎就造成內(nèi)存泄漏抽米。針對此問題特占,Android中提出了一套類似Java垃圾回收機制的智能指針,采用強指針sp(Strong Pointer)和弱指針wp(Weak Pointer)對目標(biāo)對象進行應(yīng)用云茸,實現(xiàn)對象的自動回收是目。下面我們將從C++的基礎(chǔ)知識入手,對Android的智能指針展開逐步的分析标捺。
1 相關(guān)基礎(chǔ)知識
Android的智能指針懊纳,巧妙的運用C++的基礎(chǔ)特性,實現(xiàn)對象的自動釋放亡容,我們就先來看看都用了C++的什么特性嗤疯。
1.1作用域
標(biāo)記變量的有效范圍。從作用域上來看闺兢,可以將對象分為全局對象茂缚、局部對象、靜態(tài)全局對象和靜態(tài)局部對象。
一般來說脚囊,局部變量的有效作用域從它的定義點開始龟糕,到和定義變量之前最鄰近的開括號配對的第一個閉括號,也就是說悔耘,作用域由變量所在的最近一對{}括號確定讲岁。
演示代碼1
void testScope() {
Sheep bigSheep; //局部對象
{
Sheep smallSheep; // 局部對象
}// smallSheep的作用域結(jié)束
}// bigSheep的作用域結(jié)束
1.2對象內(nèi)存空間的分配、釋放
從內(nèi)存分配空間來看衬以,可將對象分為棧對象和堆對象缓艳。棧對象在作用域結(jié)束后會自動釋放,而堆對象需要手動顯示的釋放看峻。
演示代碼2
void testMemoryMap() {
Sheep sheep; // 棧對象阶淘,testMemoryMap調(diào)用結(jié)束后自動釋放
Sheep* pSheep; // 堆對象,需要手動釋放
delete pSheep; // 釋放pSheep指向的對象
pSheep = 0; //將pSheep指向NULL备籽,防止造成野指針
}
圖1-1是內(nèi)存空間的分配示意圖舶治。
1.3原子操作函數(shù)
定義在system/core/libcutils/Atomic.c车猬,依賴于具體的芯片平臺霉猛。原子操作函數(shù)特點:線程安全,返回舊值珠闰。如下惜浅,Android原子操作函數(shù)一覽表。
函數(shù)名 | 功能 |
---|---|
int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr) | 加函數(shù)伏嗜,返回舊值坛悉,*ptr = *ptr + increment |
int32_t android_atomic_inc(volatile int32_t *addr) | 自增操作,返回舊值承绸,*ptr = *ptr + 1 |
int32_t android_atomic_dec(volatile int32_t *addr) | 自減操作裸影, 返回舊值,*ptr = *ptr - 1 |
int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) | 位與操作军熏,返回舊值轩猩,*ptr = *ptr & value |
int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) | 位或操作,返回舊值荡澎,*ptr = *ptr or value |
int android_atomic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t *ptr) | 如果addr == oldvalue均践,就會執(zhí)行addr = new_value的操作,然后返回0摩幔,否則返回1 |
1.4引用計數(shù)的原理
棧對象在生命周期彤委,即作用域結(jié)束后自動釋放,所以我們這里討論的是堆對象的引用或衡,也就是指針對象焦影。
圖1-2是指針引用時车遂,利用引用數(shù)管理實際對象釋放的原理圖。
引用計數(shù)的原理很簡單偷办,當(dāng)引用某個對象時艰额,使其引用數(shù)+1澄港;引用結(jié)束時椒涯,使其引用數(shù)-1;當(dāng)引用數(shù)為0時回梧,delete掉實際對象废岂。
根據(jù)前面的原理,引出兩個問題狱意,帶著這兩個問題湖苞,我們來看看Android是怎么實現(xiàn)的。
- 怎么管理引用數(shù)详囤?
- 怎么判斷引用開始和結(jié)束财骨,怎么增減引用數(shù)?
2 Android智能指針原理
Android設(shè)計了基類RefBase藏姐,用以管理引用數(shù)隆箩,所有類必須從RefBase派生,RefBase是所有對象的始祖羔杨。
設(shè)計模板類sp捌臊、wp,用以引用實際對象兜材,sp強引用和wp弱引用理澎。sp、wp聲明為棧對象曙寡,作用域結(jié)束時糠爬,自動釋放,自動調(diào)用析構(gòu)函數(shù)举庶。因此执隧,可以在sp、wp的構(gòu)造函數(shù)中灯变,增引用數(shù)殴玛;在析構(gòu)函數(shù)中,減少引用計數(shù)添祸。
專門設(shè)計weakref_impl類滚粟,該類是RefBase的內(nèi)部類,用來做真正引用數(shù)管理刃泌,創(chuàng)建實際對象時凡壤,同時創(chuàng)建一個mRefs對象署尤。不管是強引用和弱應(yīng)用,都由mRefs來管理亚侠。
圖2-1 展示Android智能指針的關(guān)系類圖曹体。
看了智能指針的實現(xiàn)原理,我們來看看具體的實現(xiàn)是什么樣的硝烂。
2 智能指針的實現(xiàn)
根據(jù)前面的原理箕别,Android設(shè)計了強引用sp和弱引用wp,故實際對象的釋放滞谢,可分為強引用控制和弱引用控制串稀。所謂強引用控制,指的是強引用數(shù)mStrong為0時狮杨,釋放實際對象母截;弱引用控制,則指的是弱引用數(shù)mWeak為0時橄教,才釋放實際對象清寇。
下面將結(jié)合一些實例,分析具體的實現(xiàn)护蝶。我們先來看一段代碼實例华烟。
代碼實例1
class Sheep: public RefBase { // 羊年,定義Sheep從RefBase派生
public:
Sheep() : RefBase() { }// 可顯示調(diào)用RefBase的構(gòu)造滓走,也可以不用
virtual ~Sheep() { }// 最好聲明為virtual垦江,以便從Sheep派生
};
void testSheep() {
Sheep* pSheep = new Sheep(); // new一個Sheep對象,這個是一個堆對象
{ // 限定sp的作用域
sp<Sheep> spSheep(pSheep); // spSheep是一個棧對象
{ // 限定wp的作用域
wp<Sheep> wpSheep(pSheep);
} // 調(diào)用wp的析構(gòu)函數(shù)
} // 調(diào)用sp的析構(gòu)函數(shù)搅方,實際對象pSheep已釋放比吭,若再使用pSheep將會出錯
}
3.1 RefBase構(gòu)造和mRefs
在實例代碼中,我們先定義了一個類Sheep姨涡,從RefBase派生衩藤,創(chuàng)建了一個實際對象,pSheep 指向?qū)嶋H對象涛漂。
在構(gòu)造Sheep的實際對象時赏表,將調(diào)RefBase的構(gòu)造函數(shù)。RefBase的構(gòu)造函數(shù)如下匈仗,在構(gòu)造函數(shù)中創(chuàng)建mRefs瓢剿。
weakref_impl從weakref_type派生,mRefs才是真正的“管家”悠轩。
[system/core/libutils/RefBase.cpp]
RefBase::RefBase()
: mRefs(new weakref_impl(this)) // 真正管理引用計數(shù)
{}
weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE) // 1<<28(268435456)间狂,為什么不是0?
, mWeak(0)
, mBase(base) // mBase指向?qū)嶋H對象
, mFlags(0) // 這個標(biāo)識很重要火架,指定是強應(yīng)用控制還是弱引用控制
{}
請注意這里的mFlags鉴象,默認(rèn)值為0忙菠,可通過修改這個標(biāo)志來設(shè)置是強引用控制,還是弱引用控制纺弊,代碼如下:
system/core/include/utils/RefBase.h]
enum {
OBJECT_LIFETIME_STRONG = 0x0000,
OBJECT_LIFETIME_WEAK = 0x0001,
OBJECT_LIFETIME_MASK = 0x0001
};
mFlags默認(rèn)為0牛欢,即OBJECT_LIFETIME_STRONG,強引用控制淆游。設(shè)置為OBJECT_LIFETIME_WEAK時傍睹,為弱引用控制』纾可以通過extendObjectLifetime函數(shù)修改焰望,代碼如下:
[system/core/libutils/RefBase.cpp]
void RefBase::extendObjectLifetime(int32_t mode)
{
android_atomic_or(mode, &mRefs->mFlags);
}
3.2 sp構(gòu)造
接下來骚亿,我們創(chuàng)建了一個sp對象spSheep已亥,這是一個棧對象,在其作用域結(jié)束后將自動釋放来屠,調(diào)用sp的析構(gòu)函數(shù)虑椎。
[system/core/include/utils/StrongPointer.h]
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other) {
if (other)
other->incStrong(this);
}
other指向真正的Sheep對象,在sp的構(gòu)造函數(shù)中俱笛,將other賦值給了sp的m_ptr捆姜,m-ptr就指向了真正的Sheep對象。
因而迎膜,other->incStrong(this)泥技,實際就是Sheep的父類RefBase的incStrong函數(shù),代碼如下:
[system/core/libutils/RefBase.cpp]
void RefBase::incStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->incWeak(id); // 調(diào)用incWeak函數(shù)
refs->addStrongRef(id); // 由DEBUG_REFS控制磕仅,release版本什么也不做
const int32_t c = android_atomic_inc(&refs->mStrong); // 強引用數(shù)+1珊豹,c為舊值
ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
if (c != INITIAL_STRONG_VALUE) { //判斷是否是第一次引用
return;
} // 第一次引用,refs->mStrong為1<<28 +1 (268435457)
android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
// refs->mStrong為1
refs->mBase->onFirstRef(); //第一次引用時調(diào)用
}
在incStrong函數(shù)中調(diào)用refs 的incWeak函數(shù)榕订,incWeak的代碼如下:
[system/core/libutils/RefBase.cpp]
void RefBase::weakref_type::incWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->addWeakRef(id); // 由DEBUG_REFS控制店茶,release版本什么也不做
const int32_t c __unused = android_atomic_inc(&impl->mWeak); //弱引用數(shù)+1
ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
OK, sp構(gòu)造完成劫恒,增加一次強引用贩幻。sp構(gòu)造完成后,mRefs的強引用數(shù)變?yōu)?两嘴,弱引用數(shù)也變?yōu)?丛楚;第一次強引用時,回調(diào)onFirstRef()憔辫。
3.3 wp構(gòu)造
接下來趣些,我們創(chuàng)建了一個wp對象wpSheep,這是一個棧對象螺垢,在其作用域結(jié)束后將自動釋放喧务,調(diào)用wp的析構(gòu)函數(shù)赖歌。
[system/core/include/utils/RefBase.h]
template<typename T>
wp<T>::wp(T* other)
: m_ptr(other)
{
if (other) m_refs = other->createWeak(this);
}
other指向真正的Sheep對象,在wp的構(gòu)造函數(shù)中功茴,將other賦值給了wp的m_ptr庐冯,m-ptr就指向了真正的Sheep對象。
因而坎穿,other-> createWeak (this)展父,實際就是Sheep的父類RefBase的createWeak函數(shù),代碼如下:
[system/core/libutils/RefBase.cpp]
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
mRefs->incWeak(id); // incWeak函數(shù)前面分析過玲昧,最終的結(jié)果就是弱引用數(shù)+1
return mRefs;
}
createWeak時栖茉,調(diào)用incWeak,最終的影響是弱引用數(shù)+1》跹樱現(xiàn)在吕漂,我們的實例中,強引用數(shù)為1尘应,弱引用數(shù)為2惶凝。
返回值為mRefs,也就是m_refs和mRefs指向同一個weakref_impl對象犬钢,而mRefs的mBase指向真正的對象Sheep苍鲜。因此此處的spSheep和wpSheep都是管理同一個真正的對象。
3.4 wp析構(gòu)
繼續(xù)看我們的實例代碼玷犹,現(xiàn)在wpSheep的作用域結(jié)束混滔,將調(diào)wp的析構(gòu)函數(shù),wp析構(gòu)函數(shù)的代碼如下:
[system/core/include/utils/RefBase.h]
template<typename T>
wp<T>::~wp()
{
if (m_ptr) m_refs->decWeak(this); // 調(diào)用decWeak函數(shù)
}
在wp的析構(gòu)函數(shù)中調(diào)用m_refs的decWeak函數(shù)歹颓。m_refs和mRef指向同一個weakref_impl對象坯屿,decWeak代碼如下:
[system/core/libutils/RefBase.cpp]
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); // 弱引用數(shù)-1,c為舊值
ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
if (c != 1) return; //c為舊值晴股,判斷是否是最后一次弱引用
// 記得前面我們說的愿伴,mFlags為0,我們并沒有改變它
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) ==
OBJECT_LIFETIME_STRONG) {
// 強引用控制电湘,是否釋放實際對象是根據(jù)強引用數(shù)
if (impl->mStrong == INITIAL_STRONG_VALUE) {
delete impl->mBase; // 根本就沒有強引用引用實際對象隔节,釋放實際對象
} else {
delete impl; // 釋放mRefs
}
} else {
impl->mBase->onLastWeakRef(id); //最后一次弱引用時調(diào)用
if ((impl->mFlags&OBJECT_LIFETIME_MASK) ==
OBJECT_LIFETIME_WEAK) {
delete impl->mBase; //弱引用控制,釋放實際對象
}
}
}
wp析構(gòu)寂呛,情況比較復(fù)雜怎诫,總的說來做了以下幾件事:
? 弱引用數(shù)減1。
? 最后一次弱引用時贷痪,強引用控制幻妓,釋放mRefs,若沒有強引用劫拢,釋放實際對象
? 最后一次弱引用時肉津,弱引用控制强胰,釋放實際對象
就我們的實例來看,此時妹沙,強引用數(shù)為1偶洋,弱引用數(shù)為1,并沒有任何釋放距糖。
3.5 sp析構(gòu)
在我們的實例代碼中玄窝,wp析構(gòu)完后,sp的作用域也就結(jié)束了悍引。此時恩脂,會調(diào)用sp的析構(gòu)函數(shù),代碼如下:
[system/core/include/utils/StrongPointer.h]
template<typename T>
sp<T>::~sp() {
if (m_ptr)
m_ptr->decStrong(this);
}
在析構(gòu)函數(shù)中調(diào)用m_ptr的decStrong函數(shù)趣斤,m_ptr指向?qū)嶋H對象俩块。此處為Sheep的父類RefBase的decStrong函數(shù),代碼如下:
[system/core/libutils/RefBase.cpp]
void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->removeStrongRef(id); // 由DEBUG_REFS控制唬渗,release版本什么也不做
const int32_t c = android_atomic_dec(&refs->mStrong); // 強引用數(shù)-1
ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
if (c == 1) { // c為舊值典阵,c為1時,即強引用數(shù)為0
refs->mBase->onLastStrongRef(id); //最后一次強引用結(jié)束時調(diào)用
if ((refs->mFlags&OBJECT_LIFETIME_MASK) ==
OBJECT_LIFETIME_STRONG) {
delete this; // 若是強引用控制镊逝,釋放實際對象,調(diào)實際對象的析構(gòu)函數(shù)
}
}
refs->decWeak(id);
}
refs的decWeak函數(shù)嫉鲸,前面wp析構(gòu)的時候分析過撑蒜,這里不再重復(fù)。sp析構(gòu)完成玄渗,主要完成以下工作:
? 強引用數(shù)減1座菠,弱引用數(shù)據(jù)減1。
? 最后一次強引用時藤树,若是強引用控制浴滴,釋放實際對象,釋放mRefs岁钓,調(diào)用onLastStrongRef函數(shù)升略。
在我們的代碼中,此時強引用數(shù)為0屡限,弱引用數(shù)為0品嚣,實際對象的析構(gòu)函數(shù)將被調(diào)用,mRefs將被釋放钧大。下面我看看實際對象的析構(gòu)函數(shù)翰撑。
3.6 RefBase析構(gòu)
實際對象的析構(gòu),先析構(gòu)RefBase啊央,RefBase的析構(gòu)函數(shù)如下:
[system/core/libutils/RefBase.cpp]
RefBase::~RefBase()
{
if (mRefs->mStrong == INITIAL_STRONG_VALUE) {
delete mRefs; // 沒有強引用引用實際對象眶诈,釋放mRefs
} else {
if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) !=
OBJECT_LIFETIME_STRONG) {
if (mRefs->mWeak == 0) {
delete mRefs; // 釋放mRefs指向的對象
}
}
}
// for debugging purposes, clear this.
const_cast<weakref_impl*&>(mRefs) = NULL; // mRefs指向0涨醋,避免野指針
}
OK,RefBase析構(gòu)分析完了逝撬,在RefBase的析構(gòu)函數(shù)中主要的工作就是釋放mRefs指向的weakref_impl的對象东帅。
到此,我們的實例代碼分析完成球拦,我們首先構(gòu)造一個Sheep對象靠闭,pSheep指向?qū)嶋H對象。再分別構(gòu)造一個強引用sp和一個弱引用wp坎炼,用以引用實際對象愧膀,實際對象的釋放就由sp和wp控制,我們并沒有顯示的釋放構(gòu)造的pSheep指向的實際對象谣光。
我們來看看實例代碼1中檩淋,對象的構(gòu)造和析構(gòu)Log:
[示例代碼1的Log]
D/ (13624): Sheep::------------------testSheep start--------------------------
D/ (13624): Sheep::Sheep constructor invoked this=0xb6301080
D/ (13624): Sheep:: No refs, strong count=268435456, weak count=0
D/ (13624): Sheep::in sp scope ------------
D/ (13624): Sheep::onFirstRef, object=0xb6301080
D/ (13624): Sheep:: After strong ref, strong count=1, weak count=1
D/ (13624): Sheep::in wp scope ------------
D/ (13624): Sheep:: After weak ref, strong count=1, weak count=2
D/ (13624): Sheep::out wp scope ------------
D/ (13624): Sheep:: release weak ref, strong count=1, weak count=1
D/ (13624): Sheep::out sp scope ------------
D/ (13624): Sheep::onLastStrongRef, id=0xbec42884
D/ (13624): Sheep::Sheep destructor invoked this=0xb6301080
D/ (13624): Sheep::--------------------testSheep end--------------------------
3.7 實際對象的狀態(tài)
通過前面的分析,我們可以繪制出實際對象的狀態(tài)圖萄金,如下如所示:
4 智能指針的使用
前面我們通過示例代碼1蟀悦,知道了智能指針是怎么管理實際對象的,怎么控制實際對象的釋放的氧敢。但是我們只是分析了其中的構(gòu)造函數(shù)和析構(gòu)函數(shù)日戈,下面我們將對智能指針做全面的了解。
4.1 RefBase的特性
我們先看看RefBase的類圖孙乖,如圖4-1所示浙炼。
- 所有類須從RefBase派生,只有一個無參構(gòu)造函數(shù)唯袄,RefBase析構(gòu)函數(shù)需申明為virtual弯屈。
- 在構(gòu)造函數(shù)中創(chuàng)建mRefs對象,為weakref_impl類型恋拷。
- 可以在派生類中通過函數(shù)extendObjectLifetime指定是強引用控制资厉,還是弱引用控制,默認(rèn)為強引用控制蔬顾。
- 在析構(gòu)函數(shù)中宴偿,判斷是否釋放mRefs。
- 私有的構(gòu)造函數(shù)和賦值運算重載阎抒,不允許子類使用酪我。
- 獲取實際對象的強引用數(shù)getStrongCount
- 子類可派生virtual成員函數(shù),獲知自身的引用情況且叁。
[system/core/include/utils/StrongPointer.h]
// 第一次強引用時回調(diào)
virtual void onFirstRef();
// 最后一次強引用時調(diào)用
virtual void onLastStrongRef(const void* id);
//由弱到強時調(diào)用都哭,稍候介紹
virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
// 最后一次弱引用時調(diào)用
virtual void onLastWeakRef(const void* id);
mRefs指向一個weakref_impl對象,是RefBase的應(yīng)用計數(shù)管家,其類圖如下圖4-2:
- 可以通過getWeakRefs()->getStrongCount()獲取實際對象的弱引用數(shù)
4.2 sp模板類的特性
- 提供多種形式的構(gòu)造方式
- 定義多種形式的賦值運算操作
- 重載操作運算符*欺矫,可以獲取實際對象
- 重載操作運算符->纱新,可以獲取指向?qū)嶋H對象的指針
- 可通過get函數(shù),獲取實際對象的指針
- force_set函數(shù)可以指定sp引用的實際對象穆趴,該函數(shù)設(shè)計有點缺點脸爱,若sp當(dāng)前已經(jīng)引用其他的對象,則可能造成其他對象無法釋放未妹。稍后我們單獨介紹簿废。
4.3 wp模板類特性
- 提供多種形式的構(gòu)造方式
- 定義多種形式的賦值運算操作
- 可通過unsafe_get函數(shù),獲取實際對象的指針络它,但是可能獲取到的是空的或是野指針
- 可以通過promote函數(shù)將弱引用變?yōu)閺娨米迕剩@個是一個比較重要的函數(shù),我們通過一個實例來看看是怎么由弱變強的化戳。
[實例代碼2]
void testPromote() {
{
Sheep* pSheep = new Sheep();
wp<Sheep> wpSheep(pSheep);
sp<Sheep> spSheep = wpSheep.promote();
}
}
Promote函數(shù)如下:
[system/core/include/utils/RefBase.h]
template<typename T>
sp<T> wp<T>::promote() const
{
sp<T> result;
if (m_ptr && m_refs->attemptIncStrong(&result)) {
result.set_pointer(m_ptr);
}
return result;
}
[system/core/libutils/RefBase.cpp]
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
incWeak(id); // 前面分析過单料,弱引用數(shù)+1;我們的實例中点楼,此時弱引用數(shù)為2
weakref_impl* const impl = static_cast<weakref_impl*>(this);
int32_t curCount = impl->mStrong;
while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
break;
}
curCount = impl->mStrong;
}
if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) ==
OBJECT_LIFETIME_STRONG) {
… …
while (curCount > 0) {
if (android_atomic_cmpxchg(curCount, curCount + 1,
&impl->mStrong) == 0) { // 強引用控制扫尖,強引用數(shù)+1
break;
}
curCount = impl->mStrong;
}
… …
} else {
… … // 弱引用控制,強引用數(shù) +1
curCount = android_atomic_inc(&impl->mStrong);
}
}
… …
return true; // 由弱變強成功
}
promote函數(shù)成功后掠廓,強引用數(shù)+1换怖,弱引用數(shù)+1。在我們的實例3中却盘,此時弱引用數(shù)為2狰域,強引用數(shù)為1。我們來看一下Log:
[實例代碼2的Log信息]
D/ (13742): Sheep:: -------------testPromote begin------------
D/ (13742): Sheep::Sheep constructor invoked this=0xb6301080
D/ (13742): Sheep:: No refs, strong count=268435456, weak count=0
D/ (13742): Sheep:: After weak ref, strong count=268435456, weak count=1
D/ (13742): Sheep:: After promote, strong count=1, weak count=2
D/ (13742): Sheep::onLastStrongRef, id=0xbed1c884
D/ (13742): Sheep::Sheep destructor invoked this=0xb6301080
D/ (13742): Sheep:: -------------testPromote end------------
為什么要由弱生強黄橘?我們通過弱指針wp,不能獲取實際的對象屈溉,wp并沒有提供sp那個的存取操作*和->的重載塞关,由弱生強后,可以sp獲取實際的對象子巾。
5 輕引用 LightRefBase
前面介紹的RefBase和mRefs比較復(fù)雜帆赢,Android還提供了一個輕型的引用管理LightRefBase。LightRefBase的代碼比較少线梗,直接看代碼:
[system/core/include/utils/RefBase.h]
template <class T>
class LightRefBase
{
public:
inline LightRefBase() : mCount(0) { }
inline void incStrong(__attribute__((unused)) const void* id) const {
android_atomic_inc(&mCount);
}
inline void decStrong(__attribute__((unused)) const void* id) const {
if (android_atomic_dec(&mCount) == 1) {
delete static_cast<const T*>(this);
}
}
inline int32_t getStrongCount() const {
return mCount;
}
typedef LightRefBase<T> basetype;
protected:
inline ~LightRefBase() { }
… …
private:
mutable volatile int32_t mCount;
};
LightRefBase的類圖如下:
LightRefBase比較簡單椰于,直接用mCount控制引用數(shù),從LightRefBase的特性看來仪搔,LightRefBase只支持sp控制瘾婿,不支持wp。
下面通過一個實例代碼,看看LightRefBase的使用偏陪,代碼如下:
[實例代碼3]
class Goat: public LightRefBase<Goat> {
public:
Goat() {
ALOGD("Goat::Goat constructor invoked this=%p \n", this);
}
private:
friend class LightRefBase<Goat> ;
~Goat() {
ALOGD("Goat::Goat destructor invoked this=%p \n", this);
}
};
void testGoat() {
Goat* pGoat = new Goat(); //初始時mCount為0
ALOGD("Goat:: before sp ref, mCount=%d", pGoat->getStrongCount());
{
sp<Goat> spGoat(pGoat); // 調(diào)用pGoat的incStrong函數(shù)抢呆,mCount為1
}
// spGoat的作用域結(jié)束,調(diào)用pGoat的decStrong函數(shù)笛谦,mCount為0抱虐,delete掉pGoat
}
實例代碼也非常簡單,只是和羊桿上了…今年羊年饥脑。下面是Goat的構(gòu)造和析構(gòu)情況恳邀,情況Log。
[實例代碼3的Log信息]
D/ (14539): Goat::--------------------testGoat end--------------------------
D/ (14539): Goat::Goat constructor invoked this=0xb6301080
D/ (14539): Goat:: before sp ref, mCount=0
D/ (14539): Sheep::in sp scope ------------
D/ (14539): Goat:: After sp ref, mCount=1
D/ (14539): Sheep::out sp scope ------------
D/ (14539): Goat::Goat destructor invoked this=0xb6301080
D/ (14539): Goat::--------------------testGoat end--------------------------
注意灶轰,LightRefBase的析構(gòu)函數(shù)不是virtual的谣沸,對象釋放時,可能會造成子類的析構(gòu)函數(shù)調(diào)不到框往,因此鳄抒,Android對LightRefBase做了一個簡單的包裹,提供了VirtualLightRefBase類椰弊,VirtualLightRefBase的析構(gòu)函數(shù)是virtual的许溅,使用時,優(yōu)先使用VirtualLightRefBase秉版。
6 Android原始設(shè)計缺陷
任何設(shè)計都不敢保證沒有缺陷贤重。前面說sp的時候我們說到sp的force_set函數(shù),下面我將具體來分析清焕。
6.1 **force_set會破壞 sp 設(shè)計的初衷
force_set函數(shù)的原型:
[system/core/include/utils/StrongPointer.h]
template<typename T>
void sp<T>::force_set(T* other) {
other->forceIncStrong(this);
m_ptr = other; // 若m-ptr之前指向其他的對象并蝗,則其他對象的m-ptr不為0
}
我們來舉例說明一下,請看示例代碼4:
[實例代碼4]
void testForceSet()
{
{
sp<Sheep> spSheep(new Sheep());
spSheep.force_set(new Sheep ());
}
}
我們看看Sheep的釋放情況:
[實例代碼4的Log信息]
D/ (13385): Sheep:: -------------testForceSet begin------------
D/ (13385): Sheep::Sheep constructor invoked this=0xb6301080
D/ (13385): Sheep::onFirstRef, object=0xb6301080
D/ (13385): Sheep::Sheep constructor invoked this=0xb6301088
D/ (13385): Sheep::onFirstRef, object=0xb6301088
D/ (13385): Sheep::onLastStrongRef, id=0xbea5a894
D/ (13385): Sheep::Sheep destructor invoked this=0xb6301088
D/ (13385): Sheep:: -------------testForceSet begin------------
可見秸妥,我們構(gòu)造了兩個對象0xb6301080和0xb6301088滚停,但是只有0xb6301088釋放掉了,0xb6301080沒有并釋放粥惧。所以使用force_set函數(shù)時一定要注意键畴,但是如下的代碼是沒有問題的。
[演示代碼3]
void testForceSet()
{
{
sp<Sheep> spSheep; // spSheep的m_ptr并沒有指向?qū)嶋H的對象
spSheep.force_set(new Sheep ());
}
}
6.2 force_set的改進方案
在force_set函數(shù)時突雪,先將原引用的對象的引用數(shù)-1起惕,sp再引用新對象,這這樣可避免原實際對象不能釋放的問題咏删。
[force_set的改進方案]
--- a/include/utils/StrongPointer.h
+++ b/include/utils/StrongPointer.h
@@ -187,7 +187,12 @@ sp<T>& sp<T>::operator =(U* other) {
template<typename T>
void sp<T>::force_set(T* other) {
- other->forceIncStrong(this);
+ if (m_ptr) {
+ m_ptr->decStrong(this);
+ }
+ if (other) {
+ other->forceIncStrong(this);
+ }
m_ptr = other;
}
7 智能指針小結(jié)
我們從C++的特性出發(fā)惹想,逐步展開Android中智能指針的相關(guān)知識,從原理督函,到實現(xiàn)嘀粱,再到具體的使用激挪。
Android設(shè)計sp和wp對實際對象進行引用,而實際對象類需要從基類RefBase或LightRefBase派生草穆。