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 = n
salary = 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 = n
salary = 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 数组引用类型内容示意图