既然像 String
這樣的 Swift 的類型和 Foundation 的對應的類是可以無縫轉換的,那麼我們在使用和選擇的時候,有沒有什麼需要特別注意的呢?
簡單來說,沒有特別需要注意的,但是儘可能的話還是使用原生的 String
類型。
原因有三。
首先雖然 String
和 NSString
有着良好的互相轉換的特性,但是現在 Cocoa 所有的 API 都接受和返回 String
類型。我們沒有必要也不必給自己憑空添加麻煩去把框架中返回的字符串做一遍轉換,既然
Cocoa 鼓勵使用 String
,並且爲我們提供了足夠的操作 String
的方法,那爲什麼不直接使用呢?
其次,因爲在 Swift 中 String
是 struct,相比起 NSObject
的 NSString
類來說,更切合字符串的
"不變" 這一特性。通過配合常量賦值 (let) ,這種不變性在多線程編程時就非常重要了,它從原理上將程序員從內存訪問和操作順序的擔憂中解放出來。另外,在不觸及 NSString
特有操作和動態特性的時候,使用 String
的方法,在性能上也會有所提升。
最後,因爲 String
實現了像 CollectionType
這樣的接口,因此有些 Swift 的語法特性只有 String
才能使用,而 NSString
是沒有的。一個典型就是 for...in
的枚舉,我們可以寫:
let levels = "ABCDE"
for i in levels {
print(i)
}
// 輸出:
// ABCDE
而如果轉換爲 NSString
的話,是無法編譯的。
不過也有例外的情況。有一些 NSString
的方法在 String
中並沒有實現,一個很有用的就是在 iOS 8 中新加的 containsString
。我們想使用這個
API 來簡單地確定某個字符串包括一個子字符串時,只能先將其轉爲 NSString
:
if (levels as NSString).containsString("BC") {
println("包含字符串")
}
// 輸出:
// 包含字符串
A> Swift 的 String
沒有 containsString
是一件很奇怪的事情,理論上應該不存在實現的難度,希望只是 Apple 一時忘了這個新加的 API 吧。當然你也可以自行用擴展的方式在自己的代碼庫爲 String
添加這個方法。當然,還有一些其他的像 length
和 characterAtIndex:
這樣的
API 也沒有 String
的版本,這主要是因爲 String
和 NSString
在處理編碼上的差異導致的。
使用 String
唯一一個比較麻煩的地方在於它和 Range
的配合。在 NSString
中,我們在匹配字符串的時候通常使用 NSRange
來表徵結果或者作爲輸入。而在使用 String
的對應的
API 時,NSRange
也會被映射成它在 Swift 中且對應 String
的特殊版本:Range<String.Index>
。這有時候會讓人非常討厭:
let levels = "ABCDE"
let nsRange = NSMakeRange(1, 4)
// 編譯錯誤
// 'NSRange' is not convertible to 'Range<String.Index>'
levels.stringByReplacingCharactersInRange(nsRange, withString: "AAAA")
let indexPositionOne = levels.startIndex.successor()
let swiftRange = indexPositionOne..<advance(indexPositionOne, 4)
levels.stringByReplacingCharactersInRange(swiftRange, withString: "AAAA")
// 輸出:
// AAAAA
一般來說,我們可能更願意和基於 Int
的 NSRange
一起工作,而不喜歡使用麻煩的 Range<String.Index>
。這種情況下,將 String
轉爲 NSString
也許是個不錯的選擇:
let nsRange = NSMakeRange(1, 4)
(levels as NSString).stringByReplacingCharactersInRange(
nsRange, withString: "AAAA")