Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android framwork 分析之智能指針LightRefBase ,sp,wp,RefBase

Android framwork 分析之智能指針LightRefBase ,sp,wp,RefBase

編輯:關於Android編程

Android的framework層都是由c++來實現的 大家都知道c++最令人頭痛的莫過於內存洩漏了 ,如果是一個人開發還好 當new出來一個對象後 應該會記得delete掉 但是當多人開發的時候難免會出現這裡調用了 那裡忘記釋放了 特別是對於Android是一個系統來說 那就更加了! 所以才有了用智能指針來處理這個問題,當沒有地方引用的時候實現自動釋放 避免內存洩漏或者野指針的問題。

在分析源碼的過程中時常會出現sp之類的出現 那現在就來看看它的具體實現。

首先我們來看看:

LightRefBase

這個類是輕量級指針,它裡面的代碼很簡單 就是有兩個操作的方法以及一個用來記錄引用的計數器變量:
template 
class LightRefBase
{
public:
    inline LightRefBase() : mCount(0) { }//初始化引用的次數為0
    inline void incStrong(const void* id) const {//增加一次引用
        android_atomic_inc(&mCount);//原子操作 由匯編語言完成
    }
    inline void decStrong(const void* id) const {//減少一次引用
        if (android_atomic_dec(&mCount) == 1) {
            delete static_cast(this);
        }
    }
    //獲取該對象當前被引用的次數
    inline int32_t getStrongCount() const {
        return mCount;
    }
    typedef LightRefBase basetype;
protected:
    inline ~LightRefBase() { }//析構
private:
    mutable volatile int32_t mCount;//用來記錄對象的引用次數
};

LightRefBase 它提供了引用計數的次數,刪除以及增加的操作 它是一個計數器的基類,這個類中最重要的是mutable volatile int32_t mCount; 這個類的任務就是維護mCount這個引用計數器, mutable實現互斥訪問,volatile保證值的最新,具體的管理則是由sp類來管理 每當有一個對象被引用則調用sp的構造方法來增加一次引用 由於sp屬於棧變量,所以根據C++的規則當超出范圍的時候就會執行析構函數來減少一次引用,當引用次數為0的時候就會delete此對象,現在來看看sp類,它是一個模板類:

二、sp(StrongPointer)

template 
class sp
{
public:
    inline sp() : m_ptr(0) { }//默認構造函數 他沒有初始化m_ptr
    sp(T* other);//初始化T 這裡的T代表是實際引用的對象 
    sp(const sp& other);//拷貝構造函數 同理
                                          
    template sp(U* other);//帶有返回值的構造函數
    template sp(const sp& other);
                                        
    ~sp();
                                          
    // 操作符重載
    sp& operator = (T* other);
    sp& operator = (const sp& other);
                                          
    template sp& operator = (const sp& other);
    template sp& operator = (U* other);
                                          
    //動態來設置T的對象 
    void force_set(T* other);
                                          
    // Reset
    void clear();
                                          
    // 操作符重載 這樣的話當使用這些操作符 返回的就是引用的實際對象
    inline  T&      operator* () const  { return *m_ptr; }
    inline  T*      operator-> () const { return m_ptr;  }
    inline  T*      get() const         { return m_ptr; }
                                          
    // 宏定義 一些運算符
    COMPARE(==)
    COMPARE(!=)
    COMPARE(>)
    COMPARE(<)
    COMPARE(<=)
    COMPARE(>=)
private:  
    template friend class sp;
    template friend class wp;//友元類
    void set_pointer(T* ptr);
    T* m_ptr;//這裡的T就是實際引用的對象了
};
再來看看他的構造函數和拷貝構造
template
sp::sp(T* other)//這裡的other實際就是LigthRefBase
    : m_ptr(other)//把other賦值給m_ptr
{
    if (other) other->incStrong(this);//調用LigthRefBase的incstrong
}

template
sp::sp(const sp& other)
    : m_ptr(other.m_ptr)
{
    if (m_ptr) m_ptr->incStrong(this);//同上面構造函數
}




