Android智能指針Summary

引言:由于未來(lái)需要深入android底層進(jìn)行系統(tǒng)級(jí)別的開發(fā)鸥昏,所以最近在看老羅的《Android系統(tǒng)源代碼情景分析》,里面提到了一個(gè)很重要的概念叫智能指針母截,這個(gè)東西對(duì)于理解android應(yīng)用系統(tǒng)框架很有幫助腕唧,在FrameWork層大量的C++代碼中會(huì)經(jīng)常使用到這個(gè)概念翎朱。


那么什么是智能指針呢?(這里提下忌堂,這個(gè)指針不是指*盒至,指的是一個(gè)對(duì)象,但是它引用了一個(gè)實(shí)際使用的對(duì)象)

書的原話是:智能指針式一種能夠自動(dòng)維護(hù)對(duì)象引用計(jì)數(shù)的技術(shù)士修。

具體一點(diǎn)解釋枷遂,大家都知道C++需要使用大量的指針,指針最容易出現(xiàn)錯(cuò)誤的地方就是忘記釋放其指向的對(duì)象所占的內(nèi)存導(dǎo)致內(nèi)存泄漏李命。那么為了避免這種情況登淘,Android系統(tǒng)提供了C++智能指針通過(guò)引用計(jì)數(shù)技術(shù)來(lái)維護(hù)對(duì)象的生命周期。(下面稍微解釋一下引用計(jì)數(shù)技術(shù)封字,我相信很多人都了解過(guò))

引用計(jì)數(shù)法:這種算法的思路是如果某一個(gè)對(duì)象被別的對(duì)象黔州,那么就把他們引用計(jì)數(shù)器加上1耍鬓,這樣當(dāng)進(jìn)行垃圾回收時(shí)如果判斷該引用的數(shù)量為0,此時(shí)就代表沒有進(jìn)行任何對(duì)象對(duì)其進(jìn)行引用流妻,此時(shí)就進(jìn)行回收牲蜀。

問(wèn)題:但是這種技術(shù)會(huì)出現(xiàn)一個(gè)問(wèn)題就是兩個(gè)對(duì)象相互引用的時(shí)候會(huì)出現(xiàn)“死鎖”的情況。比如A引用B绅这,B引用A涣达。當(dāng)對(duì)象A不再使用需要釋放它所占的內(nèi)存時(shí),由于A仍然被B引用所以無(wú)法釋放证薇,只能等待B釋放這個(gè)引用度苔,同樣對(duì)B來(lái)說(shuō)一樣的問(wèn)題。所以會(huì)造成相互等待浑度,這個(gè)和Java中的鎖同步問(wèn)題一個(gè)道理寇窑,A對(duì)象wait()了自己等待B對(duì)象喚醒,B對(duì)象也wait()了自己等待A對(duì)象喚醒自己箩张。就如兩個(gè)睡美人都在等待對(duì)方叫醒自己一樣造成死鎖狀態(tài)甩骏。

在java中為了解決這個(gè)問(wèn)題引入了引用鏈方法,這里僅僅提一下這個(gè)概念--“JVM采用GC Roots可達(dá)性來(lái)決定是否會(huì)被GC回收"先慷,可以參考《深入JVM虛擬機(jī)》一書饮笛。

那么Android的智能指針是怎么解決這個(gè)問(wèn)題的呢?

這里先介紹一種較為復(fù)雜的引用計(jì)數(shù)方法论熙,這種方法將對(duì)象的引用計(jì)數(shù)分為強(qiáng)引用和若引用計(jì)數(shù)兩種福青,但是對(duì)象的生命周期只受強(qiáng)引用計(jì)數(shù)控制。這種解決方案以”父子“關(guān)系將對(duì)象很有意思的關(guān)聯(lián)了起來(lái)赴肚,即"父”對(duì)象通過(guò)強(qiáng)引用計(jì)數(shù)引用”子"對(duì)象素跺,“子”對(duì)象通過(guò)弱引用計(jì)數(shù)引用“父”對(duì)象,但是很明顯按照傳統(tǒng)美德只有父親管著兒子誉券,所以當(dāng)“子”對(duì)象想要釋放自己時(shí)由于它還收到“父”對(duì)象的管制無(wú)法釋放自己指厌;但是“父”對(duì)象想要釋放自己時(shí)可以輕易釋放自己,此時(shí)由于“父”不存在了踊跟,“子”對(duì)象不受強(qiáng)引用計(jì)數(shù)的管制了就可以釋放自己了踩验。

