Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析(3)...

?提供引用計數器的類RefBase我們就暫時介紹到這里,后面我們再結合智能指針類一起分析,現在先來看看強指針類和弱指針類的定義。強指針類的定義我們在前面介紹輕量級指針的時候已經見到了,就是sp類了,這里就不再把它的代碼列出來了。我們來看看它的構造函數的實現:

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

??這里傳進來的參數other一定是繼承于RefBase類的,因此,在函數的內部,它調用的是RefBase類的incStrong函數,它定義在frameworks/base/libs/utils/RefBase.cpp文件中:?

 
  1. void?RefBase::incStrong(const?void*?id)?const???
  2. {???
  3. ????weakref_impl*?const?refs?=?mRefs;???
  4. ????refs->addWeakRef(id);???
  5. ????refs->incWeak(id);???
  6. ????refs->addStrongRef(id);????
  7. ???
  8. ????const?int32_t?c?=?android_atomic_inc(&refs->mStrong);????
  9. ????LOG_ASSERT(c?>?0,?"incStrong()?called?on?%p?after?last?strong?ref",?refs);???
  10. ???
  11. ????#if?PRINT_REFS????
  12. ????LOGD("incStrong?of?%p?from?%p:?cnt=%d\n",?this,?id,?c);???
  13. ????#endif????
  14. ???
  15. ????if?(c?!=?INITIAL_STRONG_VALUE)?{????
  16. ????????return;????
  17. ????}????
  18. ???
  19. ????android_atomic_add(-INITIAL_STRONG_VALUE,?&refs->mStrong);????
  20. ????const_cast<RefBase*>(this)->onFirstRef();???
  21. }???

??成員變量mRefs是在RefBase類的構造函數中創建的:

 
  1. RefBase::RefBase()???
  2. ????:?mRefs(new?weakref_impl(this))???
  3. {???
  4. //????LOGV("Creating?refs?%p?with?RefBase?%p\n",?mRefs,?this);???
  5. }???

?在這個incStrong函數中,主要做了三件事情:

?一是增加弱引用計數:

 
  1. refs->addWeakRef(id);???
  2. refs->incWeak(id);???

?二是增加強引用計數:

 
  1. refs->addStrongRef(id);???
  2. const?int32_t?c?=?android_atomic_inc(&refs->mStrong);???

? ?三是如果發現是首次調用這個對象的incStrong函數,就會調用一個這個對象的onFirstRef函數,讓對象有機會在對象被首次引用時做一些處理邏輯:

 
  1. if?(c?!=?INITIAL_STRONG_VALUE)??{???
  2. ????return;???
  3. }???
  4. ???
  5. android_atomic_add(-INITIAL_STRONG_VALUE,?&refs->mStrong);???
  6. const_cast<RefBase*>(this)->onFirstRef();???

?這里的c返回的是refs->mStrong加1前的值,如果發現等于INITIAL_STRONG_VALUE,就說明這個對象的強引用計數是第一次被增加,因此,refs->mStrong就是初始化為INITIAL_STRONG_VALUE的,它的值為:#define?INITIAL_STRONG_VALUE?(1<<28)??
? ?這個值加1后等于1<<28 + 1,不等于1,因此,后面要再減去-INITIAL_STRONG_VALUE,于是,refs->mStrong就等于1了,就表示當前對象的強引用計數值為1了,這與這個對象是第一次被增加強引用計數值的邏輯是一致的。
? ? ? ? 回過頭來看弱引用計數是如何增加的,首先是調用weakref_impl類的addWeakRef函數,我們知道,在Release版本中,這個函數也不做,而在Debug版本中,這個函數增加了一個ref_entry對象到了weakref_impl對象的mWeakRefs列表中,表示此weakref_impl對象的弱引用計數被增加了一次。接著又調用了weakref_impl類的incWeak函數,真正增加弱引用計數值就是在這個函數實現的了,weakref_impl類的incWeak函數繼承于其父類weakref_type的incWeak函數:

 
  1. void?RefBase::weakref_type::incWeak(const?void*?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. ????LOG_ASSERT(c?>=?0,?"incWeak?called?on?%p?after?last?weak?ref",?this);???
  7. }???

