iOS Tagged Pointer (原始碼閱讀必備知識)

Junyiii發表於2017-04-24

Tagged Pointer 介紹

蘋果對於Tagged Pointer特點的介紹:

  1. Tagged Pointer專門用來儲存小的物件,例如NSNumber和NSDate
  2. Tagged Pointer指標的值不再是地址了,而是真正的值。所以,實際上它不再是一個物件了,它只是一個披著物件皮的普通變數而已。所以,它的記憶體並不儲存在堆中,也不需要malloc和free。
  3. 在記憶體讀取上有著3倍的效率,建立時比以前快106倍。

為什麼要引入Tagged Pointer

iPhone5s 採用64位處理器。
對於64位程式,我們的資料型別的長度是跟CPU的長度有關的。

iOS Tagged Pointer (原始碼閱讀必備知識)

這樣就導致了 一些物件佔用的記憶體會翻倍。

同時 維護程式中的物件需要 分配記憶體,維護引用計數,管理生命週期,使用物件給程式的執行增加了負擔。

Tagged Pointer

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

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

iOS Tagged Pointer (原始碼閱讀必備知識)

測試

#import 

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSNumber *number1 = @1;
        NSNumber *number2 = @2;
        NSNumber *number3 = @3;
        NSNumber *numberFFFF = @(0xFFFF);

        NSNumber *numberLager = @(MAXFLOAT);

        NSLog(@"number1 pointer is %p", number1);
        NSLog(@"number2 pointer is %p", number2);
        NSLog(@"number3 pointer is %p", number3);
        NSLog(@"numberLager pointer is %p", numberLager);

        /*
         2017-03-10 12:07:50.731726 TaggedPoint[1690:50438] number1 pointer is 0x127
         2017-03-10 12:07:50.731992 TaggedPoint[1690:50438] number2 pointer is 0x227
         2017-03-10 12:07:50.732011 TaggedPoint[1690:50438] number3 pointer is 0x327
         2017-03-10 12:07:50.732043 TaggedPoint[1690:50438] numberLager pointer is 0x1002006a0
         */


    }
    return 0;
}複製程式碼

以 0x127 為例 去掉 tag27(假設27為標記) 0x1 就是number 的值。
0x227
0x327
都有這種規律

numberLager 儲存的值為MAXFloat 顯然超過了tagged pointer 可以儲存的範圍。
所以列印的地址是單純的指標地址,指向儲存numberLager的記憶體地址。

對於isa指標的影響

因為tagged pointer 不是一個真正的物件,如果使用isa指標在編譯時會報錯。
如圖:

iOS Tagged Pointer (原始碼閱讀必備知識)

提示我們改為object_getClass()
object_getClass()中做了相應的處理

由於object_getClass()沒有對應的實現,只能從其他地方窺探一二
objc-weak.mm

weak_read_no_lock(weak_table_t *weak_table, id *referrer_id) 
{
    objc_object **referrer = (objc_object **)referrer_id;
    objc_object *referent = *referrer;
    if (referent->isTaggedPointer()) return (id)referent;
    //...
}複製程式碼
inline bool 
objc_object::isTaggedPointer() 
{
#if SUPPORT_TAGGED_POINTERS
    return ((uintptr_t)this & TAG_MASK);
#else
    return false;
#endif
}複製程式碼

這裡取物件的值做了一些判斷
如果是tagged pointer , 物件的值就是指標
如果非tagged pointer , 物件的值是指標指向的記憶體區域中的值

相關文章