好的介紹完了這些背景可以公布答案了,答案就是:Android提供了三種類型的指針商玫,分別為輕量級(jí)指針(Light Pointer)箕憾、強(qiáng)指針(Strong Pointer)、弱指針(Weak Pointer)拳昌。

輕量級(jí)指針

這里不多提輕量級(jí)指針袭异,因?yàn)檫@種指針式通過(guò)簡(jiǎn)單引用計(jì)數(shù)技術(shù)來(lái)維護(hù)對(duì)象生命周期的。(個(gè)人覺得還是會(huì)有相互引用的風(fēng)險(xiǎn)產(chǎn)生炬藤,所以并沒有懂使用這個(gè)指針的意義在哪兒御铃?也許是相比強(qiáng)指針和弱指針其效率更高吧)碴里。關(guān)于它只需知道3點(diǎn):

第一點(diǎn)使用它需要繼承LightRefBase(模板類)

public LightClass: public LightRefBase<LightClass>

第二點(diǎn)LightRefBase類只有一個(gè)成員變量mCount用來(lái)描述一個(gè)對(duì)象的引用計(jì)數(shù)值。

第三點(diǎn)需要知道輕量級(jí)指針的實(shí)現(xiàn)類和強(qiáng)指針的實(shí)現(xiàn)類是同一個(gè)類sp上真。

強(qiáng)指針

與輕量級(jí)指針不同咬腋,強(qiáng)指針不是直接使用一個(gè)整數(shù)來(lái)維護(hù)對(duì)象的引用計(jì)數(shù)的,而是使用一個(gè)weakref_impl對(duì)象睡互,這個(gè)對(duì)象是繼承RefBase類(一個(gè)類要使用強(qiáng)指針和弱指針必須繼承RefBase)中的內(nèi)部類weakref_type類根竿,其中weakref_type僅僅只定義了引用計(jì)數(shù)維護(hù)接口,具體實(shí)現(xiàn)是weakref_type就珠。(具體關(guān)系如下圖寇壳,圖是手碼的,一個(gè)是繼承關(guān)系嗓违,一個(gè)是引用關(guān)系)

這里說(shuō)一下成員變量mFlags的作用九巡,mFlags這個(gè)標(biāo)志位有三種取值:

0:表示對(duì)象的生命周期只受強(qiáng)引用計(jì)數(shù)影響;默認(rèn)就是這個(gè)蹂季。

1(OBJECT_LIFETIME_WEAK):表示對(duì)象的生命周期同時(shí)受強(qiáng)引用計(jì)數(shù)和弱引用計(jì)數(shù)影響

OBJECT_LIFETIME_FOREVER:表示對(duì)象的生命周期完全不受強(qiáng)引用計(jì)數(shù)和弱引用計(jì)數(shù)的影響。//這個(gè)地方我想說(shuō)一下疏日,我實(shí)踐的時(shí)候發(fā)現(xiàn)并沒有這個(gè)標(biāo)志位偿洁,可能是后來(lái)的android版本在基類里面取消了這個(gè)標(biāo)識(shí)位,具體我還沒有仔細(xì)查最新的源碼沟优,會(huì)繼續(xù)補(bǔ)充涕滋。

*原書里面解釋強(qiáng)指針或弱指針均涉及源代碼分析,這里我嘗試用自己的語(yǔ)言總結(jié)一下重要的部分


RefBase的incStrong函數(shù)干了哪些事情呢挠阁?(主要有三步宾肺,第三步是第一次強(qiáng)引用的一些邏輯處理,這里不分析)

1.增加弱引用計(jì)數(shù)(這個(gè)看起來(lái)好像與函數(shù)的名字有點(diǎn)相互違背侵俗,這個(gè)后面會(huì)解釋)

具體過(guò)程:通過(guò)mRefs的incWeak方法來(lái)增加對(duì)象的弱引用計(jì)數(shù)(可以配合類圖理解)锨用,mRefs是Weakref_impI類型的,Weakref_impl又繼承了inWeak方法隘谣,實(shí)際上調(diào)用的是weakref_type的方法

2.增加強(qiáng)引用計(jì)數(shù)增拥。

通過(guò)android_atomic_inc函數(shù)增加強(qiáng)引用計(jì)數(shù)值(返回增加前的值,這里注意是之前)