? 增加弱引用計數是下面語句執行的:const?int32_t?c?=?android_atomic_inc(&impl->mWeak); ?? ?但是前面為什么又調用了一次addWeakRef函數呢?前面不是已經調用過了嗎?在Release版本中,因為weakref_impl類的addWeakRef函數是空實現,這里再調用一次沒有什么害處,但是如果在Debug版本,豈不是冗余了嗎?搞不清,有人問過負責開發Android系統Binder通信機制模塊的作者Dianne Hackborn這個問題,他是這樣回答的:?

?http://groups.google.com/group/android-platform/browse_thread/thread/cc641db8487dd83

?? ? ? ?Ah I see. ?Well the debug code may be broken, though I wouldn't leap to that?
?? ? ? ?conclusion without actually testing it; I know it has been used in the?
?? ? ? ?past. ?Anyway, these things get compiled out in non-debug builds, so there?
?? ? ? ?is no reason to change them unless you are actually trying to use this debug?
?? ? ? ?code and it isn't working and need to do this to fix it.?

?? ? ? ?既然他也不知道怎么回事,我們也不必深究了,知道有這么回事就行。

?? ? ? ?這里總結一下強指針類sp在其構造函數里面所做的事情就是分別為目標對象的強引用計數和弱引和計數增加了1。

? ? ? ? 再來看看強指針類的析構函數的實現:

 
  1. template<typename?T>???
  2. sp<T>::~sp()???
  3. {???
  4. ????if?(m_ptr)?m_ptr->decStrong(this);???
  5. }???

? ? ?同樣,這里的m_ptr指向的目標對象一定是繼承了RefBase類的,因此,這里調用的是RefBase類的decStrong函數,這也是定義在frameworks/base/libs/utils/RefBase.cpp文件中:

 
  1. void?RefBase::decStrong(const?void*?id)?const???
  2. {???
  3. ????weakref_impl*?const?refs?=?mRefs;???
  4. ????refs->removeStrongRef(id);???
  5. ????const?int32_t?c?=?android_atomic_dec(&refs->mStrong);???
  6. #if?PRINT_REFS???
  7. ????LOGD("decStrong?of?%p?from?%p:?cnt=%d\n",?this,?id,?c);???
  8. #endif???
  9. ????LOG_ASSERT(c?>=?1,?"decStrong()?called?on?%p?too?many?times",?refs);???
  10. ????if?(c?==?1)?{???
  11. ????????const_cast<RefBase*>(this)->onLastStrongRef(id);???
  12. ????????if?((refs->mFlags&OBJECT_LIFETIME_WEAK)?!=?OBJECT_LIFETIME_WEAK)?{???
  13. ????????????delete?this;???
  14. ????????}???
  15. ????}???
  16. ????refs->removeWeakRef(id);???
  17. ????refs->decWeak(id);???
  18. }???

??這里的refs->removeStrongRef函數調用語句是對應前面在RefBase::incStrong函數里的refs->addStrongRef函數調用語句的,在Release版本中,這也是一個空實現函數,真正實現強引用計數減1的操作是下面語句:

 
  1. const?int32_t?c?=?android_atomic_dec(&refs->mStrong);???

? ?如果發現減1前,此對象的強引用計數為1,就說明從此以后,就再沒有地方引用這個目標對象了,這時候,就要看看是否要delete這個目標對象了:

 
  1. if?(c?==?1)?{???
  2. ????const_cast<RefBase*>(this)->onLastStrongRef(id);???
  3. ????if?((refs->mFlags&OBJECT_LIFETIME_WEAK)?!=?OBJECT_LIFETIME_WEAK)?{???
  4. ????????delete?this;???
  5. ????}???
  6. }???

??在強引用計數為0的情況下,如果對象的標志位OBJECT_LIFETIME_WEAK被設置了,就說明這個對象的生命周期是受弱引用計數所控制的,因此,這時候就不能delete對象,要等到弱引用計數也為0的情況下,才能delete這個對象。

?