看看他的析構函數:

template
sp::~sp()
{
    if (m_ptr) m_ptr->decStrong(this);//實際是調用LightRefBase的decStrong函數-1了 如果引用次數為0則delete該對象
}

通過上面的分析應該就知道 如果一個對象要支持輕量級指針 那肯定得繼承LightRefBase類 ,當對象類繼承了LightRefBase 則代表其也有引用的計數器了 既然這樣 那我們也來實現一個看看效果: 我們先定義一個MyLightClass然後繼承自LightRefBase ,如果用NDK來寫的話 比價麻煩 還要引入其源碼裡面的libutils.so 以及Include文件,所以我們直接在源碼中寫代碼,這樣引入頭文件方便許多
#include 
#include 

using namespace android;

class MyLightClass : public LightRefBase
{
public:
        MyLightClass()
        {
                printf("開始調用構造函數\n");
        }

        virtual ~MyLightClass()
        {
                printf("析構\n");
        }
};

int main(int argc, char** argv)
{
        MyLightClass* light = new MyLightClass();
        sp lp = light;

        printf("當前的引用次數為%d\n", light->getStrongCount());

        {
                sp lp2 = lp;

                printf("當前的引用次數為%d\n", light->getStrongCount());
        }

        printf("當前的引用次數為%d\n", light->getStrongCount());

        return 0;
}
現在來分析下我們寫的代碼 當執行到 sp lp=light 時會自動調用sp的構造函數初始化裡面的m_ptr為LinghtClass的實際對象 進而調用LightRefBase裡面的incStrong方法使引用計數m_count加一操作,所以此時打印的應該是1,然後在進入內嵌代碼塊 執行sp lp2=lp; 同樣會執行加1操作 所以在打印的時候應該是2了,當執行完代碼塊裡面的內容後 由於是局部變量保存在棧中 當執行完了以後所以就會自動掉用sp的析構函數 那麼此時應該會調用LightRefBase的decStrong函數-1操作了 並且判斷下當前是引用計數是不是為0 由於不為0 表明還有對象在用所以不會刪除此對象 ,出了代碼塊再打印那麼應該是1了,當我們執行完這個程序退出時候那麼lp的局部變量會再次調用sp的析構函數 在把引用計數-1 然後判斷當前是否為0 此時滿足條件 所以就會delete此對象 也就是我們new的 light對象了;我們在external目錄新建此文件為 MyLightClass.cpp   由於寫的代碼是在源碼中 為了參與編譯所以我們得寫一個Android.mk文件在external目錄下
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := mylight
LOCAL_SRC_FILES := MyLightClass.cpp
LOCAL_SHARED_LIBRARIES := \
        libcutils \
        libutils
include $(BUILD_EXECUTABLE)

上面的mk文件代碼我們要編譯成可執行文件 也就是在system裡面bin目錄 然後執行編譯 這裡我們使用mmm命令只編譯單模塊
xielinhua@ubuntu:~/Downloads/Android$ mmm ./external/mylight


編譯完成後我們就可以來打包:

xielinhua@ubuntu:~/Downloads/Android$make snod
然後在打開模擬器來執行剛才編譯好的有我們寫的代碼
xielinhua@ubuntu:~/Downloads/Android$  emulator -kernel ./kernel/goldfish/arch/arm/boot/zImage &
最後來執行我們剛才寫的程序看看他的打印是不是我們剛才分析的一樣呢:
xielinhua@ubuntu:~/Downloads/Android$ adb shell
root@android:/ # cd system/bin/ root@android:/system/bin # ./mylight    
開始調用構造函數
當前次數為1
當前次數為2
當前次數為1
析構
\
   

