8.3 集合的复制

集合在赋值或参数传递过程中会发生复制。Swift中的集合都是结构体类型,即值类型。值类型在赋值或参数传递时会发生复制行为,赋予的值或传递的参数是一个副本;而引用类型在赋值或参数传递时不发生复制行为,赋予的值或传递的参数是一个引用(实例本身)。

同样是值类型,集合要比其他值类型(整型、浮点型等)的复制行为更复杂,这是因为集合里面包含了一些数据。这些数据是否发生复制行为呢?这要看这些数据本身是值类型还是引用类型,如果是值类型则发生复制行为,如果是引用类型则不发生复制行为。

下面我们分别讨论字典和数组的复制。

8.3.1 字典复制

在为字典赋值或参数传递的时候,字典总是发生复制行为。而字典中的键和值等数据是否发生复制,要看本身键和值本身的类型。

我们先看一个示例:

  1. var students = [102 : "张三",105 : "李四"]
  2. var copyStudents = students
  3. copyStudents[102] = "王五"
  4. println(students[102])

上述代码第①行是创建并初始化字典变量students,然后在第②行将字典变量students赋值给copyStudents变量,这个过程中发生了复制行为,students中的内容被复制到copyStudents。由于字典中存放的键和值都是值类型,也发生了复制行为。所以当在第③行修改copyStudents[102]内容的时候,不会影响students[102]内容,在第④行打印的时候还是"张三"。图8-5是这个示例的复制示意图。

8.3 集合的复制 - 图1

图 8-5 字典值类型复制示意图

下面我们再看一个示例:

  1. class Employee {
  2. var name : String // 姓名
  3. var salary : Double // 工资
  4. init (n : String) {
  5. name = n
  6. salary = 0
  7. }
  8. }
  9. var emps = Dictionary<String, Employee>()
  10. let emp1 = Employee(n: "Amy Lee")
  11. let emp2 = Employee(n: "Harry Hacker")
  12. emps["144-25-5464"] = emp1
  13. emps["567-24-2546"] = emp2
  14. //赋值,发生复制
  15. var copyEmps = emps
  16. let copyEmp : Employee! = copyEmps["567-24-2546"]
  17. copyEmp.name = "Gary Cooper"
  18. let emp : Employee! = emps["567-24-2546"]
  19. println(emp.name)

上述代码第①行是定义一个Employee类,关于类的定义我们会在11.4节详细介绍,在本例中不用关心Employee类的细节问题。

第②行代码声明字典变量,它的键要求是String类型,值要求是Employee类型。第③行和第④行是实例化Employee对象。第⑤行和第⑥行分别将两个对象赋值给字典。

第⑦行代码是将字典变量emps赋值给copyEmps变量,这个过程中发生了复制行为,但是由于emp1emp2对象是引用类型,不发生复制,因此在第⑧行代码修改copyEmp内容之后,在第⑨行打印的时候输出"Gary Cooper"

这个示例说明,emps字典中的emp2copyEmps字典中的copyEmp具有相同的引用。在字典发生复制行为的过程中,引用类型的内容并没有发生复制。图8-6是这个示例的示意图。

8.3 集合的复制 - 图2

图 8-6 字典引用类型内容示意图

8.3.2 数组复制

Xcde 6 beta2中的数组复制的语法非常复杂,beta3之后变得简单了,与字典的复制类似。在为数组赋值或参数传递的时候,数组总是发生复制行为。而数组中的数据是否也发生复制行为,要看数据本身的类型。

下面我们再看一个示例:

  1. var a = ["张三", "李四", "王五"]
  2. var b = a
  3. var c = a
  4. a[0] = "董六"
  5. println(a[0])
  6. println(b[0])
  7. println(c[0])

上述代码第①行是声明并初始化数组a,在代码第②行和第③行将a赋值给bc。这时它们发生了复制行为,然后当我们在第④行修改a的第一个元素后,第⑤行输出的是"董六",而第⑥行和第⑦行输出的是"张三"

图8-7是这个示例的复制示意图。

8.3 集合的复制 - 图3

图 8-7 数组值类型复制示意图

下面我们再看一个示例:

  1. class Employee {
  2. var name : String // 姓名
  3. var salary : Double // 工资
  4. init (n : String) {
  5. name = n
  6. salary = 0
  7. }
  8. }
  9. var emps = Array<Employee>()
  10. let emp1 = Employee(n: "Amy Lee")
  11. let emp2 = Employee(n: "Harry Hacker")
  12. emps.append(emp1)
  13. emps.append(emp2)
  14. //赋值,发生复制
  15. var copyEmps = emps
  16. let copyEmp : Employee! = copyEmps[0]
  17. copyEmp.name = "Gary Cooper"
  18. let emp : Employee! = emps[0]
  19. println(emp.name)

第①行代码声明数组变量,它要求内容数据是Employee类型。第②行和第③行是实例化Employee对象。第④行和第⑤分别将两个对象添加到数组中。

第⑥行代码是将数组变量emps赋值给copyEmps变量,这个过程中发生了复制行为,但是由于emp1emp2对象是引用类型,不发生复制,因此在第⑦行代码修改copyEmp内容之后,在第⑧行打印的时候输出"Gary Cooper"

这个示例说明,emps数组中的emp2copyEmps数组中的copyEmp具有相同的引用。在数组发生复制行为的过程中,引用类型的内容并没有发生复制。图8-8是这个示例的示意图。

8.3 集合的复制 - 图4

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