8.3 集合的复制
集合在赋值或参数传递过程中会发生复制。Swift中的集合都是结构体类型,即值类型。值类型在赋值或参数传递时会发生复制行为,赋予的值或传递的参数是一个副本;而引用类型在赋值或参数传递时不发生复制行为,赋予的值或传递的参数是一个引用(实例本身)。
同样是值类型,集合要比其他值类型(整型、浮点型等)的复制行为更复杂,这是因为集合里面包含了一些数据。这些数据是否发生复制行为呢?这要看这些数据本身是值类型还是引用类型,如果是值类型则发生复制行为,如果是引用类型则不发生复制行为。
下面我们分别讨论字典和数组的复制。
8.3.1 字典复制
在为字典赋值或参数传递的时候,字典总是发生复制行为。而字典中的键和值等数据是否发生复制,要看本身键和值本身的类型。
我们先看一个示例:
var students = [102 : "张三",105 : "李四"] ①var copyStudents = students ②copyStudents[102] = "王五" ③println(students[102]) ④
上述代码第①行是创建并初始化字典变量students,然后在第②行将字典变量students赋值给copyStudents变量,这个过程中发生了复制行为,students中的内容被复制到copyStudents。由于字典中存放的键和值都是值类型,也发生了复制行为。所以当在第③行修改copyStudents[102]内容的时候,不会影响students[102]内容,在第④行打印的时候还是"张三"。图8-5是这个示例的复制示意图。

图 8-5 字典值类型复制示意图
下面我们再看一个示例:
class Employee { ①var name : String // 姓名var salary : Double // 工资init (n : String) {name = nsalary = 0}}var emps = Dictionary<String, Employee>() ②let emp1 = Employee(n: "Amy Lee") ③let emp2 = Employee(n: "Harry Hacker") ④emps["144-25-5464"] = emp1 ⑤emps["567-24-2546"] = emp2 ⑥//赋值,发生复制var copyEmps = emps ⑦let copyEmp : Employee! = copyEmps["567-24-2546"]copyEmp.name = "Gary Cooper" ⑧let emp : Employee! = emps["567-24-2546"]println(emp.name) ⑨
上述代码第①行是定义一个Employee类,关于类的定义我们会在11.4节详细介绍,在本例中不用关心Employee类的细节问题。
第②行代码声明字典变量,它的键要求是String类型,值要求是Employee类型。第③行和第④行是实例化Employee对象。第⑤行和第⑥行分别将两个对象赋值给字典。
第⑦行代码是将字典变量emps赋值给copyEmps变量,这个过程中发生了复制行为,但是由于emp1和emp2对象是引用类型,不发生复制,因此在第⑧行代码修改copyEmp内容之后,在第⑨行打印的时候输出"Gary Cooper"。
这个示例说明,emps字典中的emp2与copyEmps字典中的copyEmp具有相同的引用。在字典发生复制行为的过程中,引用类型的内容并没有发生复制。图8-6是这个示例的示意图。

图 8-6 字典引用类型内容示意图
8.3.2 数组复制
Xcde 6 beta2中的数组复制的语法非常复杂,beta3之后变得简单了,与字典的复制类似。在为数组赋值或参数传递的时候,数组总是发生复制行为。而数组中的数据是否也发生复制行为,要看数据本身的类型。
下面我们再看一个示例:
var a = ["张三", "李四", "王五"] ①var b = a ②var c = a ③a[0] = "董六" ④println(a[0]) ⑤println(b[0]) ⑥println(c[0]) ⑦
上述代码第①行是声明并初始化数组a,在代码第②行和第③行将a赋值给b和c。这时它们发生了复制行为,然后当我们在第④行修改a的第一个元素后,第⑤行输出的是"董六",而第⑥行和第⑦行输出的是"张三"。
图8-7是这个示例的复制示意图。

图 8-7 数组值类型复制示意图
下面我们再看一个示例:
class Employee {var name : String // 姓名var salary : Double // 工资init (n : String) {name = nsalary = 0}}var emps = Array<Employee>() ①let emp1 = Employee(n: "Amy Lee") ②let emp2 = Employee(n: "Harry Hacker") ③emps.append(emp1) ④emps.append(emp2) ⑤//赋值,发生复制var copyEmps = emps ⑥let copyEmp : Employee! = copyEmps[0]copyEmp.name = "Gary Cooper" ⑦let emp : Employee! = emps[0]println(emp.name) ⑧
第①行代码声明数组变量,它要求内容数据是Employee类型。第②行和第③行是实例化Employee对象。第④行和第⑤分别将两个对象添加到数组中。
第⑥行代码是将数组变量emps赋值给copyEmps变量,这个过程中发生了复制行为,但是由于emp1和emp2对象是引用类型,不发生复制,因此在第⑦行代码修改copyEmp内容之后,在第⑧行打印的时候输出"Gary Cooper"。
这个示例说明,emps数组中的emp2与copyEmps数组中的copyEmp具有相同的引用。在数组发生复制行为的过程中,引用类型的内容并没有发生复制。图8-8是这个示例的示意图。

图 8-8 数组引用类型内容示意图