恩 現在應該明白輕量指針是怎麼來控制對象的了 不過當我們遇到復雜一點的比如說A引用了B B也引用了A這種情況的話 兩者相互引用的話那麼利用上面的顯然是釋放不了的,因為程序再執行的過程中會判斷 對象的計數是否為0才釋放,假設程序只執行一邊的代碼(比如就執行A類裡面的代碼,A裡面的B引用其他類裡面的對象,但是沒有去執行那邊的類代碼)時候那麼這兩個對象都得不到釋放 因為對象減一操作好還不為0!
Android中它采用了對象的引用計數同時存在強引用和弱引用兩種計數。例如A引用B則B的強引用計數和弱引用計數+1,而B引用A則A僅僅弱引用數+1,在回收時只要對象的強引用計數為0,則不管弱引用數是否為0都進行回收,類似於死鎖解決中的強制釋放資源,那現在我們來看看更強大的引用計數器 RefBase

三、RefBase

class RefBase
{
public:
	void            incStrong(const void* id) const;//增加一個引用計數
	void            decStrong(const void* id) const;//減少一個引用計數

	void            forceIncStrong(const void* id) const;

	class weakref_type//內部類 主要是通過這個類來實現強弱引用的計數
	{
	public:
		RefBase*            refBase() const;//實際對象

		void                incWeak(const void* id);
		void                decWeak(const void* id);//減少弱引用的次數

		bool                attemptIncStrong(const void* id);

		//! This is only safe if you have set OBJECT_LIFETIME_FOREVER.
		bool                attemptIncWeak(const void* id);

	};

	weakref_type*   createWeak(const void* id) const;//創建一個管理引用的對象

	weakref_type*   getWeakRefs() const;

protected:
	RefBase();
	virtual                 ~RefBase();

	//! Flags for extendObjectLifetime()對象的生命周期控制 默認為0則代表是受強引用的控制 
	enum {
		OBJECT_LIFETIME_WEAK    = 0x0001,//代表受弱引用的控制
		OBJECT_LIFETIME_FOREVER = 0x0003//代表不受控制 自己來管理
	};

	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);

	weakref_impl* const mRefs;//看名字就知道weakref_type的實現類
};

由此可見 在RefBase裡面跟ligthRefBase不一樣 它使用的是一個類 weakref_impl來管理對象的強弱引用, 首先來看看RefBase的構造函數:
RefBase::RefBase()
    :mRefs(new weakref_impl(this))//構造的時候 new了一個weakref_impl對象
{
  //mRefs是RefBase的成員變量,類型是weakref_impl
}
那先來看看weakref_impl類的初始化:
weakref_impl(RefBase* base)
        :mStrong(INITIAL_STRONG_VALUE) //強引用計數,初始值為0x1000000
        ,mWeak(0) //弱引用計數,初始值為0
        ,mBase(base)//所指向的實際對象
        ,mFlags(0)//生命周期受什麼影響 默認是0 也就是強引用
        ,mStrongRefs(NULL)
        ,mWeakRefs(NULL)
        ,mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
        ,mRetain(false)
    {
     }
我們再來舉一個列子:
A a=new A();
sp sa=a;
這裡是new了一個A對象a 然後賦值給了 sp sa; 那現在來看看sp的構造函數:
template
sp::sp(T* other)
        : m_ptr(other) {
    if (other)
        other->incStrong(this);//這裡調用的是實際對象的incStrong方法
}
  這裡的A如果要實現智能指針 那一定是繼承了RefBase類了,那other->incStrong(this)實際調用的是RefBase的對應方法:  
void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);//增加弱引用的計數
    
    refs->addStrongRef(id);//debug 正式不做處理
    const int32_t c = android_atomic_inc(&refs->mStrong);//原子加1 後返回+1前的值
    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);

    if (c != INITIAL_STRONG_VALUE)  {//判斷INITIAL_STRONG_VALUE是否等於c 如果不等於那就代表已經引用了 所以返回
        return;
    }

    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
    refs->mBase->onFirstRef();//第一次被引用
}
上面的方法首先調用incWeak方法增加了弱引用計數然後再增加強引用計數 那我們來看看這個函數:
void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast(this);
    impl->addWeakRef(id);//調試用的 這裡是空實現
    const int32_t c = android_atomic_inc(&impl->mWeak);//原子操作加1
    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
