內(nèi)存優(yōu)化之Tagged Pointer

什么是Tagged Pointer驻售?

在2013年9月嗦明,蘋(píng)果推出了iPhone5s撩荣,與此同時(shí)椭更,iPhone5s配備了首個(gè)采用64位架構(gòu)的A7雙核處理器哪审,為了節(jié)省內(nèi)存和提高執(zhí)行效率,蘋(píng)果提出了Tagged Pointer的概念虑瀑。對(duì)于64位程序湿滓,引入Tagged Pointer后,相關(guān)邏輯能減少一半的內(nèi)存占用舌狗!

那么Tagged Pointer是如何節(jié)省內(nèi)存的呢叽奥?

  • 我們先看看原有的對(duì)象為什么會(huì)浪費(fèi)內(nèi)存。假設(shè)我們要存儲(chǔ)一個(gè) NSNumber 對(duì)象把夸,其值是一個(gè)整數(shù)而线。正常情況下,如果這個(gè)整數(shù)只是一個(gè) NSInteger 的普通變量恋日,那么它所占用的內(nèi)存是與 CPU 的位數(shù)有關(guān)膀篮,在 32 位 CPU 下占 4 個(gè)字節(jié),在 64 位 CPU 下是占 8 個(gè)字節(jié)的岂膳。而指針類(lèi)型的大小通常也是與 CPU 位數(shù)相關(guān)誓竿,一個(gè)指針?biāo)加玫膬?nèi)存在 32 位 CPU 下為 4 個(gè)字節(jié),在 64 位 CPU 下也是 8 個(gè)字節(jié)谈截。
    所以一個(gè)普通的 iOS 程序筷屡,如果沒(méi)有Tagged Pointer對(duì)象涧偷,從 32 位機(jī)器遷移到 64 位機(jī)器中后,雖然邏輯沒(méi)有任何變化毙死,但這種 NSNumber燎潮、NSDate 一類(lèi)的對(duì)象所占用的內(nèi)存會(huì)翻倍。如下圖所示:


    未使用Tagged Pointer內(nèi)存圖.png

    從上圖中可以看到內(nèi)存的多余的浪費(fèi)扼倘,以及查找該值的繁瑣邏輯确封!我們?cè)賮?lái)看看效率上的問(wèn)題,為了存儲(chǔ)和訪(fǎng)問(wèn)一個(gè) NSNumber 對(duì)象再菊,我們需要在堆上為其分配內(nèi)存爪喘,另外還要維護(hù)它的引用計(jì)數(shù),管理它的生命期纠拔。這些都給程序增加了額外的邏輯秉剑,造成運(yùn)行效率以及內(nèi)存的損失。

  • 為了改進(jìn)上面提到的內(nèi)存占用和效率問(wèn)題稠诲,所以蘋(píng)果提出了Tagged Pointer對(duì)象侦鹏。對(duì)于某些占用內(nèi)存很小的數(shù)據(jù)實(shí)例,不再單獨(dú)開(kāi)辟空間去存儲(chǔ)吕粹,而是將實(shí)際的實(shí)例值存儲(chǔ)在對(duì)象的指針中种柑,同時(shí)對(duì)該指針進(jìn)行標(biāo)記,用于區(qū)分正常的指針指向匹耕!由于NSNumber聚请、NSDate類(lèi)的變量本身的值需要占用的內(nèi)存大小常常不需要8個(gè)字節(jié),拿整數(shù)來(lái)說(shuō)稳其,4個(gè)字節(jié)所能表示的有符號(hào)整數(shù)就可以達(dá)到20多億(注:2^31=2147483648驶赏,另外1位作為符號(hào)位),對(duì)于絕大多數(shù)情況都是可以處理的既鞠。所以蘋(píng)果將一個(gè)對(duì)象的指針拆成兩部分煤傍,一部分直接保存數(shù)據(jù),另一部分作為特殊標(biāo)記嘱蛋,表示這是一個(gè)特別的指針蚯姆,不指向任何一個(gè)地址。所以洒敏,引入了Tagged Pointer對(duì)象之后龄恋,64位CPU下NSNumber的內(nèi)存圖變成了以下這樣:


    64位使用Tagged Pointer內(nèi)存圖.png
