1. 程式人生 > >Android智慧指標分析(sp、wp)

Android智慧指標分析(sp、wp)

在Android native編寫程式碼時,會經常接觸到sp、wp,sp並不是smart pointer的意思,而是strong point;wp就是weak pointer。這兩個概念比較像JAVA中的強弱引用,使用sp和wp可以讓程式設計人員不需要再關係記憶體的釋放問題,防止記憶體洩露。下面先來看它們的類關係圖:


要實現記憶體的自動釋放,sp、wp必須結合RefBase這個類來使用,在Android中,大多數類的最上層基類都是RefBase類。我們舉個簡單的例子,然後順著這個例子來分析RefBase、sp和wp四種不同的應用,並介紹其實現。

  1. class A : public RefBase  
  2. {  
  3. }  

上面定義一個類A,繼承與RefBase,下面我們首先來看RefBases的建構函式:
  1. RefBase::RefBase()  
  2.     : mRefs(new weakref_impl(this))  
  3. {  
  4. }  
  5.     weakref_impl(RefBase* base)  
  6.         : mStrong(INITIAL_STRONG_VALUE)  
  7.         , mWeak(0)  
  8.         , mBase(base)  
  9.         , mFlags(0)  
  10.     {  
  11.     }  

在RefBase中,首先構造weakref_impl物件,在weakref_impl對mStong和mWeak進行強弱引用計數賦初始值,INITIAL_STRONG_VALUE是0X10000000,這裡不直接賦初始值為0,是方便我們區分,0到底是初始化的值,還是在sp釋放後再變為0,方便做不同的處理。

列舉第一種應用:只有sp指標,沒有wp指標的應用

{

    sp<A> spA(new A);

}

首先來看sp的建構函式:

  1. template<typename T>  
  2. sp<T>::sp(T* other)  
  3.         : m_ptr(other) {  
  4.     if (other)  
  5.         other->incStrong(this);  
  6. }  

首先將實際的A物件的指標賦給m_ptr,然後呼叫A物件的incStrong方法,由上面的類圖關係,我們知道這裡會呼叫RefBase的incStrong方法:
  1. void RefBase::incStrong(
    constvoid* id) const
  2. {  
  3.     weakref_impl* const refs = mRefs;  
  4.     refs->incWeak(id);  
  5.     refs->addStrongRef(id);  
  6.     const int32_t c = android_atomic_inc(&refs->mStrong);  
  7.     ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);  
  8.     if (c != INITIAL_STRONG_VALUE)  {  
  9.         return;  
  10.     }  
  11.     android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);  
  12.     refs->mBase->onFirstRef();  
  13. }  

這裡首先呼叫weakref_impl的incWeak方法來增加弱指標引用數;addStrongRef用於debug版本,正式版中沒有實現;android_atomic_inc是一個原子操作,增加refs->mStrong的強指標引用數,並返回原值;如果是第一次引用改物件,則還會呼叫A物件的onFirstRef方法。首先來看incWeak的實現:
  1. void RefBase::weakref_type::incWeak(constvoid* id)  
  2. {  
  3.     weakref_impl* const impl = static_cast<weakref_impl*>(this);  
  4.     impl->addWeakRef(id);  
  5.     const int32_t c = android_atomic_inc(&impl->mWeak);  
  6.     ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref"this);  
  7. }  

這裡還是呼叫android_atomic_inc去增加weakref_impl的mWeak計數。經過建構函式,mStong和mWeak的計數都變成了1。當spA物件退出作用域以後,就會呼叫其解構函式來釋放這個物件:

  1. template<typename T>  
  2. sp<T>::~sp() {  
  3.     if (m_ptr)  
  4.         m_ptr->decStrong(this);  
  5. }  
  6. void RefBase::decStrong(constvoid* id) const
  7. {  
  8.     weakref_impl* const refs = mRefs;  
  9.     refs->removeStrongRef(id);  
  10.     const int32_t c = android_atomic_dec(&refs->mStrong);  
  11.     ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);  
  12.     if (c == 1) {  
  13.         refs->mBase->onLastStrongRef(id);  
  14.         if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {  
  15.             deletethis;  
  16.         }  
  17.     }  
  18.     refs->decWeak(id);  
  19. }  

