我们在swift中最常用的集合类型就是数组了。数组,简单来说,就是一列东西。比如我们可以这样创建一个数字的数组:
let fibs = [0, 1, 1, 2, 3, 5]
复制代码
数组有一些常用操作,比如isEmpty
和cout
方法1,分别获取数组的第一个和最后一个元素(除非数组为空)。数组还允许使用下标脚本(subscript),通过指定一个下标直接访问元素。但使用下标脚本是不安全的操作,因为在获取元素之前,你需要确保下标没有越界,否则程序就会崩溃。
如果我们想要修改刚刚定义的数组(比如使用apppend
方法),我们会得到一个编译错误。这是因为通过let
关键字,数组被定义为了常量(constant)。在很多情况下这么做毫无问题,因为它可以防止你偶然间修改到这个数组。我们需要使用var
关键字来让这个数组成为一个变量(variable)。
var mutableFibs = [0, 1, 1, 2, 3, 5]
复制代码
这样就容易在数组后追加一个或一组元素了:
mutableFibs.append(8)
mutableFibs.appendContentsOf([13,21])
复制代码
把var
和let
区别开来有很多好处。被定义为let
的变量不能被改变,因此它更容易理解。当你读到类似于let fibs = ……
的时候,你就知道编译器会保证fibs
的值永远不会变。这极大的帮助我们通读整篇代码。需要注意的是当变量表示一个对象时,它的值是一个指针。用let
来定义一个对象可以保证这个指针所指向的内存地址是不变的,但是它指向的内存中的数据是可能会改变的(也就是说实例变量)2。我们会在第四章中更加深入的理解结构体和类的区别。
Swift中的数组有自己的值语义。具体说来就是数组中的值从来不会共享。当创建一个新的数组变量,然后把旧的数组赋值给它时,其实是把旧的数组变量复制了一份,把数组作为参数传给函数中也是如此。比如在下面的代码段中,x
不会被修改。
var x = [1,2,3]
var y = x
y.append(4)
复制代码
var y = x
这句代码把x
复制了一份,所以在y
的末尾追加一个4
不会对x有任何影响,x
的值依然是[1,2,3]
对比一下NSArray
采用的实现可变性的方法,NSArray
没有变异方法(mutating method),需要用NSMutableArray
来让一个数组变成可变的。
但只是有一个不可变的NSArray
引用并不意味着数组就不能在你眼皮子底下被改变。
let a = NSMutableArray(array: [1,2,3])
// 我不想让b能被改变
let b: NSArray = a
// 但其实b还是可以被改变 —— 通过a
a.insertObject(4, atIndex:3)
// 现在b也含有4了
print(b)
复制代码
正确的做法是在创建b
的时候把a
拷贝一份:
let a = NSMutableArray(array: [1,2,3])
// 我不想让b能被改变
let b: NSArray = a.copy() as! NSArray
a.insertObject(4, atIndex:3)
print(b) // b现在依然还是[1,2,3]
复制代码
在上面的例子中,我们显然需要把a复制一份,因为毕竟a自己是可变的。虽然在方法间传递数组的情况不太常见、但这会导致很多不必要的复制。
在Swift中,我们不需要NSArray
和NSMutableArray
这两种数组了,只需要一个Swift自己的数组便足矣。数组的可变性由var
和let
关键字来决定。与OC不同的是,对某一个数组的引用是不会共享的,也就是当你用let
来定义第二个数组的时候,你可以确保这第二个数组是不会改变的。
确实,经常复制数组会带来性能上的问题,但实际上,Swift标注库里所有的集合类型在实现时都采用了“写时复制(copy-on-write)”技术以确保数据只有在真正需要的时候才会被复制。所以在之前的例子中,x
和y
在y.append(4)
调用之前,其实共享了同一块内部存储区域。在“Mutability”章节我们会深入了解数组的值语义,包括如何为自定义的类型实现“写时复制”技术
#译者注
[1]:我觉得严格来说是isEmpty
和cout
属性的get方法。
[2]:也就说object.attribute = newValue
可以,而object = newObject
不可以。