Android中的智能指標
1 智能指標的設計思想 Java和C++語言很重要的一個區別就是Java中沒有指標這個概念,這裡只是沒有這個概念,內部使用時還是用到指標,只是將其隱藏起來,封裝起來,讓開發人員不必擔心指標的問題。先來看一個下C++中常見的關於指標的問題 1
指標沒有初始化:變數在使用前要記得初始化,這個問題很好解決,將指標預設設定為null。 2
new 對象之後沒有及時delete:該對象佔用的記憶體空間沒有及時的回收,使得系統可用的記憶體空間越來越少,很有可能最終導致記憶體溢出,出現崩潰 3
野指標:ptr=new Object ,建立了一個對象Object,且指標ptr指向它,當Object使用結束之後也delete了,但是沒有做的操作就是沒有將ptr清空,這樣ptr仍然指向了該Object對象的地址。結果在使用時ptr不為空白,可以使用,但是此時Object對象已經delete了,Object所在的地址上的對象可能已經發生了變化,還用ptr訪問的話就可能出現未知錯誤。
如何解決這些問題呢? 對於第一個問題,很好解決,在初始化時設定為null即可 對於第二個問題,實現new和delete的同時操作,在new一個對象之後,要記得及時delete。 假如這裡封裝一個智能指標類SmartPointer,那麼SmartPoint類中要儲存object的記憶體位址,且這個SmartPoint是一個模板類,設計如下:
template class SmartPointer{ //初始化時將m_ptr設定為空白,這樣第一個問題就解決了 inline SmartPointer(): m_ptr(0){} private: T* m_ptr;//儲存object對象的地址}初始化時將m_ptr設定為空白,第一個問題解決了,那麼如果如何判斷一個對象需要delete呢:當這個對象不再被需要時,換句話說就是沒有一個指標指向該對象時,就可以認為這個對象不再被需要了。常用的解決方案就是使用一個計數器來記錄該對象目前被指標指向的次數,每被指向一次,該對象的計數加一;每次SmartPointer析構時,該對象的計數器減一,如果計數器的值為0時,就可以刪除該對象了。 這裡使用封裝一個父類,該父類表示一個對象,這個對象內部維護一個int類型的指標,記錄當前該對象被指標指向的次數:
template class LightRefBase{public: inline LightRefBase():mCount(0){} inline void incStrong() const//引用計數加一 { android_atomic_inc(&mCount); } inline void decStrong() const//引用計數減一 { if(android_atomic_dec(&mCount)==1) { delete static_cast(this);//如果沒有被引用就直接刪除該對象 } }private: mutable volatile int32_t mCount;//引用計數的個數}上面是對象類,有兩個關於引用計數的操作,incStrong和decStrong,這兩個函數又分別在SmartPointer的“=”賦值操作和析構操作時調用:
template class SmartPoint{ inline SmartPointer():m_ptr(0){} ~wp(); SmartPointer& operator = (T* other);private: T* m_ptr;}當為SmartPointer賦值時,要將該Object對象的引用計數加一,所以要重載賦值運算子:
SmartPointer &SmartPointer::operator=(T* other){ if(other!=null) { m_ptr=other;//指向該對象other other->incStrong();//other引用計數加一 } return *this;}析構時,要調用對象的decStrong,減少引用計數
wp::~wp(){ if(m_ptr) m_ptr-假如現在又兩個SmartPointer指向一個對象,那麼效果如下:有了這個引用計數,不用擔心對象沒有及時delete了,那麼第二個問題就解決了。同時第三個問題也解決了。2 Android中智能指標的實現Android中智能指標的實現有兩種,一個是Strong Pointer強指標(簡稱sp),一個是Weak Pointer弱指標(簡稱wp)。2.1 sp先看sp,它對應的檔案在frameworks/native/include/utils/StrongPointer.h檔案中template class sp{public: inline sp() : m_ptr(0) { } sp(T* other); sp(const sp& other); template sp(U* other); template sp(const sp& other); ~sp();// Assignment sp& operator = (T* other); sp& operator = (const sp& other);private: template friend class sp; template friend class wp; void set_pointer(T* ptr); T* m_ptr;};這裡還考慮了如果是為已賦值的SmartPointer重新賦值的情況,需要先撤銷之前指向的對象 templatesp& sp::operator = (T* other){ if (other) other->incStrong(this);//other對象引用計數加一 if (m_ptr) m_ptr->decStrong(this);//原來對象的引用計數減一 m_ptr = other;//現在m_ptr指向other return *this;}可以看出sp的操作和之前SmartPointer的操作類似,都是直接對引用計數的加一或者減一,這種類型的指標還是很好理解的,下面看看wp指標
2.2 wp 再看wp弱引用 已經有了強引用,為什麼還要用弱引用呢?假如現在又兩個類:CDad,CChild struct CDad{ CChild *myChild;}Struct CChild{ CDad *myDad;}而且他們之間很有可能出現這個情況:互相引用
這樣就處於死結狀態,兩者都處於被需要狀態,不能被釋放,這個時候就出現了弱引用,具體做法: CDad使用強指標引用CChild對象,而CChild只能使用弱引用指向CDad。規定當強引用計數為0時,不管弱引用是否為0都要delete自己,一旦delete自己就能打破僵局,避免死結的出現。那麼看看wp的實現,它的實現和sp還是有一些差別的,看它類之間的關係:
template class wp{public: typedef typename RefBase::weakref_type weakref_type; inline wp() : m_ptr(0) { }//建構函式 wp(T* other); ~wp(); // Assignment wp& operator = (T* other); sp promote() const;//將wp轉換為sp函數private: T* m_ptr; //指向目標對象 weakref_type* m_refs;//指向weakref_type類型的對象};要注意一下幾點: 1 除了指向目標對象的m_ptr還有一個指向weakref_type類型的對象,weakref_type是用來管理wp的類,由weakref_impl子類實現 2 一個的方法就是promote方法,可以將wp升級為sp,後面會涉及到 3 這裡的目標對象不再是sp中的LightRefBase,而是其父類RefBase,這個父類不僅僅需要處理sp的情況,還要處理wp的情況。
這裡舉一個例子,看下面代碼 class A:public RefBase//該類繼承RefBase{ //空實現}int main(){ A* pA = new A;//建立一個對象並讓pA指向它 { sp spA(pA);//先建立一個sp,在建立一個wp wp wpA(spA); } //之後析構wp,再析構sp} 一步一步的看,構造A對象,它繼承RefBase,所以使用的RefBase的建構函式 1 構造 RefBase對象 RefBase::RefBase() : mRefs(new weakref_impl(this))//建立一個weakref_impl類型對象,並賦值給mRefs{}RefBase中維護了一個weakref_impl類型的mRefs,weakref_impl繼承weakref_type類,簡要看一下weakref_impl類和weakref_impl的建構函式 class RefBase::weakref_impl : public RefBase::weakref_type{public: volatile int32_t mStrong; volatile int32_t mWeak; RefBase* const mBase; volatile int32_t mFlags; weakref_impl(RefBase* base) : mStrong(INITIAL_STRONG_VALUE)//強引用計數INITIAL_STRONG_VALUE 等於0x1000000 , mWeak(0)//弱引用計數初始化為0 , mBase(base)//儲存傳入的base值,就是目標對象 , mFlags(0)//儲存生命週期標誌,預設為Strong , mStrongRefs(NULL) , mWeakRefs(NULL) , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) , mRetain(false) { } // 其他一些函數.....}可以看出weakref_impl的建構函式主要操作就是為mStrong和mWeak設定初始值,而RefBase的構造就是建立一個weakref_impl對象並賦值給mRefs成員變數。
2 建立sp sp spA(pA);
看sp的建構函式,在StrongPointer.h檔案中 templatesp::sp(T* other)//other就是1中建立的pA對象: m_ptr(other)//將other賦值給m_ptr對象 { if (other) other->incStrong(this); }調用pA的incStrong函數,也就是RefBase的incStrong函數: void RefBase::incStrong(const void* id) const{ weakref_impl* const refs = mRefs;//mRefs就是RefBase構造時建立的weakref_impl對象 refs->incWeak(id); //讓impl的mWeak變數加一 refs->addStrongRef(id);///調試使用,非調試沒有具體操作 // c的值為自加之前的值,mStrong的值初始化為0x1000000,增加以後就是0x1000001 const int32_t c = android_atomic_inc(&refs->mStrong); if (c != INITIAL_STRONG_VALUE) { return;//c的值如果不是INITIAL_STRONG_VALUE,直接返回 } //否則,如果是INITIAL_STRONG_VALUE,也就是第一次使用,進行下面操作 //將mStrong-INITIAL_STRONG_VALUE ,0x1000001-0x1000000=1 android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); refs->mBase->onFirstRef();//第一次引用時調用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);//讓impl的mWeak變數加一,這個時候mWeak為0+1=1 }可以看出sp構造調用了RefBase的incStrong函數,對mRefs中的mWeak和mStrong進行操作,使得mWeak值為1,mStrong也為1
3 wp的建構函式 wp wpA(spA); 這裡出現了wp類,它的定義和sp有些不同,上面已經說明了,直接看wp的建構函式,在RefBase.h檔案中 templatewp::wp(const sp& other) : m_ptr(other.m_ptr)//sp中的m_ptr指向目標對象,將sp的m_ptr的值賦值給wp的m_ptr{ if (m_ptr) { m_refs = m_ptr->createWeak(this);//調用pA的createWeak 方法,並儲存返回知道m_refs }}這裡沒有增加引用計數,而是調用RefBase類的createWeak函數,該函數在RefBase.cpp檔案中: RefBase::weakref_type* RefBase::createWeak(const void* id) const{ mRefs->incWeak(id);//使得mRefs的mWeak 再次加一,此時mWeak 等於2 return mRefs;//返回mRefs }createWeak實現就是讓mRefs的mWeak加一,並返回mRefs,這樣wp中m_refs也指向mRefs變數,這是mStrong為1,mWeak為2
4 wp的解構函式 這個時候就意味著wp的生命快結束了,看它的解構函式,在RefBase.h檔案中 templatewp::~wp(){ if (m_ptr) m_refs->decWeak(this);//調用m_refs的decWeak 函數}還記得createWeak,wp的m_refs指向mRefs變數,所以就是調用weakref_type的decWeak函數,在RefBase.cpp檔案中 void RefBase::weakref_type::decWeak(const void* id){ weakref_impl* const impl = static_cast(this); impl->removeWeakRef(id);//調試使用,非調試不做任何操作 //取出mWeak的值,此時為2儲存到c中,並減一,此時為1 const int32_t c = android_atomic_dec(&impl->mWeak); if (c != 1) return;//由於c==2,返回 // 如果c==1的話,這個時候減一之後,mWeak=0,說明弱引用沒有指向目標對象,額可以考慮釋放記憶體 //OBJECT_LIFETIME_xxxxxx代表生命週期 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 { delete impl;//釋放weakref_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; } }}可以看出wp析構之後,mWeak減一變成1,mStrong沒有被操作,仍然是1,這個時候目標對象和weakref_impl對象都沒有真的被回收
5 sp解構函式 自己sp的解構函式,在StrongPointer.h檔案中: templatesp::~sp(){ if (m_ptr) m_ptr->decStrong(this);//調用目標對象的desStrong函數}這裡的目標函數類型是RefBase類型,看RefBase的decStrong函數,在RefBase.cpp檔案中: void RefBase::decStrong(const void* id) const{ weakref_impl* const refs = mRefs;//賦值refs為RefBase的mRefs值 refs->removeStrongRef(id);//調試使用,非調試不做任何操作 //取出mStrong的值,為1,賦值給c,並讓mStrong減一變成0 const int32_t c = android_atomic_dec(&refs->mStrong); if (c == 1) {//符合條件 refs->mBase->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { delete this;//刪除目標對象 } } refs->decWeak(id);}這裡c==1,符合條件,調用delete this,也就是RefBase的解構函式: RefBase::~RefBase(){ if (mRefs->mStrong == INITIAL_STRONG_VALUE) {//mStrong =0,不符合 // 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) {//此時mWeak 的值為1,不是0,不符合條件 delete mRefs; } } } // for debugging purposes, clear this. const_cast(mRefs) = NULL;}可以看出RefBase的解構函式並沒有符合條件,沒有刪除weakref_impl對象。在RefBase的解構函式執行之後再一次執行 refs->decWeak(id),在此之前mStrong=0,mWeak=1 void RefBase::weakref_type::decWeak(const void* id){ weakref_impl* const impl = static_cast(this); impl->removeWeakRef(id);//調試使用,非調試不做任何操作 //取出mWeak的值,此時為1儲存到c中,並減一,此時為0 const int32_t c = android_atomic_dec(&impl->mWeak); if (c != 1) return;//由於c==1,不符合 // 如果c==1的話,這個時候減一之後,mWeak=0,說明弱引用沒有指向目標對象,額可以考慮釋放記憶體 //OBJECT_LIFETIME_xxxxxx代表生命週期 if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { if (impl->mStrong == INITIAL_STRONG_VALUE) { delete impl->mBase;//釋放目標對象 } else {//進入這個條件判斷 delete impl;//釋放weakref_impl對象 } } else { impl->mBase->onLastWeakRef(id); if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) { delete impl->mBase; //釋放目標對象 } }}
到此為止一個整體的過程結束,總結一下 RefBase中有一個weakref_impl類型的mRefs,weakref_impl類型維護兩個重要的變數mWeak和mStrong。而RefBase的構造就是建立一個weakref_impl對象並賦值給mRefs成員變數,weakref_impl的建構函式主要操作就是為mStrong和mWeak設定初始值,其中mStrong為0x1000000,mWeak為0 sp構造時mStrong和mWeak都加一,sp析構時mStrong和mWeak都減一 wp構造時mWeak加一,wp析構時mWeak減一 RefBase徹底清除包括目標對象和weakref_impl類型的mRefs兩者的清除。如果flag=0時,也就是OBJECT_LIFETIME_STRONG時,如果mStrong=0,刪除實際目標對象;如果mWeak=0,刪除weakref_impl類型的mRefs
3 wp其他知識
3.1 wp變成sp 在wp還有一個重要的函數promote,它的作用就是得到一個sp,看下面 一個例子 int main(){ A *pA = new A(); wp wpA(pA); sp spA = wpA.promote();} 其中wp構造之後mWeak變成1,mStrong還是初始值0x100000。再看promote函數,在RefBase.h檔案中: templatesp wp::promote() const{ sp result; if (m_ptr && m_refs->attemptIncStrong(&result)) {//關鍵函數是attempIncStrong函數 result.set_pointer(m_ptr);//將m_ptr賦值給result的m_ptr } return result;}這裡調用了attemptIncStrong函數,它在RefBase.cpp檔案中: bool RefBase::weakref_type::attemptIncStrong(const void* id){ incWeak(id);//mWeak加一,變成2 weakref_impl* const impl = static_cast(this); //取出mStrong的值,這裡是0x1000000 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) { bool allow;//判斷是否允許從wp產生sp if (curCount == INITIAL_STRONG_VALUE) { // Attempting to acquire first strong reference... this is allowed // if the object does NOT have a longer lifetime (meaning the // implementation doesn't need to see this), or if the implementation // allows it to happen. allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); } else { // Attempting to revive the object... this is allowed // if the object DOES have a longer lifetime (so we can safely // call the object with only a weak ref) and the implementation // allows it to happen. allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); } if (!allow) { 如果不允許,則mWeak減一,並返回 decWeak(id); return false; } // 如果允許則mStrong加一 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->addStrongRef(id);//調試使用,沒有具體操作 if (curCount == INITIAL_STRONG_VALUE) { android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong); impl->mBase->onFirstRef(); } //返回成功 return true;}promote執行之後mStrong和mWeak都加一,這時mWeak為2,mStrong為1,最終並返回一個sp對象,該對象指向wp指向的目的對象。
3.2 extendObjectLifetime RefBase中extendObjectLifetime函數的作用是延長對象的生命週期,不再受到強弱引用的影響。 void RefBase::extendObjectLifetime(int32_t mode){ android_atomic_or(mode, &mRefs->mFlags);//or運算}其中定義了兩種生命週期,mFlag預設是0,也就是Strong,可以設定為weak,設定完之後,對於刪除目標對象時的判斷有所不同。 //! Flags for extendObjectLifetime() enum { OBJECT_LIFETIME_STRONG = 0x0000, OBJECT_LIFETIME_WEAK = 0x0001, OBJECT_LIFETIME_MASK = 0x0001 };