一往弓、NSString的內(nèi)存管理機(jī)制
1植锉、眾所周知,在Objective-C中的內(nèi)存管理是通過(guò)一種叫做“引用計(jì)數(shù)器”的機(jī)制管理的簇宽。舉例勋篓, 當(dāng)我們聲明了一個(gè)新的實(shí)例:
NSData *data = [[NSData alloc] init];
NSLog(@"%ld",[data retainCount]);//打印輸出為1
如果我們?cè)俅我盟臅r(shí)候,他的引用計(jì)數(shù)值會(huì)+1變?yōu)?魏割。使用完畢后需要調(diào)用[data release] ,使引用計(jì)數(shù)-1. 當(dāng)該值為零的時(shí)候譬嚣,系統(tǒng)會(huì)將data實(shí)例回收掉并釋放內(nèi)存。
[data release];
2钞它、請(qǐng)問(wèn)在這種情況下會(huì)不會(huì)造成內(nèi)存泄露拜银?為什么不會(huì)崩潰?
代碼如下:
NSString *str = [[NSString alloc] initWithString:@"ABC"];
str = @"123";
[str release];
NSLog(@"%@".str);
(1)首先遭垛,咱們先對(duì)這段代碼進(jìn)行分析尼桶。
- 第一句 聲明了一個(gè)NSString類型的實(shí)例 str, 并將其初始化init后賦值為@"ABC"
- 第二行锯仪,將str的指針指向了一個(gè)常量@"123"泵督。 理論上講在第一行初始化的@"ABC"沒(méi)有任何任何指針指向了。 所以造成了內(nèi)存泄露
- 然后第三行庶喜, 將str的引用計(jì)數(shù)-1
- 第四行輸出str的值 為123.
(2)然后回答為什么不會(huì)崩潰小腊, 因?yàn)榈谌械膔elease 實(shí)際上是release了一個(gè)常量@"123" 而作為常量,其默認(rèn)的引用計(jì)數(shù)值是很大的(100k+)溃卡,不信的話你們可以試試這句:
NSLog(@"retainCount = %d",[@"123" retainCount]);
(3)最終的輸出值會(huì)是一個(gè)很大很大的數(shù)溢豆。 所以單單一個(gè)release是不會(huì)將其釋放掉的。
(4)最后再回答這樣會(huì)不會(huì)造成內(nèi)存泄露瘸羡。理論上是會(huì)內(nèi)存泄漏的。
但是實(shí)際上搓茬,Objective-C對(duì)NSString類型有特殊照顧犹赖。所有的NSString的引用計(jì)數(shù)器默認(rèn)初始值都會(huì)非常非常大。
NSString是一個(gè)不可變的字符串對(duì)象卷仑。這不是表示這個(gè)對(duì)象聲明的變量的值不可變峻村,而是表示它初始化以后,你不能改變?cè)撟兞克峙涞膬?nèi)存中的值锡凝,但你可以重新分配該變量所處的內(nèi)存空間粘昨。
二、__NSCFConstantString的解釋
那么__NSCFConstantString是什么呢窜锯?其實(shí)__NSCFConstantString是一個(gè)字符串常量张肾,是沒(méi)有retainCount(引用計(jì)數(shù))的,所以沒(méi)有強(qiáng)指針指向它锚扎,它也不會(huì)被銷毀吞瞪。那么,怎么獲得不是__NSCFConstantString的字符串呢驾孔?
NSString *string1 = @"string 1";
NSString *string2 = [NSString stringWithString:@"string 2"];
NSString *string3 = [NSString stringWithFormat:@"string 3"];
NSString *string4 = [[NSString alloc] initWithString:@"string 4"];
NSString *string5 = [[NSMutableString alloc] initWithString:@"string"];
NSLog(@"%@:%@",string1,[string1 class]);
NSLog(@"%@:%@",string2,[string2 class]);
NSLog(@"%@:%@",string3,[string3 class]);
NSLog(@"%@:%@",string4,[string4 class]);
NSLog(@"%@:%@",string5,[string5 class]);
運(yùn)行結(jié)果:
2015-04-18 10:14:35.587 內(nèi)存管理[6631:201537] string 1:__NSCFConstantString
2015-04-18 10:14:35.587 內(nèi)存管理[6631:201537] string 2:__NSCFConstantString
2015-04-18 10:14:35.587 內(nèi)存管理[6631:201537] string 3:NSTaggedPointerString
2015-04-18 10:14:35.587 內(nèi)存管理[6631:201537] string 4:__NSCFConstantString
2015-04-18 10:14:35.588 內(nèi)存管理[6631:201537] string5:__NSCFString
通過(guò)以上的運(yùn)行結(jié)果芍秆,發(fā)現(xiàn)只有使用[[NSMutableString alloc] initWithString:]得到的NSString才是__NSCFString惯疙。
三、NSString特性分析學(xué)習(xí)
我們都知道NSString是一個(gè)Objective-C的類妖啥,但是我們有時(shí)發(fā)現(xiàn)它的對(duì)象在內(nèi)存管理上貌似和其他的對(duì)象有一些區(qū)別霉颠。比如有時(shí)你會(huì)發(fā)現(xiàn)對(duì)一個(gè)NSString進(jìn)行copy操作時(shí),它還是原本的對(duì)象荆虱,實(shí)際上并未拷貝對(duì)象蒿偎。本博客就來(lái)研究下這個(gè)問(wèn)題。
1.NSString內(nèi)存管理特性分析
1.1 準(zhǔn)備
為了方便測(cè)試克伊,我先寫了個(gè)宏酥郭,用來(lái)打印NSString的isa、內(nèi)存地址愿吹、值不从、retainCount。 注:為了了解內(nèi)存特性犁跪,后面的代碼都使用了手動(dòng)內(nèi)存管理椿息。
#define TLog(_var) ({ NSString *name = @#_var; NSLog(@"%@: %@ -> %p : %@ %d", name, [_var class], _var, _var, (int)[_var retainCount]); })
1.2 NSString的創(chuàng)建
1.2.1測(cè)試NSString
在objc中,我們一般通過(guò)幾種方法來(lái)創(chuàng)建NSString呢坷衍,一般有三種方法寝优,現(xiàn)在我們就分別對(duì)這三種情況寫段測(cè)試代碼,如下:
NSString *str1 = @"1234567890"; TLog(str1);
//str1: __NSCFConstantString -> 0x715ec : 1234567890 -1
NSString *str2 = [NSString stringWithString:@"1234567890"]; TLog(str2);
//str2: __NSCFConstantString -> 0x715ec : 1234567890 -1
NSString *str3 = [NSString stringWithFormat:@"1234567890"]; TLog(str3);
//str3: __NSCFString -> 0x1557cb50 : 1234567890 1
看到上面這段測(cè)試代碼枫耳,我們可以發(fā)現(xiàn)幾點(diǎn)同我們想象不同的地方:
- 第一種方式和第二種方式創(chuàng)建出來(lái)的NSString時(shí)一模一樣的乏矾,isa是__NSCFConstantString,內(nèi)存地址一樣迁杨,retainCount是-1.
- 第三種方式創(chuàng)建的NSString和創(chuàng)建其他objc對(duì)象類似的钻心,在堆上分配內(nèi)存,初始retainCount為1.
這里面有幾個(gè)疑問(wèn): - 什么是__NSCFConstantString铅协?
- 為什么第一種和第二種NSString的內(nèi)存地址是一樣的捷沸?
- 為什么他們的retainCount是-1?
1.2.2 NSString創(chuàng)建的寫法
其實(shí)上面第一種寫法和第二種寫法是完全一樣的狐史,沒(méi)有任何區(qū)別痒给,從iosSDK6開(kāi)始,第二種寫法已經(jīng)被遺棄了骏全,如果用第二種寫法創(chuàng)建NSString,編譯器就會(huì)報(bào)一個(gè)警告苍柏。
1.2.3 retainCount為-1是什么情況
1、首先retainCount是NSUInteger的類型吟温,其實(shí)上面的打印是將它作為int類型打印序仙。所以它其實(shí)不是-1,它的實(shí)際值是4294967295鲁豪。
2潘悼、在objc的retainCount中.如果對(duì)象的retainCount為這個(gè)值律秃,就意味著“無(wú)限的retainCount”,這個(gè)對(duì)象是不能被釋放的治唤。
3棒动、所有的 __NSCFConstantString對(duì)象的retainCount都為-1,這就意味著 __NSCFConstantString不會(huì)被釋放宾添,使用第一種方法創(chuàng)建的NSString船惨,如果值一樣,無(wú)論寫多少遍缕陕,都是同一個(gè)對(duì)象粱锐。而且這種對(duì)象可以直接用 == 來(lái)比較
NSString *str1 = @"1234567890"; TLog(str1);
//str1: __NSCFConstantString -> 0x715ec : 1234567890 -1
NSString *str2 = @"1234567890"; TLog(str2);
//str2: __NSCFConstantString -> 0x715ec : 1234567890 -1
assert(@"abc"==@"abc"); //一直正確
1.3 NSString的retain、copy和mutableCopy
我們寫一段代碼分別對(duì) __NSCFConstantString 和 __NSCFString 進(jìn)行retain和copy測(cè)試
NSString *str1 = @"a"; TLog(str1);
NSString *str2 = [str1 retain]; TLog(str2);
NSString *str3 = [str1 copy]; TLog(str3);
NSString *str4 = [str1 mutableCopy]; TLog(str4);
/*
str1: __NSCFConstantString -> 0x7c5e0 : a -1
str2: __NSCFConstantString -> 0x7c5e0 : a -1
str3: __NSCFConstantString -> 0x7c5e0 : a -1
str4: __NSCFString -> 0x1559eb80 : a 1
*/
上面的測(cè)試可以看出扛邑,對(duì)一個(gè)__NSCFConstantString進(jìn)行retain和copy操作都還是自己怜浅,沒(méi)有任何變化,對(duì)其mutableCopy操作可將其拷貝到堆上蔬崩,retainCount為1.
NSString *str1 = [@"a" mutableCopy]; TLog(str1);
NSString *str2 = [str1 retain]; TLog(str2);
NSString *str3 = [str1 copy]; TLog(str3);
NSString *str4 = [str1 mutableCopy]; TLog(str4);
/*
str1: __NSCFString -> 0x17d6d280 : a 1
str2: __NSCFString -> 0x17d6d280 : a 2
str3: __NSCFConstantString -> 0x3bd40090 : a -1
str4: __NSCFString -> 0x17e684d0 : a 1
*/
上面的測(cè)試中恶座,我們發(fā)現(xiàn),對(duì)__NSCFString進(jìn)行retain和mutableCopy操作時(shí)沥阳,其特性符合正常的對(duì)象特性跨琳。但是對(duì)其copy時(shí),它卻變成了一個(gè)__NSCFConstantString對(duì)象桐罕!為了確定什么情況下才會(huì)出現(xiàn)這種現(xiàn)象我們多做一些測(cè)試
NSString *str1 = [[@"a" mutableCopy] copy]; TLog(str1);
NSString *str2 = [NSString stringWithFormat:@"%s","a"]; TLog(str2);
NSString *str3 = [[[@"path/a" lastPathComponent] mutableCopy] copy]; TLog(str3);
NSString *str4 = [[@"b" mutableCopy] copy]; TLog(str4);
NSString *str5 = [[@"c" mutableCopy] copy]; TLog(str5);
NSString *str6 = [[@"d" mutableCopy] copy]; TLog(str6);
NSString *str7 = [[@"e" mutableCopy] copy]; TLog(str7);
NSString *str8 = [[@"f" mutableCopy] copy]; TLog(str8);
NSString *str9 = [[@"\\" mutableCopy] copy]; TLog(str9);
NSString *str10 = [[@"$" mutableCopy] copy]; TLog(str10);
NSString *str11 = [[@"." mutableCopy] copy]; TLog(str11);
NSString *str12 = [[@"aa" mutableCopy] copy]; TLog(str12);
/*
str1: __NSCFConstantString -> 0x3bd40090 : a -1
str2: __NSCFConstantString -> 0x3bd40090 : a -1
str3: __NSCFConstantString -> 0x3bd40090 : a -1
str4: __NSCFString -> 0x175ab390 : b 1
str5: __NSCFString -> 0x176a5ce0 : c 1
str6: __NSCFString -> 0x175ab960 : d 1
str7: __NSCFString -> 0x176a5cc0 : e 1
str8: __NSCFString -> 0x176a5d50 : f 1
str9: __NSCFString -> 0x176a5d60 : \ 1
str10: __NSCFString -> 0x176a6700 : $ 1
str11: __NSCFString -> 0x175ab750 : . 1
str12: __NSCFString -> 0x175ab760 : aa 1
*/NSString *str1 = [[@"a" mutableCopy] copy]; TLog(str1);
NSString *str2 = [NSString stringWithFormat:@"%s","a"]; TLog(str2);
NSString *str3 = [[[@"path/a" lastPathComponent] mutableCopy] copy]; TLog(str3);
NSString *str4 = [[@"b" mutableCopy] copy]; TLog(str4);
NSString *str5 = [[@"c" mutableCopy] copy]; TLog(str5);
NSString *str6 = [[@"d" mutableCopy] copy]; TLog(str6);
NSString *str7 = [[@"e" mutableCopy] copy]; TLog(str7);
NSString *str8 = [[@"f" mutableCopy] copy]; TLog(str8);
NSString *str9 = [[@"\\" mutableCopy] copy]; TLog(str9);
NSString *str10 = [[@"$" mutableCopy] copy]; TLog(str10);
NSString *str11 = [[@"." mutableCopy] copy]; TLog(str11);
NSString *str12 = [[@"aa" mutableCopy] copy]; TLog(str12);
/*
str1: __NSCFConstantString -> 0x3bd40090 : a -1
str2: __NSCFConstantString -> 0x3bd40090 : a -1
str3: __NSCFConstantString -> 0x3bd40090 : a -1
str4: __NSCFString -> 0x175ab390 : b -1
str5: __NSCFString -> 0x176a5ce0 : c -1
str6: __NSCFString -> 0x175ab960 : d - 1
str7: __NSCFString -> 0x176a5cc0 : e -1
str8: __NSCFString -> 0x176a5d50 : f - 1
str9: __NSCFString -> 0x176a5d60 : \ -1
str10: __NSCFString -> 0x176a6700 : $ -1
str11: __NSCFString -> 0x175ab750 : . -1
str12: __NSCFString -> 0x175ab760 : aa -1
*/
2. 小結(jié)
經(jīng)過(guò)這一系列的測(cè)試分析脉让,讓我們認(rèn)識(shí)了__NSCFConstantString以及它的一些特性,它是在編譯時(shí)就決定的功炮,不能在運(yùn)行時(shí)創(chuàng)建侠鳄。