- String 还是字符串,始终如一。
- Substring 是string的切片。它们与base string共享内存buffer,并拥有一对范围索引。
- StringProtocol 抽取出字符串的特征以及如何访问其功能,放进一个协议中。
String
及Substring
都遵循StringProtocol
。
字符串在不同的 Swift 版本中变化比较大。在 Swift 4 中,当需要从一个字符串中获取子串时,我们获取到的是 Substring 类型而不是一个 String 类型的值。为什么会这样?在 Swift 中,字符串是值类型。这意味着如果想从一个字符串中生成一个新的字符串,则需要做拷贝处理。这对于稳定性是有益的,但效率却不高。
从另一方面讲,一个 Substring 则是对其原始 String 的一个引用。它不需要做拷贝操作,所以更加高效。但是有另一个问题,假设我们要从一个很长的字符串中获取一个长度为 10 的 Substring,由于这个 Substring 会引用 String,那么只要 Substring 一直存在,String 就必须一直存在。所以,任何时候当处理完 Substring 后,需要将其转换为 String。
let myString = String(mySubstring)
这样只会拷贝子串,而原来的字符串可以被正确地回收。Substring 作为一种类型,本身即表示临时存在的。
在 Swift 4 另一个大的改进是字符串又成为集合了。这意味着在集合上的所有操作,也可以应用在字符串中(如下标、迭代字符、过滤等)。
Substring内部
substring的一部分魔法是重用了“parent” string的内存。你可以认为substring由base string和range构成。
这意味着从一个8000字符的string上切一个100字符的substring不需要分配额外内存,也不用复制100个字符。
这也意味着你可能无意延长了你的base string的生命周期。如果你有一个超大字符串存了一整本小说,然后从其中切片了一个单词,那么只要substring还在,这个巨大的string就会一直阴魂不散。
那么到底substring内部是如何跟踪这些的呢?
public struct Substring {
internal var _slice: RangeReplaceableBidirectionalSlice<String>
内部的 _slice
属性保存了关于base string的所有信息:
// 仍然是 Substring 的代码
internal var _wholeString: String {
return _slice._base
}
public var startIndex: Index { return _slice.startIndex }
public var endIndex: Index { return _slice.endIndex }
计算属性 _wholeString
(返回原来完整的字符串),以及 startIndex
和endIndex
(指出在string中切片的范围)只是简单的传递了内部切片属性的值。
你也能看到切片如何用 _base
来保存原始string的信息。
参考:
https://www.jianshu.com/p/90adf8ce7b9a
https://blog.csdn.net/weixin_34054931/article/details/87979393