Tagged Pointer

算是知道為什么nsstring不能用== 判斷了. 因?yàn)?==判斷的是指針

前言

在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)存占用贯城,以及3倍的訪問(wèn)速度提升,100倍的創(chuàng)建鲫骗、銷毀速度提升悲雳。本文從Tagged Pointer試圖解決的問(wèn)題入手,帶領(lǐng)讀者理解Tagged Pointer的實(shí)現(xiàn)細(xì)節(jié)和優(yōu)勢(shì)合瓢,最后指出了使用時(shí)的注意事項(xiàng)。

問(wèn)題

我們先看看原有的對(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é)的霍狰。而指針類型的大小通常也是與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一類的對(duì)象所占用的內(nèi)存會(huì)翻倍郎嫁。如下圖所示:

image.png

我們?cè)賮?lái)看看效率上的問(wèn)題,為了存儲(chǔ)和訪問(wèn)一個(gè)NSNumber對(duì)象尚辑,我們需要在堆上為其分配內(nèi)存盔腔,另外還要維護(hù)它的引用計(jì)數(shù)月褥,管理它的生命期瓢喉。這些都給程序增加了額外的邏輯,造成運(yùn)行效率上的損失栓票。

Tagged Pointer

為了改進(jìn)上面提到的內(nèi)存占用和效率問(wèn)題,蘋(píng)果提出了Tagged Pointer對(duì)象佛猛。由于NSNumber坠狡、NSDate一類的變量本身的值需要占用的內(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ù)情況都是可以處理的触幼。

所以我們可以將一個(gè)對(duì)象的指針拆成兩部分究飞,一部分直接保存數(shù)據(jù),另一部分作為特殊標(biāo)記亿傅,表示這是一個(gè)特別的指針葵擎,不指向任何一個(gè)地址。所以酬滤,引入了Tagged Pointer對(duì)象之后,64位CPU下NSNumber的內(nèi)存圖變成了以下這樣:

image.png

對(duì)此氯檐,我們也可以用 Xcode做實(shí)驗(yàn)來(lái)驗(yàn)證体捏。我們的實(shí)驗(yàn)代碼如下:

