在 Objective-C 的編程中,NSString 是使用率非常高的一個(gè)類,用法也很簡(jiǎn)單。
NSString 表示一個(gè)固定字符串,關(guān)鍵點(diǎn)就在于固定二字。當(dāng)你給一個(gè) NSString 變量賦值之后,字符串本身是不能再改變的。
NSString * name = @"張三";
name = @"李四";
你可以發(fā)現(xiàn),以上代碼執(zhí)行并不會(huì)出錯(cuò),并不是說(shuō)固定字符串這種說(shuō)法是錯(cuò)的。先分析一下,其實(shí)你把李四賦值給name,只是改變了name變量的指針,而不是張三本身。保存張三的內(nèi)存還是靜靜地躺在那里,一動(dòng)也不動(dòng)。
繼續(xù)執(zhí)行代碼
name = @"王五";
這樣其實(shí)內(nèi)存里面有三塊內(nèi)存,分別存儲(chǔ) 張三、李四、王五。
比如說(shuō)張三內(nèi)存是 0x001,李四內(nèi)存 0x002,王五內(nèi)存0x003,那么看下以下代碼的執(zhí)行結(jié)果
NSString * name = @"張三"; // name 指向內(nèi)存 0x001
name = @"李四";// name 指向內(nèi)存 0x002
name = @"王五";// name 指向內(nèi)存 0x003
name = @"張三";// name 重新指向內(nèi)存 0x001
name = @"王五";// name 重新指向內(nèi)存 0x003
一般來(lái)說(shuō),如果在源代碼里面出現(xiàn)的字符串會(huì)被編譯器保存在二進(jìn)制文件的text段,在二進(jìn)制加載到內(nèi)存的時(shí)候就存在了,不會(huì)臨時(shí)分配所需內(nèi)存。
不可變字符串有很多好處,最重要的一個(gè)就是多線程安全,因?yàn)椴豢勺?,所以任何線程都可以隨意使用它。在程序編寫中,如果沒(méi)有特殊需求,一般都是使用NSString保存字符串。
當(dāng)然你會(huì)有特殊需求,比如你現(xiàn)在正在處理一批用戶輸入,代碼如下
NSString * input = @"";
input = [input stringByAppendingString:@"input text1\t"];
input = [input stringByAppendingString:@"input text2\t"];
input = [input stringByAppendingString:@"input text3\t"];
NSLog(@"input text is \t %@", input);
你使用 stringByAppendingString將每個(gè)用戶輸入拼接成一個(gè)字符串,??你完成了這個(gè)需求,但是呢,這樣效率比較低。看下面的代碼
NSString * input = @"";
NSLog(@"input address %p", input); // input address 0x102015050
input = [input stringByAppendingString:@"input text1\t"];
NSLog(@"input address %p", input);// input address 0x102015090
input = [input stringByAppendingString:@"input text2\t"];
NSLog(@"input address %p", input);//input address 0x7fedea003d50
input = [input stringByAppendingString:@"input text3\t"];
NSLog(@"input address %p", input);//input address 0x7fedea003ed0
NSLog(@"input text is \t %@", input);
觀察下 input的地址變化,剛始是 0x102015050,后面你每執(zhí)行一次 stringByAppendingString,地址就變了一次,結(jié)合我上面提到的不可變特性就很容易解釋了
NSString 本質(zhì)就是不可變的,你調(diào)用了 stringByAppendingString函數(shù)返回的是一個(gè)新的對(duì)象,可想而知,太多的stringByAppendingString調(diào)用必然會(huì)引起性能問(wèn)題,因?yàn)閮?nèi)存分配是很消耗資源的,我們應(yīng)該盡量避免。
解決方案當(dāng)然就是可辨字符串類型 NSMutableString,如下代碼
NSMutableString * input = [NSMutableString stringWithCapacity:1024];
NSLog(@"input address %p", input);
[input appendString:@"input text1\t"];
NSLog(@"input address %p", input);
[input appendString:@"input text2\t"];
NSLog(@"input address %p", input);
[input appendString:@"input text3\t"];
NSLog(@"input address %p", input);
NSLog(@"input text is \t %@", input);
輸出信息:
2016-09-29 17:55:21.604 Untitled[71051:1785450] input address 0x7ff6064035f0
2016-09-29 17:55:21.604 Untitled[71051:1785450] input address 0x7ff6064035f0
2016-09-29 17:55:21.604 Untitled[71051:1785450] input address 0x7ff6064035f0
2016-09-29 17:55:21.604 Untitled[71051:1785450] input address 0x7ff6064035f0
2016-09-29 17:55:21.605 Untitled[71051:1785450] input text is input text1 input text2 input text3
可變與不可變的區(qū)別在與內(nèi)存管理方式上,可變類型會(huì)管理一塊內(nèi)存用于存放數(shù)據(jù)(在這里是字符),比如上面我們分配了1024個(gè)字符,那么當(dāng)前數(shù)據(jù)如果沒(méi)超過(guò)1024個(gè)字符限制的話,NSMutableString 直接往里填就可以了,如果超出,那么會(huì)像NSString一樣,重新開辟一段更大的內(nèi)存,然后把原來(lái)的字符逐個(gè)拷貝到新的地址,但相對(duì)來(lái)說(shuō),內(nèi)存分配的次數(shù)大大減少了,性能也就提高了。
當(dāng)你需要頻繁變動(dòng)字符串的時(shí)候,NSMutableString可以極大的提高性能,但是需要耗費(fèi)更大的內(nèi)存,造成浪費(fèi),所以在實(shí)際編程的時(shí)候還是需要取舍到底哪個(gè)類比較適合當(dāng)前業(yè)務(wù)。
另外因?yàn)镹SMutableString是NSString的子類,所以NSMutableString的實(shí)例可以給NSString的變量賦值,這樣會(huì)導(dǎo)致NSMutableString實(shí)例改變了內(nèi)容,NSString的變量也改變的情況,這在某些情況下是錯(cuò)的,這個(gè)時(shí)候應(yīng)該使用 copy關(guān)鍵字來(lái)將 NSMutableString 的實(shí)例字符串拷貝一份到 NSString實(shí)例里面。