sp物件的解構函式呼叫RefBase的decStrong來減少強弱引用指標計數。weakref_impl的removeStrongRef用於debug版本;呼叫android_atomic_dec減少mStrong計數並返回原值,如果mStrong之前為1了,這是再減少,說明已經沒有其它sp指標引用了,這時候首先呼叫A物件的onLastStrongRef方法,如果Flag設定的是當前物件的生命週期由sp指標決定(這也是default的設定),則釋放掉A物件;然後呼叫weakref_impl的decWeak去減少弱引用指標計數:
  1. void RefBase::weakref_type::decWeak(constvoid* id)  
  2. {  
  3.     weakref_impl* const impl = static_cast<weakref_impl*>(this);  
  4.     impl->removeWeakRef(id);  
  5.     const int32_t c = android_atomic_dec(&impl->mWeak);  
  6.     ALOG_ASSERT(c >= 1, "decWeak called on %p too many times"this);  
  7.     if (c != 1) return;  
  8.     if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {  
  9.         if (impl->mStrong == INITIAL_STRONG_VALUE) {  
  10.             delete impl->mBase;  
  11.         } else {  
  12.             delete impl;  
  13.         }  
  14.     } else {  
  15.         impl->mBase->onLastWeakRef(id);  
  16.         if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {  
  17.             delete impl->mBase;  
  18.         }  
  19.     }  
  20. }  
weakref_impl的removeWeakRef方法也是用於debug版本;然後呼叫android_atomic_dec去減少mWeak計數,如果mWeak之前不等於1,表示還有其它的wp引用,這裡就直接返回。如果這裡的mWeak等於1,說明已經沒有其它sp和wp的引用了,所以這裡要去釋放A物件和weakref_impl物件。

來看判斷是否釋放的邏輯,如果Flag設定當前物件的生命週期由sp指標決定,並且之前沒有初始化過任何sp物件,則直接刪除A物件;如果之前由初始化過sp物件,則刪除weakref_impl本身,A物件會在RefBase的decStrong中被釋放。如果Flag設定當前物件的生命週期由wp指標決定,則首先呼叫A物件的onLastWeakRef方法,然後刪除物件A。在刪除物件A的時候,都會呼叫RefBase的解構函式,我們再來分析RefBase的系統函式:

  1. RefBase::~RefBase()  
  2. {  
  3.     if (mRefs->mStrong == INITIAL_STRONG_VALUE) {  
  4.         delete mRefs;  
  5.     } else {  
  6.         if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {  
  7.             if (mRefs->mWeak == 0) {  
  8.                 delete mRefs;  
  9.             }  
  10.         }  
  11.     }  
  12.     const_cast<weakref_impl*&>(mRefs) = NULL;  
  13. }  

如果沒有初始化過sp物件,則刪除mRefs物件;如果Flag設定當前物件的生命週期由wp指標決定並且mWeak計數為0,也刪除mRefs物件。

列舉第二種應用:只有wp指標,沒有sp指標的應用

{

    wp<A> wpA(new A);

}

首先來看wp的構造方法:

  1. template<typename T>  
  2. wp<T>::wp(const sp<T>& other)  
  3.     : m_ptr(other.m_ptr)  
  4. {  
  5.     if (m_ptr) {  
  6.         m_refs = m_ptr->createWeak(this);  
  7.     }  
  8. }  

首先將A物件的指標賦予給m_ptr,可見在sp和wp中都儲存有A物件的實際指標,但wp中並沒有過載"->",所以wp並不能直接呼叫A物件的方法,並且由前面sp的知識,我們知道,在decStrong的時候,有可能A物件會被釋放,並不會care 是否存在wp的引用。然後呼叫A物件的createWeak方法,實際上是呼叫RefBase的這個方法。注意的是在wp中,m_refs是weakref_type的指標;而在RefBase中,mRefs是weakref_impl的指標,所以在下面的程式碼返回時要注意型別的轉型。
  1. RefBase::weakref_type* RefBase::createWeak(constvoid* id) const
  2. {  
  3.     mRefs->incWeak(id);  
  4.     return mRefs;  
  5. }  
  6. void RefBase::weakref_type::incWeak(constvoid* id)  
  7. {  
  8.     weakref_impl* const impl = static_cast<weakref_impl*>(this);  
  9.     impl->addWeakRef(id);  
  10.     const int32_t c = android_atomic_inc(&impl->mWeak);  
  11.     ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref"this);  
  12. }  

