NSString在不同的初始化方式導(dǎo)致的神奇現(xiàn)象

本文主要是實(shí)驗(yàn)了兩個(gè)不同的NSString初始化方法導(dǎo)致的奇怪現(xiàn)象,因?yàn)槟芰τ邢蓿贿M(jìn)行了一番簡(jiǎn)單的解釋,拋磚引玉。最后附上了一些猜想,供君參考。

1.當(dāng)NSString長(zhǎng)度小于10時(shí),不再遵循引用計(jì)數(shù)規(guī)則

如果不能理解【引用計(jì)數(shù)規(guī)則】可以參考下另一篇文章iOS中copy,strong,retain,weak和assign的區(qū)別
實(shí)驗(yàn)代碼如下

NSString *stringLess10 = [[NSString alloc] initWithUTF8String:"123456789"];
NSString *string1Less10 = [[NSString alloc] initWithUTF8String:"123456789"];
NSLog(@"stringLess10值地址%p,引用計(jì)數(shù)%@",stringLess10,[stringLess10 valueForKey:@"retainCount"]);
NSLog(@"string1Less10值地址%p,引用計(jì)數(shù)%@",string1Less10,[string1Less10 valueForKey:@"retainCount"]);

NSString *stringMore10 = [[NSString alloc] initWithUTF8String:"12345678910"];
NSString *string1More10 = [[NSString alloc] initWithUTF8String:"12345678910"];
NSLog(@"stringMore10值地址%p,引用計(jì)數(shù)%@",stringMore10,[stringMore10 valueForKey:@"retainCount"]);
NSLog(@"string1More10值地址%p,引用計(jì)數(shù)%@",string1More10,[string1More10 valueForKey:@"retainCount"]);

輸出結(jié)果:

stringLess10值地址0xa1ea1f72bb30ab19,引用計(jì)數(shù)18446744073709551615
string1Less10值地址0xa1ea1f72bb30ab19,引用計(jì)數(shù)18446744073709551615
stringMore10值地址0x6080000306e0,引用計(jì)數(shù)1
string1More10值地址0x6080000306a0,引用計(jì)數(shù)1

現(xiàn)象:stringLess10和string1Less10值地址相同且引用計(jì)數(shù)異常,但在字符串長(zhǎng)度大于10的stringMore10和string1More10上這個(gè)現(xiàn)象就不存在了
解釋:當(dāng)NSString長(zhǎng)度小于10時(shí)不再遵循引用計(jì)數(shù)規(guī)則,Tagged Pointer技術(shù)對(duì)其進(jìn)行了優(yōu)化?;疽馑季褪悄J(rèn)會(huì)將一些長(zhǎng)度小于10的字符串直接保存在指針上面,下次創(chuàng)建相同值的時(shí)候直接用同一份拷貝,這樣既減少了一次指針到值的訪問(wèn),又減少了一份內(nèi)存的占用。
深入資料:http://www.cocoachina.com/ios/20150918/13449.html

2.NSString直接字符串賦值的異常

實(shí)驗(yàn)代碼:

NSString *stringWithOutInit = @"1234567891011";
NSLog(@"stringWithOutInit,值地址%p,引用計(jì)數(shù)%@",stringWithOutInit,[stringWithOutInit valueForKey:@"retainCount"]);

輸出結(jié)果:

stringWithOutInit,值地址0x1081f7180,引用計(jì)數(shù)18446744073709551615

現(xiàn)象:值地址相當(dāng)靠前,如一般是0xa1ea1f72bb30ab19,而他是0x1081f7180。另外引用計(jì)數(shù)非常大。
解釋:直接字符串賦值和init系列初始化方法有所不同。前者創(chuàng)建的是一個(gè)常量,不遵循引用計(jì)數(shù)。且在App結(jié)束前不會(huì)被釋放掉。引用計(jì)數(shù)在這個(gè)不能被釋放的內(nèi)存塊上默認(rèn)返回是一個(gè)很大的值
我們通過(guò)weak類型來(lái)接收這個(gè)值,并把stringWithOutInit設(shè)置nil,驗(yàn)證原來(lái)的內(nèi)存塊是否會(huì)釋放
實(shí)驗(yàn)代碼