可以看出強(qiáng)指針類增加對(duì)象的強(qiáng)引用計(jì)數(shù)的同時(shí)也會(huì)增加弱引用計(jì)數(shù)寻歧,即一個(gè)對(duì)象的弱引用計(jì)數(shù)一定是大于或者等于它的強(qiáng)引用計(jì)數(shù)的掌栅。(sp的構(gòu)造函數(shù)就干了這么些事情)

那么sp的析構(gòu)函數(shù)干了什么事情呢?(對(duì)應(yīng)函數(shù)decStrong)

1.減少對(duì)象的強(qiáng)引用計(jì)數(shù)码泛,當(dāng)強(qiáng)引用計(jì)數(shù)為0時(shí)(實(shí)際上不是0猾封,這里用0好解釋),即不再被強(qiáng)指針引用時(shí)噪珊。此時(shí)需要判斷標(biāo)識(shí)位mFlags(上面提過(guò))是否為1晌缘,如果不為1逾苫,就會(huì)釋放對(duì)象所占的內(nèi)存,同時(shí)也會(huì)導(dǎo)致RefBase類的析構(gòu)函數(shù)調(diào)用枚钓。

2.減少對(duì)象的弱引用計(jì)數(shù)铅搓,一旦發(fā)現(xiàn)弱引用計(jì)數(shù)為0時(shí),把引用計(jì)數(shù)對(duì)象mRefs(weakref_impl類型)也釋放掉(前面提過(guò)搀捷,建議回頭看看方便理解)星掰。前面說(shuō)過(guò),一個(gè)對(duì)象的弱引用計(jì)數(shù)一定大于或者等于強(qiáng)引用計(jì)數(shù)的嫩舟,當(dāng)強(qiáng)引用計(jì)數(shù)為0時(shí)氢烘,會(huì)釋放掉RefBase對(duì)象,但當(dāng)此時(shí)弱引用計(jì)數(shù)大于0時(shí)家厌,不能將mRefs也釋放掉播玖,因?yàn)檫€有其他的弱指針通過(guò)weakref_impl對(duì)象來(lái)引用實(shí)際的對(duì)象。

*如果還是不懂饭于,建議配合原書中的源代碼看蜀踏。

弱指針

弱指針同樣從RefBase類繼承下來(lái),因?yàn)镽efBase提供了弱引用計(jì)數(shù)器掰吕。弱指針類的實(shí)現(xiàn)類為wp果覆。弱指針使用的是類型為weakref_type*的成員變量m_refs維護(hù)對(duì)象的弱引用計(jì)數(shù)。

弱指針和強(qiáng)指針有一個(gè)很大的區(qū)別殖熟,就是弱指針不可以直接操作它所引用的對(duì)象局待,因?yàn)樗玫膶?duì)象可能是不受弱引用計(jì)數(shù)控制的,即它所引用的對(duì)象可能是一個(gè)無(wú)效的對(duì)象菱属。因此钳榨,如果需要操作一個(gè)弱指針?biāo)玫膶?duì)象,那么就需要將這個(gè)弱指針升級(jí)為強(qiáng)指針纽门,這是通過(guò)它的成員函數(shù)promote來(lái)實(shí)現(xiàn)的薛耻。如果升級(jí)成功,就說(shuō)明該弱指針?biāo)玫膶?duì)象還沒有被銷毀膜毁,可以正常使用昭卓。

下面著重介紹wp的promote函數(shù)。先來(lái)看兩段源代碼代碼(純手碼截圖瘟滨,下次用markdown編輯器寫候醒,這么寫太sb了)

RefBase.h

參數(shù)p指向?qū)ο蟮牡刂罚鴧?shù)refs指向該對(duì)象內(nèi)部的一個(gè)弱引用計(jì)數(shù)器對(duì)象杂瘸。只有在對(duì)象地址不為null的情況下倒淫,才會(huì)調(diào)用它內(nèi)部的弱引用計(jì)數(shù)器對(duì)象的成員函數(shù)attempIncStrong來(lái)試圖增加該對(duì)象的強(qiáng)引用計(jì)數(shù)。如果能夠成功增加對(duì)象的強(qiáng)引用計(jì)數(shù)败玉,那么就可以成功地把一個(gè)弱指針升級(jí)為一個(gè)強(qiáng)指針敌土。

attempIncStrong看著是不是很熟悉镜硕,可以從之前的圖中找到。