可以看到當調用incStrong方法的時候會使強引用加1操作 同時弱引用也增加1 當sp退出作用域的時候會調用 析構函數:
template
sp::~sp() {
    if (m_ptr)//如果m_ptr不為空則滿足條件 m_ptr是實際對象這裡滿足
        m_ptr->decStrong(this);
}

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) {//如果對象的引用計數為0 則滿足條件
        const_cast(this)->onLastStrongRef(id);
        if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {//如果對象生命周期是受弱引用的話還不能刪除該對象 否則就delete 自己了
            delete this;
        }
    }
    refs->removeWeakRef(id);
    refs->decWeak(id);
}
  上面可以看到刪除一個強引用並且判斷是不是要刪除對象自己 然後再減少弱引用 再來看看decWeak:
void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast(this);
    impl->removeWeakRef(id);
    const int32_t c = android_atomic_dec(&impl->mWeak);//弱引用-1操作
    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) {//如果是受強引用的控制 則進入
        if (impl->mStrong == INITIAL_STRONG_VALUE) {//判斷是不是初始值 如果是(代表弱引用計數不是通過強引用的方式來加1)那麼刪掉實際對象 否則刪掉管理計數的對象 也就是 weakref_impl實際對象則在Refbase中的decStrong中刪除
            delete impl->mBase; 
        } else {
            delete impl;
        }
    } else {
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
            delete impl->mBase;//如果是受弱引用控制 那麼就刪掉實際對象 
        }
    }
}
decWeak函數首先是把弱引用-1操作 如果-1前的c是不等於那麼久返回 代表還有對象再引用 。如果沒有對象引用的話那麼繼續往下執行 首先判斷它的生命周期是不是受強引用的控制 如果是的話,那麼它有兩種情況 ,一種是這個對象是強引用初始化的 它會分別把強引用和弱引用計數加一;一種是沒有初始化強引用 直接是弱引用來初始化的 這樣只會增加弱引用的計數加一; 既然這樣的話 就代表強引用可能沒有初始化 所以會判斷下 如果是沒有初始化強引用 則直接刪除對象 因為若引用的計數是大於等於強引用的!而這裡的弱引用的計數為0 那肯定代表強引用是0了!如果對象曾經是被強引用過的 那麼直接刪除impl也就是計數的管理類 因為再初始化sp的時候我們new了這個對象 !因為上面再刪除強引用計數的時候已經把實際對象給刪除了 到此所有的對象也就都釋放了!這是在對象生命周期受強引用控制的時候 那如果不是的話 調用onLastWeakRef首先通知這個對象可能馬上要釋放了 再判斷該對象是不是受弱引用控制的 如果是那麼此時弱引用為0了 也就是可以刪除實際對象了 那麼此時會調用RefBase的析構   再來看看RefBase的析構函數:

