本文主要是實(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謝謝了