swift的值类型与inout

1、什么是值类型

值类型与引用类型相对,值类型的数据特征是,每一个内存实例都会有一份数据copy。在swift语言中,用struct定义的类型都是值类型,这包括了基本数据类型(Int,String,Double,Float),集合类型(Array,Dictionary,Set)等。值类型在赋值和传递的过程中,会表现出copy数据的性质。

struct Point {
    var x = 0
    var y = 0
}

var point1 = Point()
var point2 = point1

我们定义两个值类型的变量point1、point2,当我们使用point1对point2进行赋值的时候,理论上编译器会创建point2对象,并将point1的数据进行一次copy赋值给point2。如果我们修改了point2的内容,我们会发现point1的内容并没有发生变化。

point2.x = 1
print(point2.x) // 1
print(point1.x) // 0, 对point2的修改不会影响point1的内容

2、值类型的优缺点

  • 线程的目标是提升程序的运行效率,但是由于数据在线程之间的共享所带来的复杂度,大大的削弱了这种性能的提升带来的好处。甚至在复杂的线程锁机制下,在某些情况下线程带来了性能的提升变得微乎其微。值类型的设计可以有效的降低编程问题的复杂度,编译器保证值类型的数据拥有者都持有一份数据的私有copy,这种数据的私有性可以让数据的的持有者安全的修改自己的数据,而不需要担心其它线程可能会修改这些数据而被迫采用使用线程锁。

  • 因为值类型数据有机会直接在栈空间分配内存,所以可以减少堆上内存分配和回收的次数,这对于要在堆上创建大量的小对象而言,值类型表现出了特有的优势。但是理论上,值类型在赋值和传递的过程中总是需要进行copy会带来一定性能的影响,swift使用了很多优化机制,来减轻copy带了的性能影响。

编译器可以将copy操作延时到不得不进行copy的时候再进行操作(比如常常被提到的“写时拷贝”),更进一步来说,当一个结构体不要求在内存空间中连续存放的话,那么当发生“写时copy”的时候,编译器则完全只处理脏数据的copy,而继续共享相同内容的数据。

  • swift语言的设计者重视值类型,将90%的内容都设计为值类型,而且平等的强调了let与var(在很多语言中默认声明的都是变量,常量需要用特殊的关键字来进行修饰)。swift语言中的let关键字与值类型配合可以让不变性在语义上更加完整。
let array = [1,2,3,4,5]
array.append(6) // error!!!!!!!

如果array是一个引用类型,let的声明只能限制array不可以再指向其它内容,但let无法限制我们在array所指向的对象中新增内容(既在数据中新增内容)。但此时array是一个值类型,swift编译器保证用let修饰过的值类型不可变,从而将不变性的语义更加完整。

3、inout

我们现在已经知道,值类型在传递的过程中会进行数据的copy,那么显而易见的是下面改变x的值是不会成功的

var x = 10
func changeX(x: Int) {
    x + = 10
}

changeX(x)
print(x) // 10 x的值不会发生改变

如果我们希望x的值可以被改变,那么我们必须使用引用的方式来传递x。这时候我们就需要使用inout,当我们使用inout的时候,编译器不论当前struct的存放状态如何,都一定会把结构体放入一个连续的内存空间中,并将地址作为参数进行传递。

var x = 10
func changeX(x: inOut Int) { // swift3.0改变了inOut修饰符的位置
    x + = 10
}

changeX(x)
print(x) // 20 x的值发生了改变

推荐阅读更多精彩内容