Java和C/C++的一個重大區(qū)別越妈,就是它沒有"指針"的概念错沃,這并不代表Java不需要指針边败,而是將這個"超級武器隱藏了"。如果大家使用C/C++開發(fā)過一些大型項目捎废,就會知道一個比較頭疼的問題——指針異常笑窜。所以Java以其他更"安全"的形式向開發(fā)人員提供了隱形的"指針",使得用戶既能享受到指針的強大功能登疗,又能盡量避免指針帶來的問題排截。
(一)嫌蚤、C/C++中常見的指針問題
1、指針沒有初始化
對指針進行初始化是程序員必須養(yǎng)成的良好習慣断傲,也是指針問題中最容易解決和控制的一個問題
2脱吱、new了對象沒有及時delete
動態(tài)分配內(nèi)存的對象,其實生命周期的控制不當认罩,常常會引起不少麻煩箱蝠。如果只有一個程序員在維護時,問題通常不大垦垂,因為只要稍微留心就可以實現(xiàn)new和delete的配套操作宦搬;但是如果一個大型工程(特別是多滴協(xié)同研發(fā)的軟件項目),由于溝通的不及時或者人員素質(zhì)的參差不齊劫拗,就很可能會出現(xiàn)動態(tài)分配的內(nèi)存沒有回收的情況——造成的內(nèi)存泄露問題往往是致命的间校。
3、野指針
- 假設(shè)1:我們new了一個對象A页慷,并將指針ptr指向這個新是對象(即ptr= new )憔足。當對A使用結(jié)束后,我們也主動delete了A酒繁,但是唯一沒做的是將ptr指針置空滓彰,那么可能出現(xiàn)什么問題?沒錯州袒,就是野指針揭绑。因此如果有"第三方"視圖用ptr來使用內(nèi)存對象,它首先通過判斷發(fā)現(xiàn)ptr不為空稳析,自然而然的就認為這個對象還是存在的洗做,其結(jié)果就是導致死機。
- 假設(shè)2:假設(shè)ptr1和ptr2都指向?qū)ο驛彰居,后來我們通過ptr1釋放了A的內(nèi)存空間诚纸,并且將ptr1也置為null;但是ptr2并不知道它所指向的內(nèi)存對象已經(jīng)不存在了陈惰,此時如果ptr2來訪問A也會導致死機
(二)畦徘、我們設(shè)計的解決方案
上面分析了C/C++指針的問題。如果讓我們設(shè)計Android的智能指針抬闯,怎么做才能防止以上幾個問題井辆?解決方案思路如下:
- 問題1的解決方案:這個簡單,只要讓指針在創(chuàng)建時設(shè)置為null即可
- 問題2的解決方案:比較復雜溶握,既然是智能指針就為意味著它應該是一個"雷鋒"杯缺,盡可能自動的實現(xiàn)new和delete的相應工作,那什么時候應該delete一個內(nèi)存對象呢睡榆?肯定是"不需要的時候"(其實是個廢話)萍肆。那怎么來分別什么是"需要"和"不需要"袍榆?在C/C++中,我們一般認為:當一個指針指向一個object的時候塘揣,這個內(nèi)存對象就是"需要"的包雀,當這個指針解除了與內(nèi)存對象的關(guān)系,我們就認為這個內(nèi)存對象已經(jīng)"不需要"了亲铡。所以我們想到用一個布爾類型變量來保存即可才写。
問題3的解決方案:問題又來了,當有兩個指針及兩個以上指針同時使用這個內(nèi)存怎么辦奖蔓,用布爾類型肯定是不行的赞草。所以我們要用一個計數(shù)器來記錄這個內(nèi)存對象"被需要"的個數(shù)即可,當這個計數(shù)器遞減到零時锭硼,就說明這個內(nèi)存對象應該"壽終正寢"了房资。這就是在很多領(lǐng)域了都得到廣泛應用的"引用計數(shù)"的概念蜕劝。如下圖
引用計數(shù)
(三)檀头、Android智能指針的原理
重點強調(diào) :
智能指針是一個對象,而不是一個指針.
- 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ù)管理,都有mRef來管理
Android智能指針的關(guān)系圖:
(四)兑凿、Android智能指針的源碼位置
android中的智能指針的主要代碼是:RefBase.h和RefBase.cpp StrongPointer.h 這三個文件凯力,他們分別位于:
RefBase.cpp:Android源碼目錄 /system/core/libutils/RefBase.cp
RefBase.h:Android源碼目錄 /system/core/include/utils/RefBase.h
StrongPointer.h:Android源碼目錄/system/core/include/utils/StrongPointer.h
鏈接如下
(五)眨业、強指針sp
看到sp,很多人會以為是StrongPointer的縮寫沮协。與sp對應的是wp龄捡,我們將會在下一節(jié)講解。
先來看下源碼:
///system/core/include/utils/StrongPointer.h 58行
template<typename T>
class sp {
public:
inline sp() : m_ptr(0) { }
sp(T* other); //常用的構(gòu)造函數(shù)
sp(const sp<T>& other);
template<typename U> sp(U* other);
template<typename U> sp(const sp<U>& other);
~sp(); //析構(gòu)函數(shù)
// Assignment
sp& operator = (T* other); // 重載運算符"="
sp& operator = (const sp<T>& other);
template<typename U> sp& operator = (const sp<U>& other);
template<typename U> sp& operator = (U* other);
//! Special optimization for use by ProcessState (and nobody else).
void force_set(T* other);
// Reset
void clear();
// Accessors
inline T& operator* () const { return *m_ptr; } // 重載運算符 " * "
inline T* operator-> () const { return m_ptr; } // 重載運算符" -> "
inline T* get() const { return m_ptr; }
// Operators
COMPARE(==)
COMPARE(!=)
COMPARE(>)
COMPARE(<)
COMPARE(<=)
COMPARE(>=)
private:
template<typename Y> friend class sp;
template<typename Y> friend class wp;
void set_pointer(T* ptr);
T* m_ptr;
};
通過閱讀源碼慷暂,我們知道這個sp類的設(shè)計和我們之前想象的基本一致聘殖,比如運算符的實現(xiàn)為:
//system/core/include/utils/StrongPointer.h 157行
template<typename T>
sp<T>& sp<T>::operator =(T* other) {
if (other)
other->incStrong(this); //增加引用計數(shù)
if (m_ptr)
m_ptr->decStrong(this); // 減少引用計數(shù)
m_ptr = other;
return *this;
}
上面的diamante同時考慮了對一個智能指針重復賦值的情況。即當m_ptr不為空時行瑞,要先撤銷它之前指向的內(nèi)存對象奸腺,然后才能賦予其新值。另外為sp分配一個內(nèi)存對象血久,不一定要通過操作運算符(比如等號)突照,它的構(gòu)造函數(shù)也是可以的。比如下面這段代碼
//system/core/include/utils/StrongPointer.h 112行
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other) {
if (other)
other->incStrong(this); //因為是構(gòu)造函數(shù)氧吐,所以不同擔心mptr之前已經(jīng)賦值過
}
這時候m_ptr就不用先置為null讹蘑,可以直接指向目標對象。而析構(gòu)函數(shù)的做法和我們的預想也是一樣筑舅。
template<typename T>
sp<T>::~sp() {
if (m_ptr)
m_ptr->decStrong(this);
}
(六)座慰、弱指針wp
1、弱引用產(chǎn)生的背景:
在前面討論之智能指針的"設(shè)計理念"時翠拣,其實是以強指針為原型逐步還原出智能指針作者的"意圖'版仔,那么"弱指針"又由什么作用?
其實弱指針主要是為了解決一個問題?那是什么問題那误墓?有這么一種情況:父對象指向子對象child蛮粮,然后子對象又指向父對象,這就存在了虛幻引用的現(xiàn)象谜慌。比如有兩個class
struct Parent
{
Child *myson;
}
struct Child
{
Parent *myfather;
}
這樣就會產(chǎn)生上面的問題然想。如果不考慮智能指針,這樣的情況不會導致任何問題畦娄,但是在智能指針的場景下又沾,就要注意了,因為Parent指向了Child熙卡,所以Child的引用計數(shù)器不為零杖刷。同時又由于Child指向了Parent,所以Parent的引用器不會為零驳癌。這有點類似于Java中的死鎖了滑燃。因為內(nèi)存回收者返現(xiàn)兩者都是"被需要"的狀態(tài),當然不能釋放颓鲜,從而形成了惡性循環(huán)表窘。
為了解決上面這個問題典予,產(chǎn)生了"弱引用"。具體措施如下:
Parent使用強指針來引用Child乐严,而Child只使用弱引用來指向父Parent類瘤袖。雙方規(guī)定當強引用計數(shù)器為0時,不論弱引用是否為0昂验,都可以delete自己(Android系統(tǒng)中這個規(guī)定是可以調(diào)整的捂敌,后面有介紹)。這樣只要一方得到了釋放了既琴,就可以成功避免死鎖占婉。當然這樣就會造成野指針。是的甫恩,比如Parent因為因為強指針計數(shù)器計數(shù)已經(jīng)到0了逆济,根據(jù)規(guī)則生命周期就結(jié)束了。但是此時Child還持有父類的弱引用磺箕,顯然如果Child此時用這個指針訪問Parent會引發(fā)致命的問題奖慌。為了別面這個問題,我們還規(guī)定: 弱指針必須先升級為強指針滞磺,才能訪問它所指向的目標對象升薯。
所以我們也可以說莱褒,若指針的主要使用就是解決循環(huán)引用的問題击困。下面具體看看它和強指針的區(qū)別。我們先從代碼上看
2广凸、wp的源碼解析:
代碼在RefBase.h 215行
template <typename T>
class wp
{
public:
typedef typename RefBase::weakref_type weakref_type;
inline wp() : m_ptr(0) { }
wp(T* other); //構(gòu)造函數(shù)
wp(const wp<T>& other);
wp(const sp<T>& other);
template<typename U> wp(U* other);
template<typename U> wp(const sp<U>& other);
template<typename U> wp(const wp<U>& other);
~wp();
// Assignment
wp& operator = (T* other); //運算符重載
wp& operator = (const wp<T>& other);
wp& operator = (const sp<T>& other);
template<typename U> wp& operator = (U* other);
template<typename U> wp& operator = (const wp<U>& other);
template<typename U> wp& operator = (const sp<U>& other);
void set_object_and_refs(T* other, weakref_type* refs);
// promotion to sp
sp<T> promote() const; //升級為強指針
// Reset
void clear();
// Accessors
inline weakref_type* get_refs() const { return m_refs; }
inline T* unsafe_get() const { return m_ptr; }
// Operators
COMPARE_WEAK(==)
COMPARE_WEAK(!=)
COMPARE_WEAK(>)
COMPARE_WEAK(<)
COMPARE_WEAK(<=)
COMPARE_WEAK(>=)
inline bool operator == (const wp<T>& o) const {
return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
}
template<typename U>
inline bool operator == (const wp<U>& o) const {
return m_ptr == o.m_ptr;
}
inline bool operator > (const wp<T>& o) const {
return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
}
template<typename U>
inline bool operator > (const wp<U>& o) const {
return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
}
inline bool operator < (const wp<T>& o) const {
return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
}
template<typename U>
inline bool operator < (const wp<U>& o) const {
return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
}
inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; }
template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
inline bool operator <= (const wp<T>& o) const { return !operator > (o); }
template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
inline bool operator >= (const wp<T>& o) const { return !operator < (o); }
template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }
private:
template<typename Y> friend class sp;
template<typename Y> friend class wp;
T* m_ptr;
weakref_type* m_refs;
};
通過和sp相比阅茶,我們發(fā)現(xiàn)有如下區(qū)別:
- 除了指向目標對象的m_ptr外,wp另外有一個m_refs指針谅海,類型為weakref_type脸哀。
- 沒有重載 " -> " 、" * " 等運算符扭吁。
- 有一個prmote方法將wp提升為sp撞蜂。
- 目標對象的父類不是LightRefBase,而是RefBase
3侥袜、wp的構(gòu)造函數(shù):
template<typename T>
wp<T>::wp(T* other)
: m_ptr(other)
{
if (other) m_refs = other->createWeak(this);
}
通過和強指針的中的構(gòu)造函數(shù)進行對比蝌诡,我們發(fā)現(xiàn),wp并沒有直接增加目標對象的引用計數(shù)值枫吧,而是調(diào)用了createWeak()函數(shù)浦旱。這個函數(shù)是RefBase類的
3.1RefBase類
在代碼在RefBase.h 69行
class RefBase
{
public:
void incStrong(const void* id) const; //增加強引用計數(shù)器的值
void decStrong(const void* id) const; //減少強引用計數(shù)器的值
void forceIncStrong(const void* id) const;
//! DEBUGGING ONLY: Get current strong ref count.
int32_t getStrongCount() const;
class weakref_type //嵌套類,wp中用到的就是這個類
{
public:
RefBase* refBase() const;
void incWeak(const void* id); //增加弱引用計數(shù)器的值
void decWeak(const void* id); //減少弱引用計數(shù)器的值
// acquires a strong reference if there is already one.
bool attemptIncStrong(const void* id);
// acquires a weak reference if there is already one.
// This is not always safe. see ProcessState.cpp and BpBinder.cpp
// for proper use.
bool attemptIncWeak(const void* id);
//! DEBUGGING ONLY: Get current weak ref count.
int32_t getWeakCount() const;
//! DEBUGGING ONLY: Print references held on object.
void printRefs() const;
//! DEBUGGING ONLY: Enable tracking for this object.
// enable -- enable/disable tracking
// retain -- when tracking is enable, if true, then we save a stack trace
// for each reference and dereference; when retain == false, we
// match up references and dereferences and keep only the
// outstanding ones.
void trackMe(bool enable, bool retain);
};
weakref_type* createWeak(const void* id) const;
weakref_type* getWeakRefs() const;
//! DEBUGGING ONLY: Print references held on object.
inline void printRefs() const { getWeakRefs()->printRefs(); }
//! DEBUGGING ONLY: Enable tracking of object.
inline void trackMe(bool enable, bool retain)
{
getWeakRefs()->trackMe(enable, retain);
}
typedef RefBase basetype;
protected:
RefBase(); //構(gòu)造函數(shù)
virtual ~RefBase(); //析構(gòu)函數(shù)
//! Flags for extendObjectLifetime()
// 以下參數(shù)用于修改object的生命周期
enum {
OBJECT_LIFETIME_STRONG = 0x0000,
OBJECT_LIFETIME_WEAK = 0x0001,
OBJECT_LIFETIME_MASK = 0x0001
};
void extendObjectLifetime(int32_t mode);
//! Flags for onIncStrongAttempted()
enum {
FIRST_INC_STRONG = 0x0001
};
virtual void onFirstRef();
virtual void onLastStrongRef(const void* id);
virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
virtual void onLastWeakRef(const void* id);
private:
friend class weakref_type;
class weakref_impl;
RefBase(const RefBase& o);
RefBase& operator=(const RefBase& o);
private:
friend class ReferenceMover;
static void renameRefs(size_t n, const ReferenceRenamer& renamer);
static void renameRefId(weakref_type* ref,
const void* old_id, const void* new_id);
static void renameRefId(RefBase* ref,
const void* old_id, const void* new_id);
weakref_impl* const mRefs;
};
RefBase嵌套了一個重要的類weakref_type九杂,也就是前面的m_refs指針所屬的類型颁湖。RefBase中還有一個mRefs的成員變量宣蠕,類型為weakref_impl。從名稱上來看甥捺,它應該是weak_type的實現(xiàn)類抢蚀。
3.2 weakref_impl類
在代碼在RefBase.cpp 64行
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
volatile int32_t mStrong; //強引用計數(shù)器的值
volatile int32_t mWeak; //弱引用計數(shù)器的值
RefBase* const mBase;
volatile int32_t mFlags;
#if !DEBUG_REFS //非Debug模式下,DEBUG_REFS是個宏
weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
, mFlags(0)
{
}
void addStrongRef(const void* /*id*/) { }
void removeStrongRef(const void* /*id*/) { }
void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { }
void addWeakRef(const void* /*id*/) { }
void removeWeakRef(const void* /*id*/) { }
void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { }
void printRefs() const { }
void trackMe(bool, bool) { }
#else //debug的情況下
weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
, mFlags(0)
, mStrongRefs(NULL)
, mWeakRefs(NULL)
, mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
, mRetain(false)
{
}
~weakref_impl()
{
bool dumpStack = false;
if (!mRetain && mStrongRefs != NULL) {
dumpStack = true;
ALOGE("Strong references remain:");
ref_entry* refs = mStrongRefs;
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
#if DEBUG_REFS_CALLSTACK_ENABLED
refs->stack.log(LOG_TAG);
#endif
refs = refs->next;
}
}
if (!mRetain && mWeakRefs != NULL) {
dumpStack = true;
ALOGE("Weak references remain!");
ref_entry* refs = mWeakRefs;
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
#if DEBUG_REFS_CALLSTACK_ENABLED
refs->stack.log(LOG_TAG);
#endif
refs = refs->next;
}
}
if (dumpStack) {
ALOGE("above errors at:");
CallStack stack(LOG_TAG);
}
}
void addStrongRef(const void* id) {
//ALOGD_IF(mTrackEnabled,
// "addStrongRef: RefBase=%p, id=%p", mBase, id);
addRef(&mStrongRefs, id, mStrong);
}
void removeStrongRef(const void* id) {
//ALOGD_IF(mTrackEnabled,
// "removeStrongRef: RefBase=%p, id=%p", mBase, id);
if (!mRetain) {
removeRef(&mStrongRefs, id);
} else {
addRef(&mStrongRefs, id, -mStrong);
}
}
void renameStrongRefId(const void* old_id, const void* new_id) {
//ALOGD_IF(mTrackEnabled,
// "renameStrongRefId: RefBase=%p, oid=%p, nid=%p",
// mBase, old_id, new_id);
renameRefsId(mStrongRefs, old_id, new_id);
}
void addWeakRef(const void* id) {
addRef(&mWeakRefs, id, mWeak);
}
void removeWeakRef(const void* id) {
if (!mRetain) {
removeRef(&mWeakRefs, id);
} else {
addRef(&mWeakRefs, id, -mWeak);
}
}
void renameWeakRefId(const void* old_id, const void* new_id) {
renameRefsId(mWeakRefs, old_id, new_id);
}
void trackMe(bool track, bool retain)
{
mTrackEnabled = track;
mRetain = retain;
}
void printRefs() const
{
String8 text;
{
Mutex::Autolock _l(mMutex);
char buf[128];
sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this);
text.append(buf);
printRefsLocked(&text, mStrongRefs);
sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this);
text.append(buf);
printRefsLocked(&text, mWeakRefs);
}
{
char name[100];
snprintf(name, 100, DEBUG_REFS_CALLSTACK_PATH "/%p.stack", this);
int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
if (rc >= 0) {
write(rc, text.string(), text.length());
close(rc);
ALOGD("STACK TRACE for %p saved in %s", this, name);
}
else ALOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this,
name, strerror(errno));
}
}
private:
struct ref_entry
{
ref_entry* next;
const void* id;
#if DEBUG_REFS_CALLSTACK_ENABLED
CallStack stack;
#endif
int32_t ref;
};
void addRef(ref_entry** refs, const void* id, int32_t mRef)
{
if (mTrackEnabled) {
AutoMutex _l(mMutex);
ref_entry* ref = new ref_entry;
// Reference count at the time of the snapshot, but before the
// update. Positive value means we increment, negative--we
// decrement the reference count.
ref->ref = mRef;
ref->id = id;
#if DEBUG_REFS_CALLSTACK_ENABLED
ref->stack.update(2);
#endif
ref->next = *refs;
*refs = ref;
}
}
void removeRef(ref_entry** refs, const void* id)
{
if (mTrackEnabled) {
AutoMutex _l(mMutex);
ref_entry* const head = *refs;
ref_entry* ref = head;
while (ref != NULL) {
if (ref->id == id) {
*refs = ref->next;
delete ref;
return;
}
refs = &ref->next;
ref = *refs;
}
ALOGE("RefBase: removing id %p on RefBase %p"
"(weakref_type %p) that doesn't exist!",
id, mBase, this);
ref = head;
while (ref) {
char inc = ref->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref);
ref = ref->next;
}
CallStack stack(LOG_TAG);
}
}
void renameRefsId(ref_entry* r, const void* old_id, const void* new_id)
{
if (mTrackEnabled) {
AutoMutex _l(mMutex);
ref_entry* ref = r;
while (ref != NULL) {
if (ref->id == old_id) {
ref->id = new_id;
}
ref = ref->next;
}
}
}
void printRefsLocked(String8* out, const ref_entry* refs) const
{
char buf[128];
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
sprintf(buf, "\t%c ID %p (ref %d):\n",
inc, refs->id, refs->ref);
out->append(buf);
#if DEBUG_REFS_CALLSTACK_ENABLED
out->append(refs->stack.toString("\t\t"));
#else
out->append("\t\t(call stacks disabled)");
#endif
refs = refs->next;
}
}
mutable Mutex mMutex;
ref_entry* mStrongRefs;
ref_entry* mWeakRefs;
bool mTrackEnabled;
// Collect stack traces on addref and removeref, instead of deleting the stack references
// on removeref that match the address ones.
bool mRetain;
#endif
};
- 從開頭的幾個變量大概可以猜出weakref_impl所做的工作镰禾,其中mStrong用于強引用計數(shù)思币,mWeak用于弱引用計數(shù)。宏DEBUG_REFS用于指示release或debug版本羡微,可以看出谷饿,在release版本下,addStrongRef,removeStrongRef相關(guān)的一系列方法都沒有具體實現(xiàn)妈倔,也就是說博投,這些方法實際上是用于調(diào)試的,我們在分析時完全可以用戶例會盯蝴。這樣一來毅哗,整體分析也清晰了很多。
- Debug和Release版本都將mStrong初始化為INITIAL_STRONG_VALUE捧挺。這個值定義如下:
在代碼在RefBase.cpp 640行
#define INITIAL_STRONG_VALUE (1<<28)
而mWeak則初始化為0虑绵。上面的代碼并沒有引用計數(shù)器相關(guān)控制的實現(xiàn),真正有用的代碼在類聲明的外面闽烙。比如我們在wp構(gòu)造函數(shù)中遇到的createWeak函數(shù)翅睛,那讓我們來看一下RefBase::createWeak()函數(shù)
3.3 RefBase::createWeak()函數(shù)
在代碼在RefBase.cpp 572行
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
mRefs->incWeak(id); //增加弱引用計數(shù)
return mRefs; //直接返回weakref_type對象
}
這個函數(shù)先增加了mRefs(也就是weak_impl類型成員變量)中的弱引用計數(shù)值,然后返回這個mRefs黑竞。
3.4 wp與RefBase
關(guān)于類的關(guān)系圖如下
- 首先 wp中的m_ptr還是要指向目標對象(繼承自RefBase)捕发。RefBase提供了弱引用控制以及其他新的功能。
- 其次 因為RefBase需要處理多種計數(shù)類型很魂,所以RefBase不直接使用int來保存應用計數(shù)器中的計數(shù)值扎酷,而是采用了weakref_type的計數(shù)器。另外wp也同時保存了這個計數(shù)器的地址遏匆,也就是wp中的m_refs和RefBase中的mRefs都指向了計數(shù)器法挨。其中wp是通過構(gòu)造函數(shù)中調(diào)用目標對象的createWeak來獲取計數(shù)器地址的,而計數(shù)器本身是由RefBase在構(gòu)造時創(chuàng)建的幅聘。
- 整個wp機制看起來很復雜凡纳,但與強指針相比實際上只是啟動了一個新的計數(shù)器weakref_impl而已,其他所有工作都是圍繞如何操作這個計數(shù)器而展開的喊暖。雖然weakref_impl是RefBase的成員變量惫企,但是wp也可以直接控制它,所以整個邏輯顯得稍微有點混亂。
在createWeak中狞尔,mRefs通過incWeak增加了計數(shù)器的弱引用丛版。代碼如下:
在代碼在RefBase.cpp 391行
void RefBase::weakref_type::incWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->addWeakRef(id);
const int32_t c __unused = android_atomic_inc(&impl->mWeak);
ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
這個函數(shù)真真的有用的語句就是android_atomic_inc(&impl->mWeak); ,它增加了mWeak計數(shù)器的值偏序,而其他都與調(diào)試有關(guān)页畦。
這樣當wp構(gòu)造完成以后,RefBase所持有的weakref_type計算器中的mWeak就為1研儒。后面如果有新的wp指向這個目標對象豫缨,mWeak還會持續(xù)增加。
上面是wp增加引用的邏輯端朵,那么如果sp指向它會怎么樣好芭?上面我們已經(jīng)說了sp會調(diào)用目標對象的incStrong方法來增加強引用計數(shù)器的值,當目標對象繼承自RefBase時冲呢,這個函數(shù)實現(xiàn)是
3.5 incStrong()函數(shù)
在代碼在RefBase.cpp 572行
void RefBase::incStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->incWeak(id); //增加弱引用計數(shù)值
refs->addStrongRef(id);
const int32_t c = android_atomic_inc(&refs->mStrong); //增加強引用計數(shù)器的值
ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
ALOGD("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);
ref
其實核心就兩行代碼
refs->incWeak(id);
const int32_t c = android_atomic_inc(&refs->mStrong);
其實也就是同時增加弱引用和強引用的計數(shù)器的值。然后還要判斷目標對象是不是第一次被引用敬拓,其中C的變量得到的是"增加之前的值"邻薯,因而如果等于INITIAL_STRONG_VALUE就說明是第一次。這時候一方面回調(diào)onFirseRef通過對象自己被引用乘凸,另一方面要對mStrong值做下小調(diào)整厕诡。因為mStrong先是被置為INITIAL_STRONG_VALUE=1<<28,那么當一次增加時营勤,它就是1<<28+1灵嫌,所以還要再次減掉INITIAL_STRONG_VALUE才能得到1。
4冀偶、對象釋放
現(xiàn)在我們再來分析下目標對象在什么情況下會被釋放醒第。無非就是考察減少強弱引用時系統(tǒng)所遵循的規(guī)則,如下所示是decStrong的情況进鸠。
在代碼在RefBase.cpp 341行
void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->removeStrongRef(id);
//減少強引用計數(shù)器的值
const int32_t c = android_atomic_dec(&refs->mStrong);
#if PRINT_REFS
ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
if (c == 1) {
//減少強引用計數(shù)器的值已經(jīng)降為0
//通知事件
refs->mBase->onLastStrongRef(id);
if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
//刪除對象
delete this;
}
}
//減少弱引用計數(shù)器的值
refs->decWeak(id);
}
整體流程如下:
- 首先減少mStrong計數(shù)器。
- 如果發(fā)現(xiàn)已經(jīng)減到0(即c==1)形病,就要回調(diào)onLastStrongRef通知這一事件客年,然后執(zhí)行刪除操作(如果標志是OBJECT_LIFETIME_STRONG)。
- 最后減少弱引用計數(shù)器的值
PS:特別注意漠吻,減少強引用計數(shù)器的值還要同時減少弱引用計數(shù)器的值量瓜,即最后decWeak(id)。
4.1途乃、decWeak()函數(shù)
在代碼在RefBase.cpp 400行绍傲,實現(xiàn)代碼如下:
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);
ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
if (c != 1) return;
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
// This is the regular lifetime case. The object is destroyed
// when the last strong reference goes away. Since weakref_impl
// outlive the object, it is not destroyed in the dtor, and
// we'll have to do it here.
if (impl->mStrong == INITIAL_STRONG_VALUE) {
// Special case: we never had a strong reference, so we need to
// destroy the object now.
delete impl->mBase;
} else {
// ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
delete impl;
}
} else {
// less common case: lifetime is OBJECT_LIFETIME_{WEAK|FOREVER}
impl->mBase->onLastWeakRef(id);
if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
// this is the OBJECT_LIFETIME_WEAK case. The last weak-reference
// is gone, we can destroy the object.
delete impl->mBase;
}
}
}
通過閱讀上面的代碼,我們發(fā)現(xiàn)
- 首先 顯示減少mWeak計數(shù)器的值
- 其次 如果發(fā)現(xiàn)是0(即c==1),就直接返回
- 如果發(fā)現(xiàn)不是0(即 c烫饼!=1)猎塞,則根據(jù)LIFETIME標志分別處理。
4.2杠纵、LIEFTIME的標志
LIEFTIME的標志是一個枚舉類荠耽,代碼如下
在代碼在RefBase.h 132行
//! Flags for extendObjectLifetime()
enum {
OBJECT_LIFETIME_STRONG = 0x0000,
OBJECT_LIFETIME_WEAK = 0x0001,
OBJECT_LIFETIME_MASK = 0x0001
};
每個目標對象都可以通過以下方法來更改它的引用規(guī)則
在代碼在RefBase.cpp 609行
void RefBase::extendObjectLifetime(int32_t mode)
{
android_atomic_or(mode, &mRefs->mFlags);
}
所以實際上就是改變了mFlags標志值——默認情況下它是0,即OBJECT_LIFETIME_STRONG比藻。釋放規(guī)則則受強引用控制的情況铝量。有的人可能會想,既然是強引用控制银亲,那么弱引用還要干什么慢叨?理論上它確實可以直接返回了,不過還有些特殊情況务蝠。前面在incString函數(shù)里插爹,我們看到它同時增加了強、弱引用計數(shù)值请梢。而增加弱引用是不會同時增加強引用的赠尾,這說明弱引用的值一定會大于強引用值。當程序走到這里毅弧,弱引用數(shù)值一定為0气嫁,而強引用的的值有兩種可能:
- 一種是強引用值為INITIAL_STRONG_VALUE,說明這個目標對象沒有被強引用過够坐,也就是說沒有辦法靠強引用指針來釋放目標寸宵,所以需要 delete impl->mBase
- 另外一種就是在有強引用的情況下,此時要delete impl元咙,而目標對象會由強引用的decStrong來釋放梯影。
那么為什么在這里delete這個是計數(shù)器?weakref_impl既然是由RefBase創(chuàng)建的庶香,那么按道理來說應該由它來刪除甲棍。實際上RefBase也想做這個工作,只是力不從心赶掖。其析構(gòu)函數(shù)如下:
在代碼在RefBase.cpp 588行
RefBase::~RefBase()
{
if (mRefs->mStrong == INITIAL_STRONG_VALUE) {
// we never acquired a strong (and/or weak) reference on this object.
delete mRefs;
} else {
// life-time of this object is extended to WEAK or FOREVER, in
// which case weakref_impl doesn't out-live the object and we
// can free it now.
if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {
// It's possible that the weak count is not 0 if the object
// re-acquired a weak reference in its destructor
if (mRefs->mWeak == 0) {
delete mRefs;
}
}
}
// for debugging purposes, clear this.
const_cast<weakref_impl*&>(mRefs) = NULL;
}
在這種情況下感猛,RefBase既然是有decStrong刪除的,那么從上面的decStrong的執(zhí)行順序來看mWeak值還不為0奢赂,因而并不會被執(zhí)行陪白。
如果弱引用控制下的判斷規(guī)則(即OBJECT_LIFTIME_WEAK),其實和decStrong中的處理一樣膳灶,要首先回調(diào)通知目標對象這一時間咱士,然后才能執(zhí)行刪除操作。
5、總結(jié)
關(guān)于Android的智能指針就分析到這里序厉,我們總結(jié)一下:
- 1锐膜、智能指針分為強指針sp和弱指針wp
- 2、通常情況下目標對象的父類是RefBase——這個基類提供了一個weakref_impl類型的引用計數(shù)器脂矫,可以同時進行強弱引用的控制(內(nèi)部由mStrong和mWeak提供計數(shù))
- 3枣耀、當incStrong增加強引用,也會增加弱引用
- 4庭再、當incWeak時只增加弱引用計數(shù)
- 5捞奕、使用者可以通過extendObjectLifetime設(shè)置引用計數(shù)器的規(guī)則,不同規(guī)則下對刪除目標對象的時機判斷也是不一樣的
- 6拄轻、使用者可以根據(jù)程序需求來選擇合適的智能指針類型和計數(shù)器規(guī)則