這裡只會增加mWeak 計數,這是mStrong等於INITIAL_STRONG_VALUE,mWeak等於1。當wpA退出作用域後,呼叫wp的解構函式:
  1. template<typename T>  
  2. wp<T>::~wp()  
  3. {  
  4.     if (m_ptr) m_refs->decWeak(this);  
  5. }  

decWeak函式我們上面講過,如果Flag設定當前物件的生命週期由sp指標決定,並且之前沒有初始化過任何sp物件,則直接刪除A物件;並在RefBase的解構函式中取釋放mRefs物件。

列舉第三種應用:既有sp指標,又有wp指標的應用

{

   sp<A> spA(new A)

    wp<A> wpA(spA);

}


從上面它們的建構函式我們知道,這是mStong等於1,mWeak等於2。在spA和wpA退出作用域時,首先呼叫wp的解構函式,再呼叫sp的解構函式。在wp解構函式中,只會減少mWeak計數為1,然後就然後了。再到sp的解構函式中,就和我們前面介紹的第一種應用一樣了。

列舉第四種應用:wp指標如果呼叫物件的方法

前面說過在wp中並沒有過載"->",所以wp並不能直接呼叫A物件的方法,並且由前面sp的知識,我們知道,在decStrong的時候,有可能A物件會被釋放,所以在wp中想要呼叫A物件的方法,必須獲得sp指標,這是通過wp的promote方法實現的:

  1. template<typename T>  
  2. sp<T> wp<T>::promote() const
  3. {  
  4.     sp<T> result;  
  5.     if (m_ptr && m_refs->attemptIncStrong(&result)) {  
  6.         result.set_pointer(m_ptr);  
  7.     }  
  8.     return result;  
  9. }  

這裡呼叫weakref_type的attemptIncStrong方法去嘗試增加mStrong計數:
  1. bool RefBase::weakref_type::attemptIncStrong(constvoid* id)  
  2. {  
  3.     incWeak(id);  
  4.     weakref_impl* const impl = static_cast<weakref_impl*>(this);  
  5.     int32_t curCount = impl->mStrong;  
  6.     while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {  
  7.         if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {  
  8.             break;  
  9.         }  
  10.         curCount = impl->mStrong;  
  11.     }  
  12.     if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {  
  13.         if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {  
  14.             if (curCount <= 0) {  
  15.                 decWeak(id);  
  16.                 returnfalse;  
  17.             }  
  18.             while (curCount > 0) {  
  19.                 if (android_atomic_cmpxchg(curCount, curCount + 1,  
  20.                         &impl->mStrong) == 0) {  
  21.                     break;  
  22.                 }  
  23.                 curCount = impl->mStrong;  
  24.             }  
  25.             if (curCount <= 0) {  
  26.                 decWeak(id);  
  27.                 returnfalse;  
  28.             }  
  29.         } else {  
  30.             if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {  
  31.                 decWeak(id);  
  32.                 returnfalse;  
  33.             }  
  34.             curCount = android_atomic_inc(&impl->mStrong);  
  35.         }  
  36.         if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {  
  37.             impl->mBase->onLastStrongRef(id);  
  38.         }  
  39.     }  
  40.     impl->addStrongRef(id);  
  41.     curCount = impl->mStrong;  
  42.     while (curCount >= INITIAL_STRONG_VALUE) {  
  43.         if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,  
  44.                 &impl->mStrong) == 0) {  
  45.             break;  
  46.         }  
  47.         curCount = impl->mStrong;  
  48.     }  
  49.     returntrue;  
  50. }  

