深入理解Tagged Pointer

前言

在 2013 年 9 月,蘋果推出了 iPhone5s,與此同時,iPhone5s 配備了首個采用 64 位架構的 A7 雙核處理器编矾,為了節(jié)省內存和提高執(zhí)行效率,蘋果提出了Tagged Pointer的概念馁害。對于 64 位程序窄俏,引入 Tagged Pointer 后,相關邏輯能減少一半的內存占用碘菜,以及 3 倍的訪問速度提升凹蜈,100 倍的創(chuàng)建、銷毀速度提升忍啸。本文從Tagged Pointer試圖解決的問題入手仰坦,帶領讀者理解Tagged Pointer的實現細節(jié)和優(yōu)勢,最后指出了使用時的注意事項吊骤。

問題

我們先看看原有的對象為什么會浪費內存。假設我們要存儲一個 NSNumber 對象静尼,其值是一個整數白粉。正常情況下,如果這個整數只是一個 NSInteger 的普通變量鼠渺,那么它所占用的內存是與 CPU 的位數有關鸭巴,在 32 位 CPU 下占 4 個字節(jié),在 64 位 CPU 下是占 8 個字節(jié)的拦盹。而指針類型的大小通常也是與 CPU 位數相關鹃祖,一個指針所占用的內存在 32 位 CPU 下為 4 個字節(jié),在 64 位 CPU 下也是 8 個字節(jié)普舆。

所以一個普通的 iOS 程序恬口,如果沒有Tagged Pointer對象校读,從 32 位機器遷移到 64 位機器中后,雖然邏輯沒有任何變化祖能,但這種 NSNumber歉秫、NSDate 一類的對象所占用的內存會翻倍。如下圖所示:

image

我們再來看看效率上的問題养铸,為了存儲和訪問一個 NSNumber 對象雁芙,我們需要在堆上為其分配內存,另外還要維護它的引用計數钞螟,管理它的生命期兔甘。這些都給程序增加了額外的邏輯,造成運行效率上的損失鳞滨。

Tagged Pointer

為了改進上面提到的內存占用和效率問題洞焙,蘋果提出了Tagged Pointer對象。由于 NSNumber太援、NSDate 一類的變量本身的值需要占用的內存大小常常不需要 8 個字節(jié)闽晦,拿整數來說,4 個字節(jié)所能表示的有符號整數就可以達到 20 多億(注:2^31=2147483648提岔,另外 1 位作為符號位)仙蛉,對于絕大多數情況都是可以處理的。

所以我們可以將一個對象的指針拆成兩部分碱蒙,一部分直接保存數據荠瘪,另一部分作為特殊標記,表示這是一個特別的指針赛惩,不指向任何一個地址哀墓。所以,引入了Tagged Pointer對象之后喷兼,64 位 CPU 下 NSNumber 的內存圖變成了以下這樣:

image

對此篮绰,我們也可以用 Xcode 做實驗來驗證。我們的實驗代碼如下:


int main(int argc, char * argv[])

{

    @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]));

    }

}

在該代碼中季惯,我們將幾個 Number 類型的指針的值直接輸出吠各。需要注意的是,我們需要將模擬器切換成 64 位的 CPU 來測試勉抓,如下圖所示:

image

運行之后贾漏,我們得到的結果如下,可以看到藕筋,除去最后的數字最末尾的 2 以及最開頭的 0xb纵散,其它數字剛好表示了相應 NSNumber 的值。


number1 pointer is 0xb000000000000012

number2 pointer is 0xb000000000000022

number3 pointer is 0xb000000000000032

numberFFFF pointer is 0xb0000000000ffff2

可見,蘋果確實是將值直接存儲到了指針本身里面伍掀。我們還可以猜測掰茶,數字最末尾的 2 以及最開頭的 0xb 是否就是蘋果對于Tagged Pointer的特殊標記呢?我們嘗試放一個 8 字節(jié)的長的整數到NSNumber實例中硕盹,對于這樣的實例符匾,由于Tagged Pointer無法將其按上面的壓縮方式來保存,那么應該就會以普通對象的方式來保存瘩例,我們的實驗代碼如下:


NSNumber *bigNumber = @(0xEFFFFFFFFFFFFFFF);

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

運行之后啊胶,結果如下,驗證了我們的猜測垛贤,bigNumber的地址更像是一個普通的指針地址焰坪,和它本身的值看不出任何關系:


bigNumber pointer is 0x10921ecc0

可見,當 8 字節(jié)可以承載用于表示的數值時聘惦,系統就會以Tagged Pointer的方式生成指針某饰,如果 8 字節(jié)承載不了時,則又用以前的方式來生成普通的指針善绎。關于以上關于Tag Pointer的存儲細節(jié)黔漂,我們也可以在 這里 找到相應的討論,但是其中關于Tagged Pointer的實現細節(jié)與我們的實驗并不相符禀酱,筆者認為可能是蘋果更改了具體的實現細節(jié)炬守,并且這并不影響Tagged Pointer我們討論Tagged Pointer本身的優(yōu)點。

