前言
在 2013 年 9 月,蘋果推出了 iPhone5s,與此同時,iPhone5s 配備了首個采用 64 位架構(gòu)的 A7雙核處理器,為了節(jié)省內(nèi)存和提高執(zhí)行效率,蘋果提出了Tagged Pointer的概念。對于 64 位程序,引入 Tagged Pointer 后,相關(guān)邏輯能減少一半的內(nèi)存占用,以及3 倍的訪問速度提升,100倍的創(chuàng)建、銷毀速度提升。
問題
我們先看看原有的對象為什么會浪費內(nèi)存?
假設(shè)我們要存儲一個 NSNumber對象,其值是一個整數(shù)。正常情況下,如果這個整數(shù)只是一個 NSInteger的普通變量,那么它所占用的內(nèi)存是與 CPU 的位數(shù)有關(guān),在 32 位 CPU 下占 4個字節(jié),在 64 位CPU 下是占 8 個字節(jié)的。而指針類型的大小通常也是與CPU位數(shù)相關(guān),一個指針?biāo)加玫膬?nèi)存在 32 位 CPU 下為4 個字節(jié),在 64 位 CPU 下也是8個字節(jié)。
所以一個普通的 iOS 程序,如果沒有Tagged Pointer對象,從 32 位機器遷移到 64 位機器中后,雖然邏輯沒有任何變化,但這種 NSNumber、NSDate 一類的對象所占用的內(nèi)存會翻倍。如下圖所示:

Tagged Pointer
為了改進(jìn)上面提到的內(nèi)存占用和效率問題,蘋果提出了Tagged Pointer對象。由于 NSNumber、NSDate 一類的變量本身的值需要占用的內(nèi)存大小常常不需要8個字節(jié),拿整數(shù)來說,4 個字節(jié)所能表示的有符號整數(shù)就可以達(dá)到 20 多億(注:2^31=2147483648,另外 1 位作為符號位),對于絕大多數(shù)情況都是可以處理的。
所以我們可以將一個對象的指針拆成兩部分,一部分直接保存數(shù)據(jù),另一部分作為特殊標(biāo)記,表示這是一個特別的指針,不指向任何一個地址。所以,引入了Tagged Pointer對象之后,64 位 CPU 下NSNumber 的內(nèi)存圖變成了以下這樣:

-
Tagged Pointer是專??來存儲?的對象,例如NSNumber,NSDate等。 -
Tagged Pointer指針的值不再是地址了,?是真正的值。所以,實際上它不再是?個對象了,它只是?個披著對象?的普通變量?已。所以,它的內(nèi)存并不存儲在堆中,也不需要malloc和free。 - 當(dāng)指針不夠存儲數(shù)據(jù)時,就會使用動態(tài)分配內(nèi)存的方式來存儲數(shù)據(jù)。
- 在內(nèi)存讀取上有著
3倍的效率,創(chuàng)建時?以前快106倍。
總結(jié)
蘋果將Tagged Pointer引入,給64位系統(tǒng)帶來了內(nèi)存的節(jié)省和運行效率的提高。Tagged Pointer通過在其最后一個 bit 位設(shè)置一個特殊標(biāo)記,用于將數(shù)據(jù)直接保存在指針本身中。因為Tagged Pointer并不是真正的對象。
nonpointer

nonpointer
-
0,代表普通的指針,存儲著Class、Meta-Class對象的內(nèi)存地址 -
1,代表優(yōu)化過,使用位域存儲更多的信息
has_assoc
- 是否有設(shè)置過關(guān)聯(lián)對象,如果沒有,釋放時會更快
has_cxx_dtor
- 是否有
C++的析構(gòu)函數(shù)(.cxx_destruct),如果沒有,釋放時會更快
shiftcls
- 存儲著
Class、Meta-Class對象的內(nèi)存地址信息
magic
- 用于在調(diào)試時分辨對象是否未完成初始化
weakly_referenced
- 是否有被弱引用指向過,如果沒有,釋放時會更快
deallocating
- 對象是否正在釋放
extra_rc
- 里面存儲的值是引用計數(shù)器減1
has_sidetable_rc
- 引用計數(shù)器是否過大無法存儲在
isa中 - 如果為
1,那么引用計數(shù)會存儲在一個叫SideTable的類的屬性中
參考鏈接:巧神:
深入理解Tagged Pointer