{
    @autoreleasepool {
        NSNumber *number1 = @1;
        NSNumber *number2 = @2;
        NSNumber *number3 = @3;
        NSNumber *numberFFFF = @(0xFFFF);

        NSLog(@"number1 pointer is %p", number1);
        NSLog(@"number2 pointer is %p", number2);
        NSLog(@"number3 pointer is %p", number3);
        NSLog(@"numberffff pointer is %p", numberFFFF);
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

在該代碼中,我們將幾個(gè)Number類型的指針的值直接輸出耗拓。需要注意的是,我們需要將模擬器切換成 64位的CPU來(lái)測(cè)試樟插,如下圖所示:

[圖片上傳中...(image-af9129-1518072128646-2)]

運(yùn)行之后竿刁,我們得到的結(jié)果如下,可以看到食拜,除去最后的數(shù)字最末尾的2以及最開(kāi)頭的0xb,其它數(shù)字剛好表示了相應(yīng)NSNumber的值流强。

number2 pointer is 0xb000000000000022
number3 pointer is 0xb000000000000032
numberFFFF pointer is 0xb0000000000ffff2

可見(jiàn)呻待,蘋(píng)果確實(shí)是將值直接存儲(chǔ)到了指針本身里面。我們還可以猜測(cè)奏篙,數(shù)字最末尾的2以及最開(kāi)頭的0xb是否就是蘋(píng)果對(duì)于Tagged Pointer的特殊標(biāo)記呢迫淹?我們嘗試放一個(gè)8字節(jié)的長(zhǎng)的整數(shù)到NSNumber實(shí)例中,對(duì)于這樣的實(shí)例敛熬,由于Tagged Pointer無(wú)法將其按上面的壓縮方式來(lái)保存应民,那么應(yīng)該就會(huì)以普通對(duì)象的方式來(lái)保存,我們的實(shí)驗(yàn)代碼如下:

NSLog(@"bigNumber pointer is %p", bigNumber);

運(yùn)行之后瑞妇,結(jié)果如下,驗(yàn)證了我們的猜測(cè)改备,bigNumber的地址更像是一個(gè)普通的指針地址蔓倍,和它本身的值看不出任何關(guān)系:

可見(jiàn)盐捷,當(dāng)8字節(jié)可以承載用于表示的數(shù)值時(shí)默勾,系統(tǒng)就會(huì)以Tagged Pointer的方式生成指針,如果8字節(jié)承載不了時(shí)滞诺,則又用以前的方式來(lái)生成普通的指針环疼。關(guān)于以上關(guān)于Tag Pointer的存儲(chǔ)細(xì)節(jié),我們也可以在這里找到相應(yīng)的討論炫隶,但是其中關(guān)于Tagged Pointer的實(shí)現(xiàn)細(xì)節(jié)與我們的實(shí)驗(yàn)并不相符,筆者認(rèn)為可能是蘋(píng)果更改了具體的實(shí)現(xiàn)細(xì)節(jié)煞檩,并且這并不影響Tagged Pointer我們討論Tagged Pointer本身的優(yōu)點(diǎn)栅贴。

特點(diǎn)

我們也可以在WWDC2013的《Session 404 Advanced in Objective-C》視頻中,看到蘋(píng)果對(duì)于Tagged Pointer特點(diǎn)的介紹:

  1. Tagged Pointer專門(mén)用來(lái)存儲(chǔ)小的對(duì)象,例如NSNumberNSDate
  2. Tagged Pointer指針的值不再是地址了厨剪,而是真正的值友存。所以,實(shí)際上它不再是一個(gè)對(duì)象了屡立,它只是一個(gè)披著對(duì)象皮的普通變量而已。所以勇皇,它的內(nèi)存并不存儲(chǔ)在堆中焚刺,也不需要malloc和free。
  3. 在內(nèi)存讀取上有著3倍的效率乳愉,創(chuàng)建時(shí)比以前快106倍。

由此可見(jiàn)捕虽,蘋(píng)果引入Tagged Pointer,不但減少了64位機(jī)器下程序的內(nèi)存占用房揭,還提高了運(yùn)行效率挖滤。完美地解決了小內(nèi)存對(duì)象在存儲(chǔ)和訪問(wèn)效率上的問(wèn)題。

isa指針

Tagged Pointer的引入也帶來(lái)了問(wèn)題斩松,即Tagged Pointer因?yàn)椴⒉皇钦嬲膶?duì)象惧盹,而是一個(gè)偽對(duì)象,所以你如果完全把它當(dāng)成對(duì)象來(lái)使钧椰,可能會(huì)讓它露馬腳。比如我在《Objective-C對(duì)象模型及應(yīng)用》一文中就寫(xiě)道瓶埋,所有對(duì)象都有 isa 指針诊沪,而Tagged Pointer其實(shí)是沒(méi)有的,因?yàn)樗皇钦嬲膶?duì)象晕粪。 因?yàn)椴皇钦嬲膶?duì)象渐裸,所以如果你直接訪問(wèn)Tagged Pointerisa成員的話,在編譯時(shí)將會(huì)有如下警告:

[圖片上傳中...(image-2d8af9-1518072128645-1)]

對(duì)于上面的寫(xiě)法昏鹃,應(yīng)該換成相應(yīng)的方法調(diào)用,如 isKindOfClassobject_getClass怠褐。只要避免在代碼中直接訪問(wèn)對(duì)象的isa變量您宪,即可避免這個(gè)問(wèn)題奠涌。

總結(jié)

蘋(píng)果將Tagged Pointer引入磷杏,給64位系統(tǒng)帶來(lái)了內(nèi)存的節(jié)省和運(yùn)行效率的提高极祸。Tagged Pointer通過(guò)在其最后一個(gè)bit位設(shè)置一個(gè)特殊標(biāo)記,用于將數(shù)據(jù)直接保存在指針本身中遥金。因?yàn)?code>Tagged Pointer并不是真正的對(duì)象,我們?cè)谑褂脮r(shí)需要注意不要直接訪問(wèn)其isa變量选泻。

http://www.infoq.com/cn/articles/deep-understanding-of-tagged-pointer/

最后編輯于
?著作權(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
  • 文/不壞的土叔 我叫張陵府寒,是天一觀的道長(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