__weak NSString *weakStr = stringWithOutInit;
stringWithOutInit = nil;
NSLog(@"weakStr指針地址%p,值地址%p,引用計(jì)數(shù)%@,值為%@", &weakStr,weakStr,[weakStr valueForKey:@"retainCount"],weakStr);

輸出:

weakStr值地址0x1081f7180,值為1234567891011

發(fā)現(xiàn)值地址就是之前stringWithOutInit的值地址,且值依舊存在。
現(xiàn)象:雖然只有弱指針指向這個(gè)內(nèi)存塊,但依舊有值存在,未被釋放。
解釋:因?yàn)槌A康囊糜?jì)數(shù)無(wú)限大,自然值就不會(huì)被釋放

3.附加猜測(cè)NSString內(nèi)存中的存儲(chǔ)方式

我們知道NSMutableString對(duì)象的指針地址和值地址分別在棧和堆上,那么我們可以通過(guò)他的指針地址和棧地址來(lái)猜測(cè)nsstring不同方式初始化的時(shí)候指針和值地址在堆還是在棧上
試驗(yàn)代碼:

NSMutableString *mstr = [[NSMutableString alloc] initWithFormat:@"%@",@"asdfasdfffffff"];
    NSLog(@"mstr指針地址:%p 值地址%p,引用計(jì)數(shù)%@",&mstr,mstr,[mstr valueForKey:@"retainCount"]);
    NSLog(@"stringLess10指針地址:%p 值地址%p,引用計(jì)數(shù)%@",&stringLess10,stringLess10,[stringLess10 valueForKey:@"retainCount"]);
    NSLog(@"stringMore10指針地址:%p 值地址%p,引用計(jì)數(shù)%@",&stringMore10,stringMore10,[stringMore10 valueForKey:@"retainCount"]);
    NSLog(@"stringWithOutInit指針地址:%p 值地址%p,引用計(jì)數(shù)%@",&stringWithOutInit,stringWithOutInit,[stringWithOutInit valueForKey:@"retainCount"]);

輸出結(jié)果:

mstr指針地址:0x7fff51dbbaa8 值地址0x60800006bc00,引用計(jì)數(shù)1
stringLess10指針地址:0x7fff51dbbaf8 值地址0xa1ea1f72bb30ab19,引用計(jì)數(shù)18446744073709551615
stringMore10指針地址:0x7fff51dbbae8 值地址0x600000024a80,引用計(jì)數(shù)1
stringWithOutInit指針地址:0x7fff51dbbad0 值地址0x0,引用計(jì)數(shù)(null)

現(xiàn)象和結(jié)論:
指針地址都是12位且值都相近,所以NSString不管初始化如何指針依舊保存在棧上面。
stringMore10和mstr值地址相近,所以init初始化方式在長(zhǎng)度大于10的時(shí)候默認(rèn)值存放在堆上。
stringLess值地址10長(zhǎng)度為16位,完全不在堆上。
stringWithOutInit值地址沒(méi)有,說(shuō)明他也不在堆上面,且和stringLess值存儲(chǔ)方式不一樣。

交流qq:578172874
錯(cuò)誤之處還希望能幫忙提出來(lái),一起學(xué)習(xí),O(∩_∩)O謝謝了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • __block和__weak修飾符的區(qū)別其實(shí)是挺明顯的:1.__block不管是ARC還是MRC模式下都可以使用,...
    LZM輪回閱讀 3,609評(píng)論 0 6
  • 多線程、特別是NSOperation 和 GCD 的內(nèi)部原理。運(yùn)行時(shí)機(jī)制的原理和運(yùn)用場(chǎng)景。SDWebImage的原...
    LZM輪回閱讀 2,132評(píng)論 0 12
  • iOS面試小貼士 ———————————————回答好下面的足夠了------------------------...
    不言不愛(ài)閱讀 2,255評(píng)論 0 7
  • ———————————————回答好下面的足夠了---------------------------------...
    恒愛(ài)DE問(wèn)候閱讀 1,846評(píng)論 0 4
  • 1.寫(xiě)一個(gè)NSString類的實(shí)現(xiàn) +(id)initWithCString:(c*****t char *)nu...
    韓七夏閱讀 3,878評(píng)論 2 37

友情鏈接更多精彩內(nèi)容