這個(gè)成員函數(shù)試圖增加目標(biāo)對(duì)象的強(qiáng)引用計(jì)數(shù)返干,但是有可能會(huì)增加失敗兴枯,因?yàn)槟繕?biāo)對(duì)象可能已經(jīng)被釋放了,或者該目標(biāo)對(duì)象不允許使用強(qiáng)指針引用它矩欠。

(attempIncStrong中有個(gè)有意思的邏輯)

之前提過(guò)增加對(duì)象強(qiáng)引用計(jì)數(shù)時(shí)财剖,同時(shí)也會(huì)增加該對(duì)象的弱引用計(jì)數(shù)。

分割線(邏輯來(lái)了)

1.先調(diào)用成員函數(shù)incWeak來(lái)增加對(duì)象的弱引用計(jì)數(shù) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2.如果后面增加對(duì)象的強(qiáng)引用計(jì)數(shù)失敗癌淮,則調(diào)用decWeak來(lái)減少對(duì)象的弱引用計(jì)數(shù)躺坟。

一個(gè)弱指針?biāo)玫膶?duì)象可能處于兩種狀態(tài)。(下面均摘自原文)

第一種:該對(duì)象同時(shí)也被其他強(qiáng)指針對(duì)象所引用乳蓄,此時(shí)可以安全地將這個(gè)弱指針升級(jí)為強(qiáng)指針咪橙。

第二種:該對(duì)象沒有被任何強(qiáng)指針引用。這里情況就比較復(fù)雜了虚倒。需要根據(jù)對(duì)象生命周期來(lái)判斷

1.如果對(duì)象生命周期只受強(qiáng)引用計(jì)數(shù)影響美侦,那么就可以成功將該弱指針升級(jí)為強(qiáng)指針。因?yàn)樗軓?qiáng)引用計(jì)數(shù)影響裹刮,而此時(shí)該對(duì)象又沒有被強(qiáng)指針引用過(guò)音榜,那么它必然不會(huì)被釋放。

2.如果只受弱引用計(jì)數(shù)影響捧弃,首先我們可以確定對(duì)象現(xiàn)在一定是存在的,因?yàn)楝F(xiàn)在有一個(gè)弱指針引用它擦囊。但是违霞,這種情況需要進(jìn)一步調(diào)用對(duì)象的成員函數(shù)onIncStrongAttempted來(lái)確認(rèn)對(duì)象是否允許強(qiáng)指針引用它。如果返回為true說(shuō)明允許則成功將該弱指針升級(jí)為強(qiáng)指針瞬场。如果返回為false买鸽,則說(shuō)明升級(jí)失敗。

大概就總結(jié)這么多贯被。下面是打賞時(shí)間眼五,碼字不易,給個(gè)贊也行彤灶。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末看幼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子幌陕,更是在濱河造成了極大的恐慌诵姜,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搏熄,死亡現(xiàn)場(chǎng)離奇詭異棚唆,居然都是意外死亡暇赤,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門宵凌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鞋囊,“玉大人,你說(shuō)我怎么就攤上這事瞎惫×锔” “怎么了?”我有些...
    開封第一講書人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵微饥,是天一觀的道長(zhǎng)逗扒。 經(jīng)常有香客問(wèn)我,道長(zhǎng)欠橘,這世上最難降的妖魔是什么矩肩? 我笑而不...
    開封第一講書人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮肃续,結(jié)果婚禮上黍檩,老公的妹妹穿的比我還像新娘。我一直安慰自己始锚,他們只是感情好刽酱,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瞧捌,像睡著了一般棵里。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上姐呐,一...
    開封第一講書人閱讀 52,785評(píng)論 1 314
  • 那天殿怜,我揣著相機(jī)與錄音,去河邊找鬼曙砂。 笑死头谜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鸠澈。 我是一名探鬼主播柱告,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼笑陈!你這毒婦竟也來(lái)了际度?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤新锈,失蹤者是張志新(化名)和其女友劉穎甲脏,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡块请,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年娜氏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片墩新。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贸弥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出海渊,到底是詐尸還是另有隱情绵疲,我是刑警寧澤,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布臣疑,位于F島的核電站盔憨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏讯沈。R本人自食惡果不足惜郁岩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缺狠。 院中可真熱鬧问慎,春花似錦、人聲如沸挤茄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)穷劈。三九已至笼恰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間歇终,已是汗流浹背挖腰。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留练湿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓审轮,卻偏偏與公主長(zhǎng)得像肥哎,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子疾渣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容