首先呼叫incWeak來增加mWeak計數,因為這裡需要獲取sp指標,在sp的建構函式我們知道,會同時增加mWeak和mStrong值。然後根據mStong值分兩種情況討論:

   1. 當前面存在sp的引用,即curCount > 0 && curCount != INITIAL_STRONG_VALUE,這時直接讓mStrong加1。

   2.當前面不存在sp的引用,需要結合Flag去判斷。又分為以下幾種情況:

           一. Flag = OBJECT_LIFETIME_STRONG,並且curCount等於0。說明之前的sp物件已經釋放,由前面的知識我們知道,在釋放sp物件的同時也會釋放物件A,所以這裡呼叫decWeak來釋放前面增加的一次mWeak值並返回false

           二.Flag = OBJECT_LIFETIME_STRONG,並且curCount = INITIAL_STRONG_VALUE,說明前面沒有sp引用,這時我們可以增加mStrong值。

           三.Flag = OBJECT_LIFETIME_WEAK,並且curCount <= 0 || curCount == INITIAL_STRONG_VALUE,則呼叫RefBase的onIncStrongAttempted去嘗試增加mStrong值

當上面任何一種情況增加了mStrong值以後,mSrong的值可能大於INITIAL_STRONG_VALUE,我們需要去修正mStrong,就是通過減去INITIAL_STRONG_VALUE計算。當attemptIncStrong返回true時,promote方法就會呼叫sp的set_pointer方法去設定StrongPointer中的實際A物件的指標。接下來就可以通過sp呼叫相關的方法了。


相關推薦

Android智慧指標分析spwp

在Android native編寫程式碼時,會經常接觸到sp、wp,sp並不是smart pointer的意思,而是strong point;wp就是weak pointer。這兩個概念比較像JAVA中的強弱引用,使用sp和wp可以讓程式設計人員不需要再關係記憶體的釋放

C++中的三種智慧指標分析RAII思想

