字符串作为双向集合
有时你想要反转一个字符串。通常会倒着迭代。幸运的是,Swift有一种相当简单的方法,通过一种名为reversed()的方法:
let name = "Matt"
let backwardsName = name.reversed()
但backwardsName的类型是什么?如果你说string,那你就错了。它实际上是一个ReversedCollection<String>。这是swift做的一个相当巧妙的优化。它不是一个具体的字符串,而是一个反向的集合。这是一个关于任何集合的薄包装,你可以使用集合,就好像是它是另一种方法,但不会引起更多内存使用。
你可以像其他字符串一样访问backwardsName字符串中的字符,比如:
let secondCharIndex = backwardsName.index(backwardsName.startIndex,
offsetBy: 1)
let secondChar = backwardsName[secondCharIndex] // "t"
但是如果你想要一个字符串呢?你可以通过从反转的集合初始化一个字符串来实现,像这样:
let backwardsNameString = String(backwardsName)
从反向收集创建一个新字符串。但是,当你这样做时,你会使用新字符串自己的内存来存储复制原来的反转集合。因此,如果你不需要反转的字符串,那么保持在反转的集合域会节省内存空间。
子字符串
处理字符串时通常需要做的另一件事是生成子字符串。也就是说,提取到字符串的一部分到自己的值中。这可以通过使用索引的下标来快速完成。
例如,思考以下代码:
let fullName = "Matt Galloway"
let spaceIndex = fullName.index(of: " ")!
let firstName = fullName[fullName.startIndex..<spaceIndex] // "Matt"
这段代码找到了表示第一个空格的索引(在这里使用强制解包,因为你知道一定存在空格)。然后,它使用一个范围来找到起始索引和空格索引之间的grapheme集群(不包括空格)。
现在介绍一种你以前没见过的新类型的范围:开放式的范围。这种类型的范围只接受一个索引,并假设另一个索引是集合的开始或结束。
最后一行代码可以用一个开放式的范围重写如下:
let firstName = fullName[..<spaceIndex] // "Matt"
这一次我们省略了fullName.startIndex但是Swift会推断你的意思。
类似地,你也可以使用单边范围从某个索引开始,然后到集合的末尾,例如:
let lastName = fullName[fullName.index(after: spaceIndex)...]
//"Galloway"
有一些有趣的东西可以用子字符串指出。如果你查看它们的类型,那么你将看到它们是子字符串类型而不是字符串。就像使用反向字符串一样,你可以通过以下方式将这个子字符串强制转换为字符串:
let lastNameString = String(lastName)
这种特别的子字符串类型是一个巧妙的优化。子字符串与它的父字符串共享它被切片的存储。这意味着你在切割字符串的过程中,你不会使用额外的内存。然后,当你希望子字符串作为字符串时,你显式地创建一个新字符串,并将内存复制到这个新字符串的新缓冲区中。
Swift的设计者可能在默认情况下做出了这种复制行为。但是,通过使用子字符串类型,Swift使非常明确字符串发生了什么。好消息是字符串和子字符串共享几乎所有相同的功能。在返回或将子字符串传递给另一个需要字符串的函数之前,你可能还没有意识到要使用哪一种类型。在这种情况下,你可以直接从子字符串初始化一个新字符串。
现在很清楚的是,swift对字符串很有自己的见解,而且很仔细地考虑了字符串的实现方式。这很重要,因为字符串是复杂的,又经常被使用。因此,它们的API功能是非常重要的。
网友评论