深入理解Tagged Pointer

2021-07-08 18:54:12 字數 2846 閱讀 9205

我們先看看原有的物件為什麼會浪費記憶體。假設我們要儲存乙個nsnumber物件,其值是乙個整數。正常情況下,如果這個整數只是乙個nsinteger的普通變數,那麼它所占用的記憶體是與cpu的位數有關,在32位cpu下佔4個位元組,在64位cpu下是佔8個位元組的。而指標型別的大小通常也是與cpu位數相關,乙個指標所占用的內存在32位cpu下為4個位元組,在64位cpu下也是8個位元組。

所以乙個普通的ios程式,如果沒有tagged pointer物件,從32位機器遷移到64位機器中後,雖然邏輯沒有任何變化,但這種nsnumber、nsdate一類的物件所占用的記憶體會翻倍。如下圖所示:

我們再來看看效率上的問題,為了儲存和訪問乙個nsnumber物件,我們需要在堆上為其分配記憶體,另外還要維護它的引用計數,管理它的生命期。這些都給程式增加了額外的邏輯,造成執行效率上的損失。

為了改進上面提到的記憶體占用和效率問題,蘋果提出了tagged pointer物件。由於nsnumber、nsdate一類的變數本身的值需要占用的記憶體大小常常不需要8個位元組,拿整數來說,4個位元組所能表示的有符號整數就可以達到20多億(注:2^31=2147483648,另外1位作為符號位),對於絕大多數情況都是可以處理的。

所以我們可以將乙個物件的指標拆成兩部分,一部分直接儲存資料,另一部分作為特殊標記,表示這是乙個特別的指標,不指向任何乙個位址。所以,引入了tagged pointer物件之後,64位cpu下nsnumber的記憶體圖變成了以下這樣:

對此,我們也可以用 xcode做實驗來驗證。我們的實驗**如下:

int main(int argc, char * argv)

}

在該**中,我們將幾個number型別的指標的值直接輸出。需要注意的是,我們需要將模擬器切換成 64位的cpu來測試,如下圖所示:

執行之後,我們得到的結果如下,可以看到,除去最後的數字最末尾的2以及最開頭的0xb,其它數字剛好表示了相應nsnumber的值。

number1 pointer is 0xb000000000000012

number2 pointer is 0xb000000000000022

number3 pointer is 0xb000000000000032

numberffff pointer is 0xb0000000000ffff2

可見,蘋果確實是將值直接儲存到了指標本身裡面。我們還可以猜測,數字最末尾的2以及最開頭的0xb是否就是蘋果對於tagged pointer的特殊標記呢?我們嘗試放乙個8位元組的長的整數到nsnumber例項中,對於這樣的例項,由於tagged pointer無法將其按上面的壓縮方式來儲存,那麼應該就會以普通物件的方式來儲存,我們的實驗**如下:

nsnumber *bignumber = @(0xefffffffffffffff);

nslog(@"bignumber pointer is %p", bignumber);

執行之後,結果如下,驗證了我們的猜測,bignumber的位址更像是乙個普通的指標位址,和它本身的值看不出任何關係:

bignumber pointer is 0x10921ecc0
可見,當8位元組可以承載用於表示的數值時,系統就會以tagged pointer的方式生成指標,如果8位元組承載不了時,則又用以前的方式來生成普通的指標。關於以上關於tag pointer的儲存細節,我們也可以在這裡找到相應的討論,但是其中關於tagged pointer的實現細節與我們的實驗並不相符,筆者認為可能是蘋果更改了具體的實現細節,並且這並不影響tagged pointer我們討論tagged pointer本身的優點。

tagged pointer專門用來儲存小的物件,例如nsnumbernsdatetagged pointer指標的值不再是位址了,而是真正的值。所以,實際上它不再是乙個物件了,它只是乙個披著物件皮的普通變數而已。所以,它的記憶體並不儲存在堆中,也不需要malloc和free。

在記憶體讀取上有著3倍的效率,建立時比以前快106倍。

由此可見,蘋果引入tagged pointer,不但減少了64位機器下程式的記憶體占用,還提高了執行效率。完美地解決了小記憶體物件在儲存和訪問效率上的問題。

tagged pointer的引入也帶來了問題,即tagged pointer因為並不是真正的物件,而是乙個偽物件,所以你如果完全把它當成物件來使,可能會讓它露馬腳。比如我在《objective-c物件模型及應用》一文中就寫道,所有物件都有isa指標,而tagged pointer其實是沒有的,因為它不是真正的物件。 因為不是真正的物件,所以如果你直接訪問tagged pointerisa成員的話,在編譯時將會有如下警告:

對於上面的寫法,應該換成相應的方法呼叫,如iskindofclassobject_getclass。只要避免在**中直接訪問物件的isa變數,即可避免這個問題。

蘋果將tagged pointer引入,給64位系統帶來了記憶體的節省和執行效率的提高。tagged pointer通過在其最後乙個bit位設定乙個特殊標記,用於將資料直接儲存在指標本身中。因為tagged pointer並不是真正的物件,我們在使用時需要注意不要直接訪問其isa變數。

深入理解C語言 深入理解指標

關於指標,其是c語言的重點,c語言學的好壞,其實就是指標學的好壞。其實指標並不複雜,學習指標,要正確的理解指標。指標也是一種變數,占有記憶體空間,用來儲存記憶體位址 指標就是告訴編譯器,開闢4個位元組的儲存空間 32位系統 無論是幾級指標都是一樣的 p操作記憶體 在指標宣告時,號表示所宣告的變數為指...

mysql 索引深入理解 深入理解MySql的索引

為什麼索引能提高查詢速度 先從 mysql的基本儲存結構說起 mysql的基本儲存結構是頁 記錄都存在頁裡邊 各個資料頁可以組成乙個雙向鍊錶每個資料頁中的記錄又可以組成乙個單向鍊錶 每個資料頁都會為儲存在它裡邊兒的記錄生成乙個頁目錄,在通過主鍵查詢某條記錄的時候可以在頁目錄中使用二分法快速定位到對應...

深入理解C語言 深入理解指標

關於指標,其是c語言的重點,c語言學的好壞,其實就是指標學的好壞。其實指標並不複雜,學習指標,要正確的理解指標。指標也是一種變數,占有記憶體空間,用來儲存記憶體位址 指標就是告訴編譯器,開闢4個位元組的儲存空間 32位系統 無論是幾級指標都是一樣的 p操作記憶體 在指標宣告時,號表示所宣告的變數為指...