RefBase::~RefBase() { // LOGV("Destroying RefBase %p (refs %p)\n", this, mRefs); if (mRefs->mWeak == 0) {//如果弱引用計數為0 那麼刪除引用計數管理 也就是weakref_impl的對象 // LOGV("Freeing refs %p of old RefBase %p\n", mRefs, this); delete mRefs; } }




跟著上面分析繼續!在decWeak方法執行delete impl->mBase時由於這裡滿足條件 所以會刪除計數管理 現在知道為什麼在decWeak函數中 如果如果對象生命周期受強引用控制但是又不是使用強引用初始化的話 為什麼會delete impl了 因為impl是RefBase new出來的通常應該是誰new誰delete的 主要是因為RefBase的析構函數會判斷弱引用是不是為0 但是這裡的判斷是不為0 的 所以才會在這裡delete impl的!
通過上面可知 當對象a 超過了作用域的話那麼 就會調用sp的析構函數 然後就會調用RefBase的decStrong方法 減少強弱引用計數的值 根據相應的生命周期來釋放a的對象!!
下面來看看wp 也就是弱指針

四、wp

我們先來看看頭文件的定義
template 
class wp
{
public:
    typedef typename RefBase::weakref_type weakref_type;
    
    inline wp() : m_ptr(0) { }//初始化m_ptr

    wp(T* other);
    wp(const wp& other);
    wp(const sp& other);
    template wp(U* other);
    template wp(const sp& other);
    template wp(const wp& other);

    ~wp();
    
    // Assignment

    wp& operator = (T* other);
    wp& operator = (const wp& other);
    wp& operator = (const sp& other);
    
    template wp& operator = (U* other);
    template wp& operator = (const wp& other);
    template wp& operator = (const sp& other);
    
    void set_object_and_refs(T* other, weakref_type* refs);

    // promotion to sp 升級成為強指針
    
    sp 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(==)
    COMPARE(!=)
    COMPARE(>)
    COMPARE(<)
    COMPARE(<=)
    COMPARE(>=)

private:
    template friend class sp;
    template friend class wp;

    T*              m_ptr;//實際對象
    weakref_type*   m_refs;//操作計數類管理類
從上面可以看到有兩個成員變量 T 實際對象 也就是RefBase 類 裡面有個管理類weakref_impl* const mRefs ;weakref_type* m_refs這是用來干嘛的呢?

先來看看wp的構造函數:
template
wp::wp(T* other)
    : m_ptr(other)//初始化m_ptr
{
    if (other) m_refs = other->createWeak(this);//調用對象的createWeak方法 也就是RefBase的方法
}
再來看看createWeak方法:
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id);//調用incWeak 增加對象的弱引用計數
    return mRefs;//返回操作計數的管理對象 也就是weakref_type的實現類
}

可以看到wp的構造函數不像sp構造一樣會同時增加強弱引用 wp只增加了弱引用 ;調用完createWeak後 m_refs也就指向了mRefs了也就是實際的操作管理計數 再看看析構函數:
template
wp::~wp()
{
    if (m_ptr) m_refs->decWeak(this);//減一操作
}
弱指針是不能操作對象的因為他沒有重載* ->操作費 但是他有一個方法升級成強指針來操作對象
template
sp wp::promote() const
{
    return sp(m_ptr, m_refs);
}
sp的構造函數:
template
sp::sp(T* p, weakref_type* refs)
    : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0)
{
}
這裡首先要判斷p是否為空 也就是實際對象還在不在,還要判斷attemptIncStrong是否是true 是的話才能升級為強指針 那我們來看看這個函數:
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
	incWeak(id);//增加弱引用 

	weakref_impl* const impl = static_cast(this);

	int32_t curCount = impl->mStrong;//強引用的個數
	//如果強引用的個數大於0並且還不等於初始值 那對象有強引用
	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) {
		bool allow;
		if (curCount == INITIAL_STRONG_VALUE) {//沒有強引用過 還是初始值
			//判斷生命周期是不是受弱引用控制 onIncStrongAttempted()默認返回true 
			allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK/*不受弱引用控制的話為true 代表允許升級*/
				|| impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
		} else {
		
			allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK
				&& impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
		}
		if (!allow) {
			decWeak(id);
			return false;
		}
		curCount = android_atomic_inc(&impl->mStrong);

		// If the strong reference count has already been incremented by
		// someone else, the implementor of onIncStrongAttempted() is holding
		// an unneeded reference.  So call onLastStrongRef() here to remove it.
		// (No, this is not pretty.)  Note that we MUST NOT do this if we
		// are in fact acquiring the first reference.
		if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {
			impl->mBase->onLastStrongRef(id);
		}
	}

	impl->addWeakRef(id);
	impl->addStrongRef(id);

#if PRINT_REFS
	LOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount);
#endif

	if (curCount == INITIAL_STRONG_VALUE) {
		android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);
		impl->mBase->onFirstRef();
	}

	return true;
}
 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved