前言
在 2013
年 9
月,蘋果推出了 iPhone5s
,與此同時,iPhone5s
配備了首個采用 64
位架構的 A7
雙核處理器,為了節省內存和提高執行效率,蘋果提出了Tagged Pointer
的概念。對于 64
位程序,引入 Tagged Pointer
后,相關邏輯能減少一半的內存占用,以及3
倍的訪問速度提升,100
倍的創建、銷毀速度提升。
問題
我們先看看原有的對象為什么會浪費內存?
假設我們要存儲一個 NSNumber
對象,其值是一個整數。正常情況下,如果這個整數只是一個 NSInteger
的普通變量,那么它所占用的內存是與 CPU
的位數有關,在 32
位 CPU
下占 4
個字節,在 64
位CPU
下是占 8
個字節的。而指針類型的大小通常也是與CPU
位數相關,一個指針所占用的內存在 32
位 CPU
下為4
個字節,在 64
位 CPU
下也是8
個字節。
所以一個普通的 iOS
程序,如果沒有Tagged Pointer
對象,從 32
位機器遷移到 64
位機器中后,雖然邏輯沒有任何變化,但這種 NSNumber
、NSDate
一類的對象所占用的內存會翻倍。如下圖所示:
Tagged Pointer
為了改進上面提到的內存占用和效率問題,蘋果提出了Tagged Pointer
對象。由于 NSNumber
、NSDate
一類的變量本身的值需要占用的內存大小常常不需要8
個字節,拿整數來說,4
個字節所能表示的有符號整數就可以達到 20
多億(注:2^31=2147483648
,另外 1
位作為符號位),對于絕大多數情況都是可以處理的。
所以我們可以將一個對象的指針拆成兩部分,一部分直接保存數據,另一部分作為特殊標記,表示這是一個特別的指針,不指向任何一個地址。所以,引入了Tagged Pointer
對象之后,64
位 CPU
下NSNumber
的內存圖變成了以下這樣:
-
Tagged Pointer
是專??來存儲?的對象,例如NSNumber,NSDate
等。 -
Tagged Pointer
指針的值不再是地址了,?是真正的值。所以,實際上它不再是?個對象了,它只是?個披著對象?的普通變量?已。所以,它的內存并不存儲在堆中,也不需要malloc
和free
。 - 當指針不夠存儲數據時,就會使用動態分配內存的方式來存儲數據。
- 在內存讀取上有著
3
倍的效率,創建時?以前快106
倍。
總結
蘋果將Tagged Pointer
引入,給64
位系統帶來了內存的節省和運行效率的提高。Tagged Pointer
通過在其最后一個 bit
位設置一個特殊標記,用于將數據直接保存在指針本身中。因為Tagged Pointer
并不是真正的對象。
nonpointer
nonpointer
-
0,
代表普通的指針,存儲著Class
、Meta-Class
對象的內存地址 -
1
,代表優化過,使用位域存儲更多的信息
has_assoc
- 是否有設置過關聯對象,如果沒有,釋放時會更快
has_cxx_dtor
- 是否有
C++
的析構函數(.cxx_destruct
),如果沒有,釋放時會更快
shiftcls
- 存儲著
Class
、Meta-Class
對象的內存地址信息
magic
- 用于在調試時分辨對象是否未完成初始化
weakly_referenced
- 是否有被弱引用指向過,如果沒有,釋放時會更快
deallocating
- 對象是否正在釋放
extra_rc
- 里面存儲的值是引用計數器減1
has_sidetable_rc
- 引用計數器是否過大無法存儲在
isa
中 - 如果為
1
,那么引用計數會存儲在一個叫SideTable
的類的屬性中
參考鏈接:巧神:
深入理解Tagged Pointer