NSNumber
因為在從32位升級到64位后,同一個對象占用的內存會變成原來的2倍,為了節省內存和訪問效率而引入了TaggedPointer. 像NSNumber,NSDate的地址不是真正的地址,如果是NSNumber@(1),@(2)
,那么地址分別是是
(__NSCFNumber *) $0 = 0xb000000000000012 (int)1
(__NSCFNumber *) $3 = 0xb000000000000022 (int)2
我們發現倒數第2位就是存儲的真實值,它會以0xb......2
這樣的格式展示,指針包含兩部分,一部分是真實值,一部分是特殊標記。但是這樣的話,如果數值過大,位數就不夠了。我們測試一下,我們用一個特別大的值,@(0xEFFFFFFFFFFFFFFF)
,地址是:
(__NSCFNumber *) $5 = 0x00006000000320e0
結果很像一個指針,這個是真正的指針,它沒有用到TaggedPointer。
ISA失效
雖然taggedPointer有一定的好處,但是它并不是一真正的對象,而是個偽對象,所以我們不能直接獲取它的isa,否則就會報錯。為了獲取Isa,我們定義一個假的tempObject結構體來抽取isa指針:
struct tempObject {
Class isa;
};
ViewController *vc = [[ViewController alloc] init];
struct tempObject *oo = (__bridge struct tempObject *)number;
Class isa = oo->isa;
結果運行時報錯,訪問了野指針:
Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
說明它根本就沒有isa指針。但是如果我們用object_getClass()
或[NSNumber Class]
來獲取isa時會返正確的的Class:__NSCFNumber
。因為蘋果已經偽裝好了,很多runtime API里都會判斷是否是TaggedPointer對象,會對它單獨進行處理。