在 Objective-C 的編程中,NSString 是使用率非常高的一個類,用法也很簡單。
NSString 表示一個固定字符串,關鍵點就在于固定二字。當你給一個 NSString 變量賦值之后,字符串本身是不能再改變的。
NSString * name = @"張三";
name = @"李四";
你可以發現,以上代碼執行并不會出錯,并不是說固定字符串這種說法是錯的。先分析一下,其實你把李四賦值給name,只是改變了name變量的指針,而不是張三本身。保存張三的內存還是靜靜地躺在那里,一動也不動。
繼續執行代碼
name = @"王五";
這樣其實內存里面有三塊內存,分別存儲 張三、李四、王五。
比如說張三內存是 0x001,李四內存 0x002,王五內存0x003,那么看下以下代碼的執行結果
NSString * name = @"張三"; // name 指向內存 0x001
name = @"李四";// name 指向內存 0x002
name = @"王五";// name 指向內存 0x003
name = @"張三";// name 重新指向內存 0x001
name = @"王五";// name 重新指向內存 0x003
一般來說,如果在源代碼里面出現的字符串會被編譯器保存在二進制文件的text段,在二進制加載到內存的時候就存在了,不會臨時分配所需內存。
不可變字符串有很多好處,最重要的一個就是多線程安全,因為不可變,所以任何線程都可以隨意使用它。在程序編寫中,如果沒有特殊需求,一般都是使用NSString保存字符串。
當然你會有特殊需求,比如你現在正在處理一批用戶輸入,代碼如下
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
將每個用戶輸入拼接成一個字符串,??你完成了這個需求,但是呢,這樣效率比較低。看下面的代碼
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,后面你每執行一次 stringByAppendingString
,地址就變了一次,結合我上面提到的不可變特性就很容易解釋了
NSString 本質就是不可變的,你調用了 stringByAppendingString
函數返回的是一個新的對象,可想而知,太多的stringByAppendingString調用必然會引起性能問題,因為內存分配是很消耗資源的,我們應該盡量避免。
解決方案當然就是可辨字符串類型 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
可變與不可變的區別在與內存管理方式上,可變類型會管理一塊內存用于存放數據(在這里是字符),比如上面我們分配了1024個字符,那么當前數據如果沒超過1024個字符限制的話,NSMutableString 直接往里填就可以了,如果超出,那么會像NSString一樣,重新開辟一段更大的內存,然后把原來的字符逐個拷貝到新的地址,但相對來說,內存分配的次數大大減少了,性能也就提高了。
當你需要頻繁變動字符串的時候,NSMutableString可以極大的提高性能,但是需要耗費更大的內存,造成浪費,所以在實際編程的時候還是需要取舍到底哪個類比較適合當前業務。
另外因為NSMutableString是NSString的子類,所以NSMutableString的實例可以給NSString的變量賦值,這樣會導致NSMutableString實例改變了內容,NSString的變量也改變的情況,這在某些情況下是錯的,這個時候應該使用 copy關鍵字來將 NSMutableString 的實例字符串拷貝一份到 NSString實例里面。