NSString存儲管理--NSTaggedPointerString

    NSString *strS = @"123456789";
    NSLog(@"%@ : %p",strS.class,strS);

    strS = [NSString stringWithFormat:@"1"];
    NSLog(@"%@ : %p",strS.class,strS);
    strS = [NSString stringWithUTF8String:"1234567"];
    strS = [NSString stringWithUTF8String:"abcdabc"];//7
    NSLog(@"%@ : %p",strS.class,strS);

    strS = [NSString stringWithUTF8String:"abcdabcd"];//8
    strS = [NSString stringWithUTF8String:"teeeeeeet"];//9
    NSLog(@"%@ : %p",strS.class,strS);

    strS = [NSString stringWithUTF8String:"eeeeeeeeee"];//10
    strS = [NSString stringWithUTF8String:"eeeeeeeeeee"];//11
    NSLog(@"%@ : %p",strS.class,strS);

    strS = [NSString stringWithUTF8String:"eeeeeeeeeeee"];//12
    NSLog(@"%@ : %p",strS.class,strS);

上面這段代碼輸出為:

__NSCFConstantString : 0x10c843820
NSTaggedPointerString : 0xa000000000000311
NSTaggedPointerString : 0xa636261646362617
NSTaggedPointerString : 0xa040000000000049
NSTaggedPointerString : 0xa00000000000000b
__NSCFString : 0x6000000315c0

我們知道oc存儲主要分成數(shù)據(jù)區(qū)、堆區(qū)和棧區(qū),

  • __NSCFConstantString顯然是常量字符串,地址0x10c843820自然就是存儲在數(shù)據(jù)區(qū)。
  • __NSCFString表示為oc對象,NSString就是封裝的CFString0x6000000315c0地址顯示這個(gè)字符串對象存儲在堆中。
  • NSTaggedPointerString這個(gè)類表示這是字符串的一種指針Tagged Pointer,0xa636261646362617這個(gè)地址為什么如此與眾不同呢,接下來我們就簡單介紹這鐘字符串的存儲指針。

在蘋果推出了 采用64位架構(gòu)的A7雙核處理器 iphone 5s的時(shí)候,為了節(jié)省內(nèi)存和提高執(zhí)行效率,蘋果提出了Tagged Pointer的概念。先看看原有的對象為什么會(huì)浪費(fèi)內(nèi)存。假設(shè)要存儲一個(gè) NSNumber 對象,其值是一個(gè)整數(shù)。正常情況下,如果這個(gè)整數(shù)只是一個(gè) NSInteger 的普通變量,那么它所占用的內(nèi)存是與CPU的位數(shù)有關(guān),在32位CPU下占4個(gè)字節(jié),在64位CPU下是占8個(gè)字節(jié)的。而指針類型的大小通常也是與CPU位數(shù)相關(guān),一個(gè)指針?biāo)加玫膬?nèi)存在32位CPU下為4個(gè)字節(jié),在64位CPU下也是8個(gè)字節(jié)。所以一個(gè)普通的iOS程序,如果沒有Tagged Pointer對象,從32位機(jī)器遷移到64位機(jī)器中后,雖然邏輯沒有任何變化,但這種NSNumber、NSDate一類的對象所占用的內(nèi)存會(huì)翻倍。

感興趣的朋友可以去嘗試下,這里只列出了NSString采用這種技術(shù)的結(jié)果。

對于以前的@""符號創(chuàng)建的字符串還是常量字符串,這個(gè)沒有改變,但是采用stringWithFormat 等NSString方法的創(chuàng)建字符串對象則有了區(qū)別。

在蘋果的64位OC實(shí)現(xiàn)中,若對象指針的二進(jìn)制第一位是1,則該指針為Tagged Pointer。
例如0xa000000000000311其中a的2進(jìn)制為1010,第一位1表示這是Tagged Pointer,010表示這是一個(gè)NSTaggedPointerString類;這個(gè)地址最后一位表示字符串的數(shù)目,這里是0001表示有1位字符串;其中真正用來存儲的位數(shù)只有中間的14位16進(jìn)制。這個(gè)地址本身其實(shí)就存儲了字符串的值,可以說是存儲在&strS內(nèi)存中值,只是偽裝成了地址,它不需要存儲在數(shù)據(jù)區(qū),也不需要申請堆空間。

NSTaggedPointerString的存儲有三種編碼方式:ASCII碼,六位編碼,五位編碼。

ASCII碼

我們發(fā)現(xiàn)NSTaggedPointerString存儲內(nèi)容除去第一位和最后一位,其實(shí)只有中間的14位16進(jìn)制字符,再看ascll碼由8位二進(jìn)制組成,所以這里(14*4) / 8=7,用8位的ascll碼的話最多可以存儲7個(gè)字符。字符串?dāng)?shù)目0~7之間

[NSString stringWithFormat:@"1"]輸出的地址 0xa000000000000311,其中31的2進(jìn)制是0011 0001,在ascll碼表里查找發(fā)現(xiàn)正是對應(yīng)著“1”;

[NSString stringWithUTF8String:"abcdabc"] 這里輸出的地址是0xa636261646362617 可以發(fā)現(xiàn)在使用ascll編碼時(shí),字符串對應(yīng)的編碼是從右向左存儲的

六位編碼:

NSTaggedPointerString 采用六位二進(jìn)制編碼,(14*4)/6=9.333…,可以看出最多存儲9位字符。字符數(shù)目8~9

五位編碼:

采用五位二進(jìn)制編碼,(14*4)/5 = 11.2,可以看出這種編碼最多存儲11位字符。字符數(shù)目在10~11

NSTaggedPointerString 存儲編碼中的六位和五位編碼都是根據(jù)通常代碼中字母使用頻率來排序的,但并不是一成不變的,apple會(huì)持續(xù)更新并統(tǒng)計(jì)字母使用頻率,系統(tǒng)每次升級都可能不一樣,當(dāng)前第一位是字母e,之后是i,l,o,t…;這兩種編碼是從左向右的;根據(jù)編碼位數(shù)我們顯然也能推測出并不是所有字符都可以進(jìn)行ascll或者六位五位編碼的,當(dāng)出現(xiàn)這樣不能編碼的時(shí)候,系統(tǒng)也就不會(huì)使用NSTaggedPointerString類。



參考:
1.Tagged Pointer Strings
2.iOS Tagged Pointer

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容