Swift"可变"字符串真的可变,还是像Java字符串一样?

Rob*_*b N 18 string immutability swift

在Swift编程语言中,在字符串部分String Mutability一节中,它说:

您String可以通过将特定分配给变量(在这种情况下可以修改)或常量(在这种情况下不能修改)来指示特定是否可以修改(或变异):

并给出示例代码:

var variableString = "Horse"
variableString += " and carriage"
// variableString is now "Horse and carriage"

let constantString = "Highlander"
constantString += " and another Highlander"
// this reports a compile-time error - a constant string cannot be modified”
Run Code Online (Sandbox Code Playgroud)

这本书中的iBooks 这里,或在Web浏览器在这里.

在下一段中,它声称"字符串是值类型".

我的问题:对我来说,这看起来不像是一个可变的字符串.它看起来像我在Java(或C#,Python和其他)中习惯的:具有可变变量绑定的不可变字符串对象.换句话说,有一个对象"Horse",然后它创建了一个新的String对象"Horse and carriage"并将其设置为相同的变量.而且由于无法区分对不可变对象的引用与值类型(对吗?)之间的区别,我想知道:为什么他们这样描述它?这些Swift字符串与它在Java中的方式有​​什么区别吗?(或C#,Python,Objective-C/NSString)

new*_*cct 15

在某种程度上,"可变"和"不可变"只在谈论引用类型时才有意义.如果您尝试将其扩展为值类型,则可以将所有值类型视为在功能上等同于"不可变"引用类型.

例如,考虑一种var类型Int.这可变吗?有些人可能会说,当然 - 您可以通过赋予它来改变其可见的"值" =.然而,同样可以的可以说var的NSNumber和NSString-您可以通过分配给它改变它的可见价值.但NSNumber和NSString被描述为不可变的类.

参考类型实际发生的是分配它们会导致变量(指针)指向新对象.旧的或新的对象本身都没有"改变",但由于它指向不同的对象,你"看到"一个新的值.

当我们说一个类是"可变的"时,我们的意思是它提供了一个API(方法或引用)来实际更改对象的内容.但我们怎么知道对象已经改变了?(而不是它是一个新对象?)这是因为我们可以对同一个对象有另一个引用,并且通过另一个引用可以看到通过一个引用对对象的更改.但是这些属性(指向具有指向同一对象的多个指针的不同对象)本身仅适用于引用类型.根据定义,值类型不能具有这样的"共享"(除非"值"的一部分是引用类型,例如Array),因此,对于值类型,"可变性"的后果不会发生.

因此,如果你创建一个包装整数的不可变类,它在操作上等同于Int- 在这两种情况下,更改变量值的唯一方法是assign(=).所以Int也应该同样被认为是"不可改变的".

Swift中的值类型稍微复杂一些,因为它们可以有方法,其中一些可以mutating.因此,如果您可以mutating在值类型上调用方法,它是否可变?但是,如果我们考虑mutating将值类型的方法调用为语法糖来为其分配一个全新的值(无论方法是否会将其变异),我们都可以克服这个问题.


Jer*_*101 5

在 Swift 中,结构体和枚举都是值类型:

\n\n
\n

事实上,Swift\xe2\x80\x94 中的所有基本类型整数、浮点数、布尔值、字符串、数组和字典\xe2\x80\x94 都是值类型,并在幕后实现为结构体。

\n
\n\n

因此,字符串是一种在赋值时复制的值类型,并且不能有多个引用,但其底层字符数据存储在可共享的写入时复制缓冲区中。该结构的 API 参考String说:

\n\n
\n

尽管 Swift 中的字符串具有值语义,但字符串使用写入时复制策略将其数据存储在缓冲区中。然后,该缓冲区可以由字符串的不同副本共享。当多个字符串实例使用同一缓冲区时,字符串\xe2\x80\x99s 数据仅在突变时才被延迟复制。因此,任何变异操作序列中的第一个操作可能会花费 O(n) 时间和空间。

\n
\n\n

因此,事实上,varvs.let声明了一个可变与不可变的绑定到看起来不可变的字符缓冲区。

\n\n
var v1 = "Hi"      // mutable\nvar v2 = v1        // assign/pass by value, i.e. copies the String struct\nv1.append("!")     // mutate v1; does not mutate v2\n[v1, v2]           // ["Hi!", "Hi"]\n\nlet c1 = v1        // immutable\nvar v3 = c1        // a mutable copy\n// c1.append("!")  // compile error: "Cannot use mutating member on immutable value: \'c1\' is a \'let\' constant"\nv3 += "gh"         // mutates v3, allocating a new character buffer if needed\nv3.append("?")     // mutates v3, allocating a new character buffer if needed\n[c1, v3]           // ["Hi", "High?"]\n
Run Code Online (Sandbox Code Playgroud)\n\n

这就像非最终变量与finalJava 字符串变量有两个问题。

\n\n
    \n
  1. 使用可变绑定,您可以调用变异方法。由于值类型不能有任何别名,因此除了性能影响之外,您无法判断变异方法是否实际上修改了字符缓冲区或重新分配给 String 变量。
  2. \n
  3. 当字符缓冲区仅由一个 String 实例使用时,该实现可以通过就地改变字符缓冲区来优化改变操作。
  4. \n
\n