轉(zhuǎn)自:https://blog.cnbluebox.com/blog/2014/04/16/nsstringte-xing-fen-xi-xue-xi/
我們都知道NSString是一個Objective-C的類,但是我們有時發(fā)現(xiàn)它的對象在內(nèi)存管理上貌似和其他的對象有一些區(qū)別院究。比如有時你會發(fā)現(xiàn)對一個NSString進行copy操作時,它還是原本的對象秩仆,實際上并未拷貝對象。本博客就來研究下這個問題渣玲。
1.NSString內(nèi)存管理特性分析
1.1 準備
為了方便測試逗概,我先寫了個宏,用來打印NSString的isa、內(nèi)存地址逾苫、值卿城、retainCount。 注:為了了解內(nèi)存特性铅搓,后面的代碼都使用了手動內(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測試NSString
在objc中,我們一般通過幾種方法來創(chuàng)建NSString呢星掰,一般有三種方法多望,現(xiàn)在我們就分別對這三種情況寫段測試代碼,如下:
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
看到上面這段測試代碼氢烘,我們可以發(fā)現(xiàn)幾點同我們想象不同的地方:
第一種方式和第二種方式創(chuàng)建出來的NSString時一模一樣的怀偷,isa是__NSCFConstantString,內(nèi)存地址一樣播玖,retainCount是-1.
第三種方式創(chuàng)建的NSString和創(chuàng)建其他objc對象類似的椎工,在堆上分配內(nèi)存,初始retainCount為1.
這里面有幾個疑問:
什么是__NSCFConstantString蜀踏?
為什么第一種和第二種NSString的內(nèi)存地址是一樣的维蒙?
為什么他們的retainCount是-1?
1.2.2 NSString創(chuàng)建的寫法
其實上面第一種寫法和第二種寫法是完全一樣的果覆,沒有任何區(qū)別颅痊,從iosSDK6開始,第二種寫法已經(jīng)被遺棄了局待,如果用第二種寫法創(chuàng)建NSString,編譯器就會報一個警告斑响。
1.2.3 retainCount為-1是什么情況
首先retainCount是NSUInteger的類型,其實上面的打印是將它作為int類型打印钳榨。所以它其實不是-1恋捆,它的實際值是4294967295。
在objc的retainCount中.如果對象的retainCount為這個值重绷,就意味著“無限的retainCount”,這個對象是不能被釋放的膜毁。
所有的 __NSCFConstantString對象的retainCount都為-1昭卓,這就意味著 __NSCFConstantString不會被釋放,使用第一種方法創(chuàng)建的NSString瘟滨,如果值一樣候醒,無論寫多少遍,都是同一個對象杂瘸。而且這種對象可以直接用 == 來比較
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
我們寫一段代碼分別對 __NSCFConstantString 和 __NSCFString 進行retain和copy測試
__NSCFConstantString
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
*/
上面的測試可以看出,對一個__NSCFConstantString進行retain和copy操作都還是自己败玉,沒有任何變化敌土,對其mutableCopy操作可將其拷貝到堆上镜硕,retainCount為1.
__NSCFString
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
*/
上面的測試中,我們發(fā)現(xiàn)返干,對__NSCFString進行retain和mutableCopy操作時兴枯,其特性符合正常的對象特性。但是對其copy時矩欠,它卻變成了一個__NSCFConstantString對象财剖!為了確定什么情況下才會出現(xiàn)這種現(xiàn)象我們多做一些測試
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
*/
起初我以為是ASCII字符比較特殊,經(jīng)過上面這一段的測試發(fā)現(xiàn)癌淮,只有@“a”才有這樣的現(xiàn)象躺坟,我又用模擬器測試了這一段代碼,結(jié)果得到的都是__NSCFString的對象乳蓄。這個問題研究了一會咪橙,沒找到答案,暫時就放下了栓袖,好在這個對于我們編碼沒什么影響匣摘。
問題遺留
2. 小結(jié)
經(jīng)過這一系列的測試分析,讓我們認識了__NSCFConstantString以及它的一些特性裹刮,它是在編譯時就決定的音榜,不能在運行時創(chuàng)建。