特點

我們也可以在 WWDC2013 的《Session 404 Advanced in Objective-C》視頻中剂跟,看到蘋果對于Tagged Pointer特點的介紹:

  1. Tagged Pointer專門用來存儲小的對象减途,例如NSNumberNSDate
  2. Tagged Pointer指針的值不再是地址了,而是真正的值曹洽。所以鳍置,實際上它不再是一個對象了,它只是一個披著對象皮的普通變量而已送淆。所以税产,它的內存并不存儲在堆中,也不需要 malloc 和 free偷崩。
  3. 在內存讀取上有著 3 倍的效率辟拷,創(chuàng)建時比以前快 106 倍。

由此可見环凿,蘋果引入Tagged Pointer梧兼,不但減少了 64 位機器下程序的內存占用放吩,還提高了運行效率智听。完美地解決了小內存對象在存儲和訪問效率上的問題。

isa 指針

Tagged Pointer的引入也帶來了問題,即Tagged Pointer因為并不是真正的對象到推,而是一個偽對象考赛,所以你如果完全把它當成對象來使,可能會讓它露馬腳莉测。比如我在 《Objective-C 對象模型及應用》 一文中就寫道颜骤,所有對象都有 isa 指針,而Tagged Pointer其實是沒有的捣卤,因為它不是真正的對象忍抽。
因為不是真正的對象,所以如果你直接訪問Tagged Pointerisa成員的話董朝,在編譯時將會有如下警告:

image

對于上面的寫法鸠项,應該換成相應的方法調用,如 isKindOfClassobject_getClass子姜。只要避免在代碼中直接訪問對象的 isa 變量祟绊,即可避免這個問題。

總結

蘋果將Tagged Pointer引入哥捕,給 64 位系統帶來了內存的節(jié)省和運行效率的提高牧抽。Tagged Pointer通過在其最后一個 bit 位設置一個特殊標記,用于將數據直接保存在指針本身中遥赚。因為Tagged Pointer并不是真正的對象扬舒,我們在使用時需要注意不要直接訪問其 isa 變量。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末鸽捻,一起剝皮案震驚了整個濱河市呼巴,隨后出現的幾起案子,更是在濱河造成了極大的恐慌御蒲,老刑警劉巖衣赶,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異厚满,居然都是意外死亡府瞄,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門碘箍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遵馆,“玉大人,你說我怎么就攤上這事丰榴』醯耍” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵四濒,是天一觀的道長换况。 經常有香客問我职辨,道長,這世上最難降的妖魔是什么戈二? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任舒裤,我火速辦了婚禮,結果婚禮上觉吭,老公的妹妹穿的比我還像新娘腾供。我一直安慰自己,他們只是感情好鲜滩,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布伴鳖。 她就那樣靜靜地躺著,像睡著了一般徙硅。 火紅的嫁衣襯著肌膚如雪黎侈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天闷游,我揣著相機與錄音峻汉,去河邊找鬼。 笑死脐往,一個胖子當著我的面吹牛休吠,可吹牛的內容都是我干的。 我是一名探鬼主播业簿,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼瘤礁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了梅尤?” 一聲冷哼從身側響起柜思,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎巷燥,沒想到半個月后赡盘,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡缰揪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年陨享,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钝腺。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡抛姑,死狀恐怖,靈堂內的尸體忽然破棺而出艳狐,到底是詐尸還是另有隱情定硝,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布毫目,位于F島的核電站蔬啡,受9級特大地震影響唁毒,放射性物質發(fā)生泄漏。R本人自食惡果不足惜星爪,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望粉私。 院中可真熱鬧顽腾,春花似錦、人聲如沸诺核。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窖杀。三九已至漓摩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間入客,已是汗流浹背管毙。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留桌硫,地道東北人夭咬。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像铆隘,于是被迫代替她去往敵國和親卓舵。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內容

  • 在調試程序或者反編譯App時,經嘲蚰疲可以看到"NSTaggedPointerString"這個東西例如: 打印: 這...
    Mr_Baymax閱讀 10,383評論 15 48
  • 前言 在2013年9月掏湾,蘋果推出了iPhone5s,與此同時肿嘲,iPhone5s配備了首個采用64位架構的A7雙核處...
    woshishui1243閱讀 170評論 0 0
  • 轉至元數據結尾創(chuàng)建: 董瀟偉融击,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,679評論 0 9
  • 本文基于objc4-709源碼進行分析。關于源碼編譯:objc - 編譯Runtime源碼objc4-706 ob...
    WeiHing閱讀 809評論 1 3
  • 文/豬豬貓張 上帝造人的時候讓我們擁有兩只耳朵雳窟、兩只眼睛和一張嘴砚嘴,就是讓我們多聽、多看涩拙、少說际长。我們對還不會說話的嬰...
    向上有陽光閱讀 492評論 1 10