String和StaticString之间的区别

Dán*_*agy 20 string swift

我正在浏览文档,我找到了StaticString.它指出:

一个简单的字符串,用于表示"在编译时可以识别"的文本.

我原本以为它String具有与NSString编译时相同的行为,但看起来我错了.所以我的问题是我们何时应该使用StaticString而不是a String,唯一的区别是StaticString编译时是否已知?

我找到的一件事是

var a: String = "asdf" //"asdf"
var b: StaticString = "adsf" //{(Opaque Value), (Opaque Value), (Opaque Value)}

sizeofValue(a)  //24
sizeofValue(b)  //17
Run Code Online (Sandbox Code Playgroud)

所以看起来StaticString内存占用少了一点.

vac*_*ama 7

似乎StaticString可以容纳字符串文字。您不能为其分配类型变量,String也不能对其进行突变(+=例如,使用)。

“在编译时知道并不意味着变量的值将在编译时确定,而分配给它的任何值在编译时都是已知的。

考虑一下这个有效的例子:

var str: StaticString

for _ in 1...10 {
    switch arc4random_uniform(3) {
    case 0: str = "zero"
    case 1: str = "one"
    case 2: str = "two"
    default: str = "default"
    }
    print(str)
}
Run Code Online (Sandbox Code Playgroud)

每当您可以向Swift提供有关如何使用变量的更多信息时,它都可以使用该变量优化代码。通过将变量限制为StaticString,Swift知道该变量不会被突变,因此它可能能够更有效地存储它,或者更有效地访问各个字符。

实际上,StaticString可以只用一个地址指针和一个长度来实现。它指向的地址只是静态代码中定义字符串的位置。A StaticString不需要被引用计数,因为它(不)存在于堆中。它既没有分配也没有释放,因此不需要引用计数。

“在编译时知道”是非常严格的。即使这样也不起作用:

let str: StaticString = "hello " + "world"
Run Code Online (Sandbox Code Playgroud)

失败并出现错误:

错误:“字符串”不可转换为“ StaticString”


Kam*_*xom 5

StaticString在编译时是可知的。这可以导致优化。例子:

编辑:这部分不起作用,请参阅下面的编辑

假设您有一个函数,可以IntString您在编译时定义的一些常量计算一些值。

let someString = "Test"
let otherString = "Hello there"

func numberForString(string: String) -> Int {
    return string.stringValue.unicodeScalars.reduce(0) { $0 * 1 << 8 + Int($1.value) }
}

let some = numberForString(someString)
let other = numberForString(otherString)
Run Code Online (Sandbox Code Playgroud)

像这样,当在程序中真正调用该函数时,例如应用程序启动时,该函数将使用“Test”和“Hello there”来执行。肯定是在运行时。但是,如果您更改您的功能以采取StaticString

func numberForString(string: StaticString) -> Int {
    return string.stringValue.unicodeScalars.reduce(0) { $0 * 1 << 8 + Int($1.value) }
}
Run Code Online (Sandbox Code Playgroud)

编译器知道传入的在StaticString编译时是可知的,所以猜猜它是做什么的?它在编译时运行该函数(这真是太棒了!)。我曾经读过一篇关于这个的文章,作者检查了生成的程序集,他实际上找到了已经计算过的数字。

正如您所看到的,这在某些情况下很有用,就像提到的那样,不会降低可以在编译时完成的工作的运行时性能。

编辑:Dániel Nagy和我进行了交谈。矿的上述例子中不起作用,因为该函数stringValueStaticString无法在编译时是已知的(因为它返回一个String)。这是一个更好的例子:

func countStatic(string: StaticString) -> Int {
    return string.byteSize    // Breakpoint here
}

func count(string: String) -> Int {
    return string.characters.count    // Breakpoint here
}

let staticString : StaticString = "static string"
let string : String = "string"


print(countStatic(staticString))
print(count(string))
Run Code Online (Sandbox Code Playgroud)

在发布版本中,仅触发第二个断点,而如果您将第一个函数更改为

func countStatic(string: StaticString) -> Int {
    return string.stringValue.characters.count    // Breakpoint here
}
Run Code Online (Sandbox Code Playgroud)

两个断点都被触发。

显然有一些方法可以在编译时完成,而其他方法则不能。我想知道编译器实际上是如何计算出来的。