下面進(jìn)入大家最喜歡的環(huán)節(jié),沒(méi)錯(cuò)凶伙,就是驗(yàn)證郭毕!

1.我先來(lái)看一下Tagged Pointer的初始化過(guò)程


createTaggedPointer@2x.png

2.在上述的代碼中我們可以看到對(duì)真實(shí)的結(jié)果進(jìn)行了一次encode操作,接下來(lái)我們?cè)賮?lái)看看_objc_encodeTaggedPointer函數(shù)里做了什么操作

encodeTaggedPointer@2x.png

將實(shí)際結(jié)果與objc_debug_taggedpointer_obfuscator進(jìn)行異或操作函荣!

  1. 那么objc_debug_taggedpointer_obfuscator是什么呢显押?我們?cè)賮?lái)看看


    objc_debug_taggedpointer_obfuscator@2x.png

    可以看到在iOS 12系統(tǒng)版本之前objc_debug_taggedpointer_obfuscator是0扳肛,之后是一個(gè)無(wú)符號(hào)長(zhǎng)整型的隨機(jī)數(shù)!那么為什么要改為隨機(jī)數(shù)呢乘碑?是為了一個(gè)簡(jiǎn)便的加密挖息!大家可以試想一下,用一個(gè)值去^一個(gè)0蝉仇,那么結(jié)果是多少旋讹,可以自己嘗試一下V巢稀(可以往密碼加鹽這方面理解)

  2. 我們所看見(jiàn)的的地址轿衔,實(shí)際上是被編碼過(guò)的,如果想要看到真實(shí)的結(jié)果則需要自己手動(dòng)去解碼睦疫!


    objc_decodeTaggedPointer@2x.png

    通過(guò)上述代碼可以得知害驹,就是把已編碼后的地址再次進(jìn)行異或得到原來(lái)的值!

我們來(lái)一起看下這特殊地址的廬山真面目蛤育,是不是還有點(diǎn)小激動(dòng) ??

聲明

log

通過(guò)log我們可以看出宛官,0xa以及0xb實(shí)際為特殊標(biāo)識(shí)位,以0xb開(kāi)頭為例瓦糕,將其轉(zhuǎn)換為二進(jìn)制就是1011底洗, 首位1表示這是一個(gè)tagged Pointer,而011轉(zhuǎn)換為十進(jìn)制是3咕娄,參考下圖中tagged Pointer的類(lèi)型枚舉亥揖,可以看出這是一個(gè)NSNumber類(lèi)型!


tagged Pointer類(lèi)型枚舉

而末尾的數(shù)字仿佛在代表著類(lèi)型標(biāo)識(shí)符圣勒!經(jīng)過(guò)實(shí)踐可以總結(jié)為:0表示char類(lèi)型费变,1表示short類(lèi)型,2表示整形圣贸,3表示長(zhǎng)整型挚歧,4表示單精度類(lèi)型,5表示雙精度類(lèi)型吁峻!而中間這一部分才是真正的值滑负,可以看到A為char類(lèi)型,返回的是ASCII碼(該值為16進(jìn)制的用含,需要進(jìn)行十進(jìn)制轉(zhuǎn)換)
需要注意的是:TaggedPointerString類(lèi)型的指針與基本類(lèi)型的指針是不一樣的矮慕,末尾的數(shù)字為字符串的長(zhǎng)度!


ASCII對(duì)照表.png

聲明: 以上只是一些初探耕餐,系統(tǒng)是如何區(qū)分以及劃分的需要大家繼續(xù)去探索凡傅,例如:Tagged Pointer最大可以存儲(chǔ)多大值

總結(jié)

