本文主要是實驗了兩個不同的NSString初始化方法導(dǎo)致的奇怪現(xiàn)象昧狮,因為能力有限,只進行了一番簡單的解釋终息,拋磚引玉。最后附上了一些猜想贞让,供君參考周崭。
1.當NSString長度小于10時,不再遵循引用計數(shù)規(guī)則
如果不能理解【引用計數(shù)規(guī)則】可以參考下另一篇文章iOS中copy,strong,retain,weak和assign的區(qū)別震桶。
實驗代碼如下
NSString *stringLess10 = [[NSString alloc] initWithUTF8String:"123456789"];
NSString *string1Less10 = [[NSString alloc] initWithUTF8String:"123456789"];
NSLog(@"stringLess10值地址%p,引用計數(shù)%@",stringLess10,[stringLess10 valueForKey:@"retainCount"]);
NSLog(@"string1Less10值地址%p,引用計數(shù)%@",string1Less10,[string1Less10 valueForKey:@"retainCount"]);
NSString *stringMore10 = [[NSString alloc] initWithUTF8String:"12345678910"];
NSString *string1More10 = [[NSString alloc] initWithUTF8String:"12345678910"];
NSLog(@"stringMore10值地址%p,引用計數(shù)%@",stringMore10,[stringMore10 valueForKey:@"retainCount"]);
NSLog(@"string1More10值地址%p,引用計數(shù)%@",string1More10,[string1More10 valueForKey:@"retainCount"]);
輸出結(jié)果:
stringLess10值地址0xa1ea1f72bb30ab19,引用計數(shù)18446744073709551615
string1Less10值地址0xa1ea1f72bb30ab19,引用計數(shù)18446744073709551615
stringMore10值地址0x6080000306e0,引用計數(shù)1
string1More10值地址0x6080000306a0,引用計數(shù)1
現(xiàn)象:stringLess10和string1Less10值地址相同且引用計數(shù)異常休傍,但在字符串長度大于10的stringMore10和string1More10上這個現(xiàn)象就不存在了
解釋:當NSString長度小于10時不再遵循引用計數(shù)規(guī)則,Tagged Pointer技術(shù)對其進行了優(yōu)化蹲姐∧ト。基本意思就是默認會將一些長度小于10的字符串直接保存在指針上面,下次創(chuàng)建相同值的時候直接用同一份拷貝柴墩,這樣既減少了一次指針到值的訪問忙厌,又減少了一份內(nèi)存的占用。
深入資料:http://www.cocoachina.com/ios/20150918/13449.html
2.NSString直接字符串賦值的異常
實驗代碼:
NSString *stringWithOutInit = @"1234567891011";
NSLog(@"stringWithOutInit,值地址%p,引用計數(shù)%@",stringWithOutInit,[stringWithOutInit valueForKey:@"retainCount"]);
輸出結(jié)果:
stringWithOutInit,值地址0x1081f7180,引用計數(shù)18446744073709551615
現(xiàn)象:值地址相當靠前江咳,如一般是0xa1ea1f72bb30ab19逢净,而他是0x1081f7180。另外引用計數(shù)非常大歼指。
解釋:直接字符串賦值和init系列初始化方法有所不同爹土。前者創(chuàng)建的是一個常量,不遵循引用計數(shù)踩身。且在App結(jié)束前不會被釋放掉胀茵。引用計數(shù)在這個不能被釋放的內(nèi)存塊上默認返回是一個很大的值
我們通過weak類型來接收這個值,并把stringWithOutInit設(shè)置nil挟阻,驗證原來的內(nèi)存塊是否會釋放
實驗代碼
__weak NSString *weakStr = stringWithOutInit;
stringWithOutInit = nil;
NSLog(@"weakStr指針地址%p,值地址%p,引用計數(shù)%@,值為%@", &weakStr,weakStr,[weakStr valueForKey:@"retainCount"],weakStr);
輸出:
weakStr值地址0x1081f7180,值為1234567891011
發(fā)現(xiàn)值地址就是之前stringWithOutInit的值地址琼娘,且值依舊存在。
現(xiàn)象:雖然只有弱指針指向這個內(nèi)存塊附鸽,但依舊有值存在脱拼,未被釋放。
解釋:因為常量的引用計數(shù)無限大坷备,自然值就不會被釋放
3.附加猜測NSString內(nèi)存中的存儲方式
我們知道NSMutableString對象的指針地址和值地址分別在棧和堆上熄浓,那么我們可以通過他的指針地址和棧地址來猜測nsstring不同方式初始化的時候指針和值地址在堆還是在棧上
試驗代碼:
NSMutableString *mstr = [[NSMutableString alloc] initWithFormat:@"%@",@"asdfasdfffffff"];
NSLog(@"mstr指針地址:%p 值地址%p,引用計數(shù)%@",&mstr,mstr,[mstr valueForKey:@"retainCount"]);
NSLog(@"stringLess10指針地址:%p 值地址%p,引用計數(shù)%@",&stringLess10,stringLess10,[stringLess10 valueForKey:@"retainCount"]);
NSLog(@"stringMore10指針地址:%p 值地址%p,引用計數(shù)%@",&stringMore10,stringMore10,[stringMore10 valueForKey:@"retainCount"]);
NSLog(@"stringWithOutInit指針地址:%p 值地址%p,引用計數(shù)%@",&stringWithOutInit,stringWithOutInit,[stringWithOutInit valueForKey:@"retainCount"]);
輸出結(jié)果:
mstr指針地址:0x7fff51dbbaa8 值地址0x60800006bc00,引用計數(shù)1
stringLess10指針地址:0x7fff51dbbaf8 值地址0xa1ea1f72bb30ab19,引用計數(shù)18446744073709551615
stringMore10指針地址:0x7fff51dbbae8 值地址0x600000024a80,引用計數(shù)1
stringWithOutInit指針地址:0x7fff51dbbad0 值地址0x0,引用計數(shù)(null)
現(xiàn)象和結(jié)論:
指針地址都是12位且值都相近,所以NSString不管初始化如何指針依舊保存在棧上面省撑。
stringMore10和mstr值地址相近玉组,所以init初始化方式在長度大于10的時候默認值存放在堆上谎柄。
stringLess值地址10長度為16位,完全不在堆上惯雳。
stringWithOutInit值地址沒有朝巫,說明他也不在堆上面,且和stringLess值存儲方式不一樣石景。
交流qq:578172874
錯誤之處還希望能幫忙提出來,一起學(xué)習(xí)劈猿,O(∩_∩)O謝謝了