值类型
值类型,即每个实例保持一份数据拷贝。Swift 中,值类型的赋值为深拷贝(Deep Copy),值语义(Value Semantics)即新对象和源对象是独立的,当改变新对象的属性,源对象不会受到影响,反之同理。
Swift中所有基本类型:整型(Int),浮点型(float),布尔类型(Boolean),字符串类型(String),数组类型(Array),字典(Dictionary),元组(Tuple)都是值类型,它们底层都是以结构体(Struct)的形式实现的,所有结构体(struct)也是值类型,枚举(Enum)也是值类型。
值引用的优点:不可能存在循环引用!!!(因为它只有一个持有者)
引用类型
引用类型,即所有实例共享一份数据拷贝。与值类型不同,引用类型在分配给变量或者常量,或者传递给函数时,其不会复制。而是对相同现有实例的引用。相当于只是创建一个共享的实例分身,两者公用一套数据。修改一处,另一处也会更改。
Swift中,类(Class)和闭包 属于引用类型。
使用类,我们可以通过继承来共享代码。 结构体、枚举无法继承。正好符合swift的面相协议编程的特性。
写时复制
Swift 中 值类型 有“写时复制(Copy-On-Write)”的特性。
定义:结构体的引用在改变的一瞬间是唯一的,不会有复制发生,内存的改变将在原地进行。
不太好理解的话我们一起看下面这个demo
var x = [1,2,4]
var y = x
x.append(5) //1,2,4,5
y.removeLast() //1,2
这时,把x赋值给y时会发生复制。这时候两个数组的引用
指向的是内存中的同一个位置
。共享存储部分。 当改变x
时这个共享会被检查到。 内存将会被复制出来。 我们就独立的改变了两个变量。 耗性能的元素复制操作只会在必要的时候
发送。这个就叫做写时复制
。
存储位置
在 Swift 中,值类型,存放在栈区;引用类型,存放在堆区。
栈与堆的不同
像前面说的,引用类型实例存在堆中,值类型实例比如结构存在于一个称为栈的内存区域中。如果值类型实例是一个类的一部分,值会和类一起存在堆中。
栈被用于静态存储分配,堆用于动态存储分配,它们都存在计算机的RAM中。
栈被CPU紧密管理并优化,当一个函数创建一个变量,栈会存储这个变量,并在函数退出时候被毁掉。被分配到栈的变量直接存储在内存上,访问这段内存非常快。当一个函数或者方法调用另一个函数,另一个函数再依次调用其他函数等等,直到最后一个函数返回它的值之前,其他所有函数都会保持暂停执行.
栈总是按照LIFO顺序保留,最新保留的区块总是会下一个释放。这使得跟踪记录栈非常简单,释放一个栈上的区块不过是调整一个指针。因为栈非常组织有序,所以它快捷高效。
系统使用堆存储被其他对象引用的数据,堆是一大片内存,系统可以从中请求并动态分配内存区块。堆并不会像栈一样自动毁掉它的对象,需要外部工作来处理这些。在苹果设备中ARC就做这个工作。引用数量会被ARC追踪,当它变为0时对象会被释放。因此整个过程(分配,追踪引用,释放)会比栈要慢。所以值类型要快于引用类型。