1.TaggedPointer:并不是一個(gè)類(lèi),它是適用于 64位處理器的一個(gè)內(nèi)存優(yōu)化機(jī)制肠缔,專(zhuān)門(mén)用來(lái)存儲(chǔ)小對(duì)象夏跷,當(dāng)存儲(chǔ)不下時(shí)哼转,則轉(zhuǎn)為對(duì)象!例如 NSString槽华、NSNumber 和 NSDate等對(duì)象進(jìn)行優(yōu)化壹蔓。
2.指針不再是地址了,而是經(jīng)過(guò)標(biāo)識(shí)過(guò)的的值猫态。它不再是一個(gè)對(duì)象了佣蓉,只是普通變量而已。所以亲雪,它的內(nèi)存并不存儲(chǔ)在堆中勇凭,也不需要malloc和free。
3.在內(nèi)存讀取上有著3倍的效率义辕,創(chuàng)建時(shí)比以前快106倍虾标。

注意:Tagged Pointer并不是真正的對(duì)象,而是一個(gè)偽對(duì)象灌砖,對(duì)象都有 isa指針璧函,而Tagged Pointer是沒(méi)有的,因?yàn)樗皇钦嬲膶?duì)象基显,不能直接訪(fǎng)問(wèn)Tagged Pointer的isa蘸吓!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市撩幽,隨后出現(xiàn)的幾起案子库继,更是在濱河造成了極大的恐慌,老刑警劉巖摸航,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件制跟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡酱虎,警方通過(guò)查閱死者的電腦和手機(jī)雨膨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)读串,“玉大人聊记,你說(shuō)我怎么就攤上這事』峙” “怎么了排监?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)杰捂。 經(jīng)常有香客問(wèn)我舆床,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任挨队,我火速辦了婚禮谷暮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘盛垦。我一直安慰自己湿弦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布腾夯。 她就那樣靜靜地躺著颊埃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蝶俱。 梳的紋絲不亂的頭發(fā)上班利,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音跷乐,去河邊找鬼肥败。 笑死,一個(gè)胖子當(dāng)著我的面吹牛愕提,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播皿哨,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼浅侨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了证膨?” 一聲冷哼從身側(cè)響起如输,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎央勒,沒(méi)想到半個(gè)月后不见,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡崔步,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年稳吮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片井濒。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡灶似,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瑞你,到底是詐尸還是另有隱情酪惭,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布者甲,位于F島的核電站春感,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鲫懒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一纺铭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧刀疙,春花似錦舶赔、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至疚鲤,卻和暖如春锥累,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背集歇。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工桶略, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诲宇。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓际歼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親姑蓝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鹅心,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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

  • 本文基于objc4-709源碼進(jìn)行分析。關(guān)于源碼編譯:objc - 編譯Runtime源碼objc4-706 ob...
    WeiHing閱讀 824評(píng)論 1 3
  • 原文鏈接OC內(nèi)存管理--引用計(jì)數(shù)器 更新于2020-02-14 更新Tagged Pointer的知識(shí)點(diǎn) 引用計(jì)數(shù)...
    NeroXie閱讀 2,313評(píng)論 0 6
  • 當(dāng)一個(gè)對(duì)象的實(shí)例方法被調(diào)用的時(shí)候纺荧,會(huì)通過(guò)isa找到對(duì)應(yīng)的類(lèi)旭愧,然后在該類(lèi)的class_data_bits_t中查找方...
    雪丹妮_66865閱讀 1,214評(píng)論 0 3
  • 通過(guò)這張圖描述了實(shí)例對(duì)象,類(lèi)宙暇,元類(lèi)之間的關(guān)系输枯;圖中實(shí)線(xiàn)是 super_class 指針,虛線(xiàn)是 isa 指針占贫。從圖...
    s_在路上閱讀 1,295評(píng)論 0 13
  • 此前是空曠時(shí)才溝通失控的部分現(xiàn)在碰到失控就變得空曠像盛大的嘆息用空氣的手捧起落地的鐘聲用淚目打撈心愛(ài)之物幾乎進(jìn)化成...
    驚蟄夕閱讀 198評(píng)論 2 2