對(duì)于NSString 何時(shí)使用strong,何時(shí)使用copy,首頁(yè)讓我們來(lái)具體的了解一下NSString的一些內(nèi)部特性,下面一篇文章總結(jié)的非常好:
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è)試代碼,如下:
12345678
NSString*str1=@"1234567890";TLog(str1);//str1: __NSCFConstantString -> 0x715ec : 1234567890? -1NSString*str2=[NSStringstringWithString:@"1234567890"];TLog(str2);//str2: __NSCFConstantString -> 0x715ec : 1234567890? -1NSString*str3=[NSStringstringWithFormat:@"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是什么情況
首先retainCount是NSUInteger的類型,其實(shí)上面的打印是將它作為int類型打印。所以它其實(shí)不是-1,它的實(shí)際值是4294967295。
在objc的retainCount中.如果對(duì)象的retainCount為這個(gè)值,就意味著“無(wú)限的retainCount”,這個(gè)對(duì)象是不能被釋放的。
所有的 __NSCFConstantString對(duì)象的retainCount都為-1,這就意味著 __NSCFConstantString不會(huì)被釋放,使用第一種方法創(chuàng)建的NSString,如果值一樣,無(wú)論寫多少遍,都是同一個(gè)對(duì)象。而且這種對(duì)象可以直接用==來(lái)比較
1234567
NSString*str1=@"1234567890";TLog(str1);//str1: __NSCFConstantString -> 0x715ec : 1234567890? -1NSString*str2=@"1234567890";TLog(str2);//str2: __NSCFConstantString -> 0x715ec : 1234567890? -1assert(@"abc"==@"abc");//一直正確
1.3 NSString的retain、copy和mutableCopy
我們寫一段代碼分別對(duì) __NSCFConstantString 和 __NSCFString 進(jìn)行retain和copy測(cè)試
__NSCFConstantString
1234567891011
NSString*str1=@"a";TLog(str1);NSString*str2=[str1retain];TLog(str2);NSString*str3=[str1copy];TLog(str3);NSString*str4=[str1mutableCopy];TLog(str4);/*str1: __NSCFConstantString -> 0x7c5e0 : a? -1str2: __NSCFConstantString -> 0x7c5e0 : a? -1str3: __NSCFConstantString -> 0x7c5e0 : a? -1str4: __NSCFString -> 0x1559eb80 : a? 1*/
上面的測(cè)試可以看出,對(duì)一個(gè)__NSCFConstantString進(jìn)行retain和copy操作都還是自己,沒(méi)有任何變化,對(duì)其mutableCopy操作可將其拷貝到堆上,retainCount為1.
__NSCFString
1234567891011
NSString*str1=[@"a"mutableCopy];TLog(str1);NSString*str2=[str1retain];TLog(str2);NSString*str3=[str1copy];TLog(str3);NSString*str4=[str1mutableCopy];TLog(str4);/*str1: __NSCFString -> 0x17d6d280 : a? 1str2: __NSCFString -> 0x17d6d280 : a? 2str3: __NSCFConstantString -> 0x3bd40090 : a? -1str4: __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è)試
12345678910111213141516171819202122232425262728
NSString*str1=[[@"a"mutableCopy]copy];TLog(str1);NSString*str2=[NSStringstringWithFormat:@"%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? -1str2: __NSCFConstantString -> 0x3bd40090 : a? -1str3: __NSCFConstantString -> 0x3bd40090 : a? -1str4: __NSCFString -> 0x175ab390 : b? 1str5: __NSCFString -> 0x176a5ce0 : c? 1str6: __NSCFString -> 0x175ab960 : d? 1str7: __NSCFString -> 0x176a5cc0 : e? 1str8: __NSCFString -> 0x176a5d50 : f? 1str9: __NSCFString -> 0x176a5d60 : \? 1str10: __NSCFString -> 0x176a6700 : $? 1str11: __NSCFString -> 0x175ab750 : .? 1str12: __NSCFString -> 0x175ab760 : aa? 1*/
起初我以為是ASCII字符比較特殊,經(jīng)過(guò)上面這一段的測(cè)試發(fā)現(xiàn),只有@“a”才有這樣的現(xiàn)象,我又用模擬器測(cè)試了這一段代碼,結(jié)果得到的都是_NSTaggedPointerString的對(duì)象。Tagged Pointer是一個(gè)能夠提升性能、節(jié)省內(nèi)存的有趣的技術(shù)。在OS X 10.10中,NSString就采用了這項(xiàng)技術(shù),具體可參考這篇博客:http://blog.csdn.net/demondev/article/details/50396815
因?yàn)楦割愔羔樋梢灾赶蜃宇悓?duì)象,使用 copy 的目的是為了讓本對(duì)象的屬性不受外界影響,使用 copy 無(wú)論給我傳入是一個(gè)可變對(duì)象還是不可對(duì)象,我本身持有的就是一個(gè)不可變的副本.
如果我們使用是 strong ,那么這個(gè)屬性就有可能指向一個(gè)可變對(duì)象,如果這個(gè)可變對(duì)象在外部被修改了,那么會(huì)影響該屬性.
copy 此特質(zhì)所表達(dá)的所屬關(guān)系與 strong 類似。然而設(shè)置方法并不保留新值,而是將其“拷貝” (copy)。當(dāng)屬性類型為 NSString 時(shí),經(jīng)常用此特質(zhì)來(lái)保護(hù)其封裝性。因?yàn)閭鬟f給設(shè)置方法的新值有可能指向一個(gè) NSMutableString 類的實(shí)例。這個(gè)類是 NSString 的子類,表示一種可修改其值的字符串。此時(shí)若是不拷貝字符串,那么設(shè)置完屬性之后,字符串的值就可能會(huì)在對(duì)象不知情的情況下遭人更改。所以,這時(shí)就要拷貝一份“不可變” (immutable)的字符串,確保對(duì)象中的字符串值不會(huì)無(wú)意間變動(dòng)。只要實(shí)現(xiàn)屬性所用的對(duì)象是“可變的” (mutable),就應(yīng)該在設(shè)置新屬性值時(shí)拷貝一份。

Copy-MutableCopy.png
拷貝
實(shí)現(xiàn)拷貝的方法有2個(gè)
copy:返回不可變副本
mutableCopy:返回可變副本
普通對(duì)象實(shí)現(xiàn)拷貝的步驟
遵守協(xié)議
實(shí)現(xiàn)-copyWithZone:方法
創(chuàng)建新對(duì)象
給新對(duì)象的屬性賦值