? ? ? ? 接下來的ref->removeWeakRef函數調用語句是對應前面在RefBase::incStrong函數里的refs->addWeakRef函數調用語句的,在Release版本中,這也是一個空實現函數,真正實現強引用計數減1的操作下面的refs->decWeak函數,weakref_impl類沒有實現自己的decWeak函數,它繼承了weakref_type類的decWeak函數:

 
  1. void?RefBase::weakref_type::decWeak(const?void*?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. ????LOG_ASSERT(c?>=?1,?"decWeak?called?on?%p?too?many?times",?this);???
  7. ????if?(c?!=?1)?return;???
  8. ???
  9. ????if?((impl->mFlags&OBJECT_LIFETIME_WEAK)?!=?OBJECT_LIFETIME_WEAK)?{???
  10. ????????if?(impl->mStrong?==?INITIAL_STRONG_VALUE)???
  11. ????????????delete?impl->mBase;???
  12. ????????else?{???
  13. //????????????LOGV("Freeing?refs?%p?of?old?RefBase?%p\n",?this,?impl->mBase);???
  14. ????????????delete?impl;???
  15. ????????}???
  16. ????}?else?{???
  17. ????????impl->mBase->onLastWeakRef(id);???
  18. ????????if?((impl->mFlags&OBJECT_LIFETIME_FOREVER)?!=?OBJECT_LIFETIME_FOREVER)?{???
  19. ????????????delete?impl->mBase;???
  20. ????????}???
  21. ????}???
  22. }???

? ? 這里又一次調用了weakref_impl對象的removeWeakRef函數,這也是和RefBase::weakref_type::incWeak函數里面的impl->addWeakRef語句所對應的,實現弱引用計數減1的操作是下面語句:const?int32_t?c?=?android_atomic_dec(&impl->mWeak); ?

?減1前如果發現不等于1,那么就什么也不用做就返回了,如果發現等于1,就說明當前對象的弱引用計數值為0了,這時候,就要看看是否要delete這個對象了:

 
  1. if?((impl->mFlags&OBJECT_LIFETIME_WEAK)?!=?OBJECT_LIFETIME_WEAK)?{???
  2. ????if?(impl->mStrong?==?INITIAL_STRONG_VALUE)???
  3. ????????delete?impl->mBase;???
  4. ????else?{???
  5. //??????LOGV("Freeing?refs?%p?of?old?RefBase?%p\n",?this,?impl->mBase);???
  6. ????????delete?impl;???
  7. ????}???
  8. }?else?{???
  9. ????impl->mBase->onLastWeakRef(id);???
  10. ????if?((impl->mFlags&OBJECT_LIFETIME_FOREVER)?!=?OBJECT_LIFETIME_FOREVER)?{???
  11. ????????delete?impl->mBase;???
  12. ????}???
  13. }???

??如果目標對象的生命周期是不受弱引用計數控制的,就執行下面語句:

 
  1. if?(impl->mStrong?==?INITIAL_STRONG_VALUE)???
  2. ????delete?impl->mBase;???
  3. else?{???
  4. //??LOGV("Freeing?refs?%p?of?old?RefBase?%p\n",?this,?impl->mBase);???
  5. ????delete?impl;???
  6. }???

? ? ?這個代碼段是什么意思呢?這里是減少對象的弱引用計數的地方,如果調用到這里,那么就說明前面一定有增加過此對象的弱引用計數,而增加對象的弱引用計數有兩種場景的,一種場景是增加對象的強引用計數的時候,會同時增加對象的弱引用計數,另一種場景是當我們使用一個弱指針來指向對象時,在弱指針對象的構造函數里面,也會增加對象的弱引用計數,不過這時候,就只是增加對象的弱引用計數了,并沒有同時增加對象的強引用計數。因此,這里在減少對象的弱引用計數時,就要分兩種情況來考慮。

?

? ? ? ? 如果是前一種場景,這里的impl->mStrong就必然等于0,而不會等于INITIAL_STRONG_VALUE值,因此,這里就不需要delete目標對象了(impl->mBase),因為前面的RefBase::decStrong函數會負責delete這個對象。這里唯一需要做的就是把weakref_impl對象delete掉,但是,為什么要在這里delete這個weakref_impl對象呢?這里的weakref_impl對象是在RefBase的構造函數里面new出來的,理論上說應該在在RefBase的析構函數里delete掉這個weakref_impl對象的。在RefBase的析構函數里面,的確是會做這件事情:

 
  1. RefBase::~RefBase()???
  2. {???
  3. //????LOGV("Destroying?RefBase?%p?(refs?%p)\n",?this,?mRefs);???
  4. ????if?(mRefs->mWeak?==?0)?{???
  5. //????????LOGV("Freeing?refs?%p?of?old?RefBase?%p\n",?mRefs,?this);???
  6. ????????delete?mRefs;???
  7. ????}???
  8. }???