智慧指標 首先我們在理解智慧指標之前我們先了解一下什麼是RAII思想。RAII(Resource Acquisition Is I

【 專欄 】- 資料分析pythonSQL

資料分析(python、SQL) 隨著大資料時代的到來,各行各業有大量的資料分析需求。python是一門“新世界”的主流語言,有很強大的第三方庫(Pandas,Numpy,Matplotlib)。本欄主要基於python語言,整理

Android開發效能優化記錄自用

雖然做Android開發已經有一段時間了,但是開發過程中經常是忙著實現功能,卻忽略了效能上的優化。以下都是來自於各位前輩的總結。特此總結記錄一下。  一、Android效能優化之佈局優化技巧 佈局優化就是用最少的view實現一樣的效果layout。最少的view也就是

Qt原始碼之d指標分析QObject,QObjectPrivate

前言   閱讀過Qt原始碼的同學一定對d指標不陌生,前段時間其實寫過一次關於Qt d指標的文章,但是感覺不夠透徹就刪除了,這次打算徹底地詳細地再分析一次。 Pimpl機制   對Pimpl機制不熟悉的先熟悉下Pimpl機制Pimpl機制。Qt的d指標其實主要還是

智慧指標例項引用計數型

如果在多個類的例項中共享同一塊堆記憶體,指標的操作會相當繁瑣,一不小心就會出現野指標,試想一塊記憶體被其中一個指標釋放了,另外幾個物件的指標仍然指向他的情況,相當可怕!這時候就需要用帶有引用計數的智慧指標管理了! 《C++ Primer》上給出了兩種實現方式: 1.利用友元

Android快取原始碼分析DiskLruCache,LruCache

       Android的常見的快取有三種:網路快取、檔案快取、記憶體快取。這裡網路快取我不考慮,我們看下檔案快取(DiskLruCache)、記憶體快取(LruCache)的原始碼是咋實現的。 一,LRU演算法        檔案快取(DiskLr

mplayer的執行過程分析本地網路

main函式流程分析,分本地和網路兩種情況  1. 本地檔案的播放,以mp3檔案的播放為例,命令列輸入:mplayer test.mp3     只給出關鍵步驟  2. open_stream,                                        開啟檔案流          ope

Android智慧指標SP WP使用方法介紹

Android手機作業系統既然是開源的作業系統。那麼在具體的資料夾中就會存放著各種相關功能的開原始碼。我們在使用的時候可以根據這些原始碼進行相應的修改就能輕鬆的完成我們所需的功能。在這裡大家就一起來看看Android智慧指標的相關原始碼解讀以及應用方法。 在Androi

Android架構分析Android智慧指標

作者:劉昊昱  Android版本:4.4.2 在上一篇文章中,我們分析了Android智慧指標中的強指標sp,本文我們來分析弱指標wp。為什麼需要弱指標wp呢?我們來考慮下面一種場景:有兩個類CParent和CChild,CParent類中有一個智慧指標指向CChil

Android智慧指標 (sp & wp)

http://bbs.chinabyte.com/thread-452223-1-1.html 在Android的原始碼中,經常會看到形如:sp<xxx>、wp<xxx>這樣的型別定義,這其實是Android中的智慧 指標。智慧指標是C++中的一個概念,通過基於引用計數的方法,解決物

關於android中的單位dpsp

字號 兩個 metrics 不一定 ont ace white 超級 style android讓人頭疼的適配問題。 --------- Android 中的單位大概有這些: 經常使用的dip、sp。有時候用到px。 --------- 介紹兩個類: Type

Android-Application被回收引發空指標異常分析消滅全域性變數

問題描述 App切換到後臺後,一段時間不操作,再切回來,很容易就發生崩潰(配置低的手機這種問題出現更頻繁)。究其原因,是因為常常把物件儲存在Application裡面,而App切換到後臺後,程序很容易就被系統回收了,下次切換回來的時候App頁面再重建,但是系統

Android 之 三級快取記憶體!!!本地網路及記憶體LruCache擴充套件 及原始碼分析--- 學習和程式碼講解

一. 三級快取簡介 如上圖所示,目前App中UI介面經常會涉及到圖片,特別是像“今日關注”新聞這類app中,圖片運用的機率十分頻繁。當手機上需要顯示大量圖片類似listView、gridView控制元件並且使用者會上下滑動,即將瀏覽過的圖片又載入一遍,

Android查缺補漏線程篇-- AsyncTask的使用及原理詳細分析

catch 返回 rri 理解 ams tee ive lean keyword 本文作者:CodingBlock 文章鏈接:http://www.cnblogs.com/codingblock/p/8515304.html 一、AsyncTask的使用 AsyncT

HashMap底層原理分析putget方法

return sta rec oca ati 技術分享 AI TP load 1、HashMap底層原理分析(put、get方法) HashMap底層是通過數組加鏈表的結構來實現的。HashMap通過計算key的hashCode來計算hash值,只要hashCode一樣

Android 源碼分析 Dalvik 虛擬機創建過程

比較 read 快速 查找 dal spa instr threads heap 一. 介紹Dalvik   1.java的運行需要JVM(後面有大量篇幅介紹),同樣android中使用了java語言,也需要一個VM。針對手機處理器和內存等硬件資源不足而推出來的一款VM,為

多尺度幾何分析RidgeletCurveletContourletBandeletWedgeletBeamlet

連續 根據 向上 mman 變化 獲得 技術分享 標準 重要 稀疏基的討論已經持續了近一個月了,這次討論多尺度幾何分析。但由於下面討論的這些變換主要面向圖像,而本人現在主要關註於一維信號處理,所以就不對這些變換深入討論了,這裏僅從眾參考文獻中摘抄整理一些相關

Android ADB 原始碼分析

前言 之前分析的兩篇文章 Android Adb 原始碼分析(一) 嵌入式Linux:Android root破解原理(二)   寫完之後,都沒有寫到相關的實現程式碼,這篇文章寫下ADB的通訊流程的一些細節 看這篇文章之前,請先閱讀 Linux的SOCKET

常見檔案系統對比分析NFSGFSNAS

本章我們來分析下常見檔案系統對比分析,這裡我們主要講解NFS、GFS、NAS相關概念及區別。 《一》NFS NFS 是Network File System的縮寫,中文解釋是網路檔案系統,它是FreeBSD支援的檔案系統中的一種,它允許網路中的計算機之間通過TCP/IP網路共享資源。在N