Swift 4 正式版已经随着 iOS 11 和 Xcode 9 的发布而发布了。
这是一个值得受好评的版本,很重要的一点是,它改变了 Swift 被人吐槽最多的一个问题:每个版本因为不兼容的问题,都要重写。Swift 4 有着 Swift 3 语法兼容模式,这意味着你不用改一行代码,就可以在 Xcode 编译通过。这对大型 iOS 项目而已非常重要:我们可以在未来的一年从一个 Target、一个 Framework 开始的形式慢慢地迁移至 Swift 4。
由于我比较急,一下子把自己的项目(奇点微博客户端)就迁移至 Swift 4,并已上架 App Store。在这个过程中发现了一些新东西。其中就有 Substring。
String 的主要变化
Swift 4 的 String 最大的一个变化是,它又是一个 Collection 了,可以拥有和 Array 等 Collection 一样的操作方式,比如:
let text = "Hello world"
for char in text {
print(char)
}
text.prefix(5) // "Hello"
text.suffix(5) // "world"
text.dropFirst() // "H"
text.dropLast() // "d"
上面所有针对 text 的方法都是在 Array(或其他 Collection)上的有。但是这里有一点要注册,这些方法返回的,可不是 String,而是 Substring。
Substring 是什么?
String 和 Substring 都遵守了 StringProtocol 这个东西,很多方法也都是一样的,那么为何要有这个设计呢?主要一个原因是:性能。
看了这张图,你就知道什么是 Substring 了:
当我们用一些 Collection 的方式得到 String 的 Slice(一部分)的时候,这时候创建的都是 Substring,Substring 与 String 共享一个 Storage,这意味我们在操作 String 的一部分的时候,是不需要频繁的去创建内存的,这使得 Swift 4 的 String 相关的操作可以获取比较高的性能。
只有当你显式地将 Substring 转成 String 的时候,才会 Copy 一份 String 到新的内存空间来,这时新的 String 和之前的 String 就没有关系了。
那么作为开发者,我们是不是就可以高枕无忧,只需要用,不需要操心什么是 Substring 什么是 String 呢?当然不是。
使用 Substring 的注意点
由于 Substring 是共享 String 的 Storage 的,这意味着,假如在你从一个非常大的 String 里得到一个 Substring,即使在之后你只用 Substring 了,但内存依然是占用着整个 String 的大小。直到 Substring 被释放以后,整个 String 才会被释放。
理解了这个原理后,我们就可以在具体的场景去优化性能了。只要记住两个原则:1、创建 Substring 的性能快,因为共享 String 的 Storage;2、如果有 Substring 存在,则 String 的 Storage 不会释放。
可以说,Swift 4 的这个设计,还是提供了不少灵活性的。
常规的使用场景
可能大家看到这,会觉得 Swift 4 引入了复杂度,用 String 就用 String 好了嘛,非要搞一个 Substring 出来。但事实上,在大多数项目里,我们不会遇到要用 String 还是用 Substring 这个「选择」问题。
因为 Swift 是强类型的语言,这意味着如果你的函数参数写着「name: String」,这是不接受 Substring 的,你一定要显式的转成 String,而转换成 String 的过程就意味着进行 Copy,也就不规避了共享 Storage 的问题,不会存在内存占用的问题。
只有当你特别需要去优化时,才需要好好去设计和思索一下要用 Substring 或是 String 来做操作。
一个实用的 Extension
这是我用在奇点里面的一个 Extension,里面就用到了把 Substring 显式地转成 String。
extension String { public func substring(from index: Int) -> String { if self.characters.count > index { let startIndex = self.index(self.startIndex, offsetBy: index) let subString = self[startIndex..<self.endIndex] return String(subString) } else { return self } } }
比如我是这样用的:
let text = "@图拉鼎"
let name = text.substring(from: 1)
print(name) // "图拉鼎"
简单地举这个例子,就是想说平常使用中,也可以完全无视 Substring 这个东西,只需要把常用的操作写成 extension 即可。
看看我以后在继续实践的过程中,会不会有用到更多需要考虑 String 或 Substring 的问题吧。
swift4.0的 ABI 还是没稳定吧
嗯,要到 Swift 5.0 才稳定。
欢迎分享到掘金 https://juejin.im 上~