? ?但是不要忘記,在這個場景下,目標對象是前面的RefBase::decStrong函數delete掉的,這時候目標對象就會被析構,但是它的弱引用計數值尚未執行減1操作,因此,這里的mRefs->mWeak == 0條件就不成立,于是就不會delete這個weakref_impl對象,因此,就延遲到執行這里decWeak函數時再執行。?

如果是后一種情景,這里的impl->mStrong值就等于INITIAL_STRONG_VALUE了,這時候由于沒有地方會負責delete目標對象,因此,就需要把目標對象(imp->mBase)delete掉了,否則就會造成內存泄漏。在delete這個目標對象的時候,就會執行RefBase類的析構函數,這時候目標對象的弱引用計數等于0,于是,就會把weakref_impl對象也一起delete掉了。





本文轉自 Luoshengyang 51CTO博客,原文鏈接:http://blog.51cto.com/shyluo/966562,如需轉載請自行聯系原作者

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/279513.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/279513.shtml
英文地址,請注明出處:http://en.pswp.cn/news/279513.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ref:下一個項目為什么要用 SLF4J

ref:http://blog.mayongfa.cn/267.html 阿里巴巴 Java 開發手冊 前幾天阿里巴巴在云棲社區首次公開阿里官方Java代碼規范標準&#xff0c;就是一個PDF手冊&#xff0c;有命名規范&#xff0c;讓你知道自己原來取的每一個類名、變量名都是爛名字&#xff0c;真替你家未來孩子擔心…

洛谷P5055 【模板】可持久化文藝平衡樹(FHQ Treap)

題面 傳送門 題解 日常敲板子.jpg //minamoto #include<bits/stdc.h> #define R register #define inline __inline__ __attribute__((always_inline)) #define fp(i,a,b) for(R int i(a),I(b)1;i<I;i) #define fd(i,a,b) for(R int i(a),I(b)-1;i>I;--i) #define …

計算機突然藍屏無法啟動_為什么計算機無法立即啟動?

計算機突然藍屏無法啟動With the newer, more powerful hardware and improved operating systems that we have available to use these days, why does it still take as long as it does to fully boot a computer up each time? 借助我們如今可以使用的更新&#xff0c;更…

CCNA課堂練習:OSPF的介紹及配置

CCNA淺談OSPF的配置 今天我們來談談路由器OSPF的配置&#xff0c;那我先來介紹一下OSPF的特點&#xff1a;1、對網絡發生的變化能夠快速響應2、當網絡發生變化的時候發送觸發式更新?3、支持VLAN 4、管理方便ospf引用了區域的概念&#xff0c;區域分兩種&#xff1a;1、骨干區域…

vcenter 6.7 (vcsa)部署指南

閑言少敘&#xff0c;直達心靈。 一、部署提要1.1 vCenter Server Appliance(VCSA )6.7下載地址https://pan.baidu.com/s/1WUShsC23E2qIIBg7MPR87w 6lzb 二、安裝部署VCSA分為兩個階段安裝&#xff0c;下面我們開始第一階段2.1 打開之后&#xff0c;直接點擊安裝按鈕2.2部署設備…

如何停止Internet Explorer 11的建議站點?

Internet Explorer automatically suggests addresses and search results based on the partial address you’re typing out. If this feature irritates you, read on as we learn how to turn it off. Internet Explorer會根據您鍵入的部分地址自動建議地址和搜索結果。 如…

什么是SG?+SG模板

先&#xff0c;定義一下 狀態Position P 先手必敗 N x先手必勝 操作方法&#xff1a; 反向轉移 相同狀態 不同位置 的一對 相當于無 對于ICG游戲&#xff0c;我們可以將游戲中每一個可能發生的局面表示為一個點。并且若存在局面i和局面j&#xff0c;且j是i的后繼局面(即局面i可…

【桌面虛擬化】之三 Persistent vs NonP

作者&#xff1a;范軍 &#xff08;Frank Fan&#xff09; 新浪微博&#xff1a;frankfan7 在【桌面虛擬化】之二類型及案例中我們探討了桌面虛擬化的兩種架構&#xff0c;HostedVirtual Desktop (VDI) 和 Published Desktop/App. 本文深入分析其中VDI的兩種桌面類型&#xff0…

H5 video 開發問題及相關知識點

相關鏈接&#xff1a;H5 video 的使用H5 video 全屏播放? video點播與直播H5 video目前所有瀏覽器都支持的視頻格式是MP4格式&#xff0c;所以mp4應當是點播web視頻的首選格式。而在直播視頻上&#xff0c;H5 video只在移動端原生支持HLS流的直播視頻(Mac safari video標簽也支…

