既然2017年全球开发者大会已经结束(如果你问我的话,我认为这是自2014年以来最好的一次),Xcode 9测试版也已经发布,许多Swift开发者开始着手使用Swift 4。今年新版本的真正优点在于,它更多的是对语言的改进,而不是完全的改进(就像Swift 2和3那样), 这将使大多数代码库的升级变得更加容易。
其中一个改进是String API,它在Swift 4中变得更容易使用(同时也增强了功能)。在Swift的过去版本中,字符串API经常被拿来作为一个例子,说明Swift有时是如何在正确性而不是易用性上走得太远,处理字符和子字符串的方式很麻烦。
本周,让我们看看如何在Swift 4中处理字符串,以及如何在各种情况下利用新的、改进的API
Multi-line string literals
有时,我们的应用程序或脚本中有更长的静态字符串,它们跨越了多行。在Swift 4之前,我们必须对字符串做一些类似inline \n的操作,通过字符串的扩展添加appendOnNewLine()方法-在脚本的情况下-多次调用print()来为一个长输出添加换行符。
func printHelp() {
print("🚘 Test Drive")
print("--------------")
print("Quickly try out any Swift pod or framework in a playground.")
print("\nUsage:")
print("- Simply pass a list of pod names or URLs that you want to test drive.")
print("- You can also specify a platform (iOS, macOS or tvOS) using the '-p' option")
print("- To use a specific version or branch, use the '-v' argument (or '-m' for master)")
print("\nExamples:")
print("- testdrive Unbox Wrap Files")
print("- testdrive https://github.com/johnsundell/unbox.git Wrap Files")
print("- testdrive Unbox -p tvOS")
print("- testdrive Unbox -v 2.3.0")
print("- testdrive Unbox -v swift3")
}
下面是如何在Swift 4中使用多行字符串字面值来表示它(请原谅奇怪的语法高亮显示):
func printHelp() {
print(
"""
🚘 Test Drive
--------------
Quickly try out any Swift pod or framework in a playground.
Usage:
- Simply pass a list of pod names or URLs that you want to test drive.
- You can also specify a platform (iOS, macOS or tvOS) using the '-p' option
- To use a specific version or branch, use the '-v' argument (or '-m' for master)
Examples:
- testdrive Unbox Wrap Files
- testdrive https://github.com/johnsundell/unbox.git Wrap Files
- testdrive Unbox -p tvOS
- testdrive Unbox -v 2.3.0
- testdrive Unbox -v swift3
"""
)
}
正如您在上面看到的,当使用多行文字时,代码变得更有表现力和清晰。我们不再需要添加多个\n来添加换行符,相反,我们只需在字符串中添加实际的换行符,这样,甚至在运行脚本之前,就可以非常容易地看到输出的确切样子。
在缩进方面,多行文字使用底部的"""来确定字符串的基本缩进。 因此,所有与引号对齐的东西在字符串中都不会有额外的缩进。
Strings are collections (again!)
在Swift 1中,String符合当时所谓的CollectionType (Swift 3+中的Collection),这意味着您可以对它们执行所有类型的集合操作(如forEach()、filter()等)。 你也可以在Swift 2和3中这样做,通过访问字符属性,但这很快导致更难阅读代码。
在Swift 4中,字符串仍然是集合,这意味着你可以简单地将它们视为“字符的集合”。这可能非常有用,例如,如果你想从字符串中过滤某些字符(让我们以感叹号为例):
let filtered = string.filter { $0 != "!" }
The new Substring type
Swift 4引入了一种处理子字符串的新方法,使用不同的子字符串类型。现在它已经从大多数返回子字符串的方法中返回(比如split()),并且引入了一个新的下标API,可以让你快速访问子字符串:
// Access a substring from a given index until the end
let substring = string[index...]
让我们看一个示例,通过截断用户输入的文本,将其限制为特定长度,从而返回子字符串。在Swift 3中,你可以这样写:
extension String {
func truncated() -> String {
return String(characters.prefix(truncationLimit))
}
}
好消息是,由于Swift 4的大部分源代码与Swift 3兼容,完全相同的代码在Swift 4以及工作。但更好的消息是,由于Swift 4中的字符串是集合,我们可以简化上述操作,直接处理字符串:
extension String {
func truncated() -> Substring {
return prefix(truncationLimit)
}
}
上面的代码使用了prefix() API,该API返回多达n个元素的子序列,同时还执行边界检查(以便在我们的集合中不会遇到错误-在这种情况下,我们的字符串-包含小于n个元素)。
从上面可以看到,truncated()方法的返回类型现在是新的子字符串类型。字符串可以是不同的类型,乍一看可能有点麻烦, 它在记忆的可预测性方面给了我们很大的优势。
为了避免创建许多冗余副本,Swift字符串使用“写时复制”方法,只在需要时执行副本。 这意味着子字符串经常与它们的父字符串共享内存中相同的底层缓冲区。 但是在truncated()的情况下,我们不希望仅仅为了能够使用截断的子字符串而将整个未截断的字符串保留在内存中。
通过给我们一个子字符串类型,而不是完整字符串,Swift现在“强制”我们在需要时显式地执行复制,这样就可以释放父字符串的内存。我们可以简单地从截断的子字符串中创建一个新的字符串,如下所示:
label.text = String(userInput.truncated())
Conclusion
由于几乎所有的Swift应用程序和脚本都处理字符串,所以很高兴看到这些API的改进。我认为新的API在正确性和易用性之间取得了很好的平衡, 同时也要求程序员对复制做出深思熟虑的选择——就像Substring的情况一样。
还有更多我在这篇文章中没有提到的字符串API的改进-如Unicode 9支持更简单的字符管理和轻松访问底层Unicode代码点,组成字符的能力。
我们将在接下来的文章中深入研究字符串处理和编码,以及其他新的Swift 4 api和在WWDC上宣布的新框架和工具。所以请继续关注!😉