【发布时间】:2016-04-04 06:07:31
【问题描述】:
我来自 C# 背景,其中System.String 是不可变的,字符串连接相对昂贵(因为它需要重新分配字符串)我们知道使用StringBuilder 类型,因为它预先分配了一个更大的缓冲区,其中单个字符(@ 987654325@,一种 16 位值类型)和短字符串可以很便宜地连接起来,无需额外分配。
我正在将一些 C# 代码移植到 Swift,它从字符长度小于 8 位的子八位字节索引处读取位数组 ([Bool])(这是一种非常注重空间的文件格式)。
我的 C# 代码是这样的:
StringBuilder sb = new StringBuilder( expectedCharacterCount );
int idxInBits = 0;
Boolean[] bits = ...;
for(int i = 0; i < someLength; i++) {
Char c = ReadNextCharacter( ref idxInBits, 6 ); // each character is 6 bits in this example
sb.Append( c );
}
在 Swift 中,我假设 NSMutableString 相当于 .NET 的 StringBuilder,并且我发现了这个关于附加单个字符 (How to append a character to string in Swift?) 的 QA,所以在 Swift 中我有这个:
var buffer: NSMutableString
for i in 0..<charCount {
let charValue: Character = readNextCharacter( ... )
buffer.AppendWithFormat("%c", charValue)
}
return String(buffer)
但我不知道为什么它首先通过格式字符串,这似乎效率低下(在每次迭代中重新解析格式字符串)并且由于我的代码在 iOS 设备上运行,我希望对我的程序非常保守CPU 和内存使用情况。
在我写这篇文章的时候,我了解到我的代码真的应该使用UnicodeScalar 而不是Character,问题是NSMutableString 不允许你附加UnicodeScalar 值,你必须使用Swift 自己的可变@ 987654335@ 类型,所以现在我的代码如下所示:
var buffer: String
for i in 0..<charCount {
let x: UnicodeScalar = readNextCharacter( ... )
buffer.append(x)
}
return buffer
我认为String 是不可变的,但我注意到它的append 方法返回Void。
这样做我仍然感到不舒服,因为我不知道 Swift 的 String 类型是如何在内部实现的,而且我不知道如何预先分配一个大缓冲区以避免重新分配(假设 Swift 的 String 使用增长算法)。
【问题讨论】:
-
在 Swift 中,var 表示 variable,let 表示 constant。在您的情况下, var String 将是可变的,而 let String 将是不可变的。字符也可以附加到可变字符串。对于预分配,您可以使用
[Character](count: 100, repeatedValue: "0")创建一定长度的Characters 数组。 (并使用String(charArray)将其转换回字符串)。我会说没有这个必要。在 Swift 中追加非常快。 -
对于它的价值,GitHub 上有一个 Swift StringBuilder 要点:gist.github.com/kristopherjohnson/1fc55e811d944a430289 看起来它旨在实现 C# StringBuilder 类的子集,并且在手动将 C# 程序转换为 Swift 时可能很有用. (至少,如果你不担心让 Swift 纯粹主义者不高兴,他们更愿意重写代码以“以 Swift 方式”完成。)但不幸的是,它是为 Swift 3 之前的 Swift 版本编写的,需要大约 10小改动被接受为有效的 Swift 3。
-
@J.Wang 这是否意味着“不可变可变”
String与let x: String语句一起使用?可变字符串与不可变字符串的内部表示可能非常不同,因为它们针对不同的场景(例如不可变子字符串)进行了优化。