Mybatis-Generator自動生成XML文件以及接口和實體類

整合了MySQL和Oracle配置文件生成方法 這個是整個文件夾的下載地址&#xff1a;http://www.codepeople.cn/download 主要給大家介紹一下generatorConfig.xml文件的配置&#xff0c;以及生成后的文件。 generatorConfig.xml <?xml version"1.0" encoding"UTF…

如何在Windows 10上設置默認Linux發行版

Windows 10 now allows you to install multiple Linux environments, starting with the Fall Creators Update. If you have multiple Linux environments, you can set your default and switch between them. Windows 10現在允許您從Fall Creators Update開始安裝多個Linux…

mysql全備份+增量備份筆記總結

備份基礎知識 冷備&#xff08;cold backup&#xff09;&#xff1a;需要關mysql服務&#xff0c;讀寫請求均不允許狀態下進行&#xff1b; 溫備&#xff08;warm backup&#xff09;&#xff1a; 服務在線&#xff0c;但僅支持讀請求&#xff0c;不允許寫請求&#xff1b; 熱備…

pjax學習

PJAX 介紹 紅薯 發布于 2012/04/11 22:06閱讀 61K收藏 116評論 11jQuery.Pjax kissy開發四年只會寫業務代碼&#xff0c;分布式高并發都不會還做程序員&#xff1f;->>> 介紹 pushState是一個可以操作history的api&#xff0c;該api的介紹和使用請見這里&#xff1a…

SQL Server 2000詳細安裝過程及配置

說明&#xff1a;這篇文章是幾年前我發布在網易博客當中的原創文章&#xff0c;但由于網易博客現在要停止運營了&#xff0c;所以我就把這篇文章搬了過來&#xff0c;雖然現如今SQL Server 2000軟件早已經過時了&#xff0c;但仍然有一部分人在使用它&#xff0c;尤其是某些高校…

移動應用ios和網頁應用_如何在iOS上一次移動多個應用

移動應用ios和網頁應用Apple doesn’t really believe in detailed instruction manuals, so some handy tricks slip through the cracks. One such trick we’ve recently discovered is that you can move multiple app icons at once on iOS. Here’s how. Apple并不真正相…

如何將內核靜態庫編譯連接到驅動程序中去【轉】

轉自&#xff1a;http://blog.csdn.net/ganjianfeng2003/article/details/8089551 如何將內核靜態庫編譯連接到驅動程序中去 2010-12-07 08:27 331人閱讀 評論(1) 收藏 舉報 http://blog.chinaunix.net/u2/61663/showart_2404744.html 剛上郵箱的時候發現一位網友向我詢問這個問…

2018-2019 20165226 Exp9 Web安全基礎

2018-2019 20165226 Exp9 Web安全基礎 目錄 一、實驗內容說明及基礎問題回答 二、實驗過程 Webgoat準備XSS攻擊 ① Phishing with XSS 跨站腳本釣魚攻擊② Stored XSS Attacks 存儲型XSS攻擊③ Reflected XSS Attacks 反射型XSS攻擊 CSRF攻擊 ① Cross Site Request Forgery(CS…

用 git 同步 Colab 與 Gitlab、Github 之間的文件

Colab 是谷歌提供的免費 Jupyter 服務&#xff0c;可使用 GPU。但由于每次的 VM &#xff08;虛擬機&#xff09;登出后所有文件都會連同&#xff36;&#xff2d;被毀掉。如何將一個項目里的程序或數據同步到 Colab則往往比較麻煩。盡管谷歌盤也可以掛到 Colab 里用&#xff0…

keep-alive使用_如何使用Google Keep進行無憂筆記

keep-alive使用There are a lot of note-taking apps out there. Google Keep may not be as powerful as services like Evernote, but its value is in its simplicity. Let’s talk about how to make the most of it. 那里有很多筆記應用程序。 Google Keep可能不如Evernot…

ZedGraph在項目中的應用

ZedGraph在項目中的應用將數據庫數據提取出來&#xff0c;顯示成曲線圖&#xff08;餅狀、柱狀或立體圖&#xff09;是項目中最常見的需求。 網上搜索到的解決方法&#xff0c;大多歸為兩類&#xff0c;一種是利用ActiveX組件&#xff0c;另一種是使用.net框架自帶的畫圖的類。…