Swift中的值类型和引用类型

值类型和引用类型

首先,需要了解Swift中的值类型和引用类型。

在Swift中
值类型: 基本数据类型(Int,Double,String,Array,Dictionary)
引用类型: class, block, function

  • 值类型(Value Types):每份实例都保留了一份独有的数据拷贝,一般以结构体(struct)、枚举(enum)或者元组(tuple)的形事出现。
  • 引用类型(Referance Types):每个实例共享同一份数据来源,一般以类(class)的形式出现。

Copy on Write(写时复制)

  • Copy-on-Write是一种用来优化占用内存大的值类型的拷贝操作的机制。
  • 对于Int、String等基本类型的值类型,它们在赋值的时候就会发生拷贝,它们没有Copy-on-Write这一特性(因为Copy-on-Write带来的开销往往比直接复制的开销要大)。
  • 对于Array、Dictionary、Set类型,当它们赋值的时候不会发生拷贝,只有在修改的之后才会发生拷贝,即Copy-on-Write。
  • 在swift中,当你有一个占用内存很大的一个值类型,并且不得不将它赋值给另一个变量或者当做函数的参数传递的时候,拷贝它的值是一个非常消耗内存的操作,因为你不得不拷贝它所有的东西放置在另一块内存中。
  • 为了优化这个问题,Swift对于一些特定的值类型(集合类型:Array、Dictionary、Set)做了一些优化,在对于Array进行拷贝的时候,当传递的值进行改变的时候才会发生真正的拷贝。而对于String、Int等值类型,在赋值的时候就会发生拷贝。

存储方式

  1. 值类型和引用类型在内存上存储的地方不一样。值类型的值是存储在内存的栈当中。引用类型的值是存储在内存的堆中。
  2. 在传递值类型和传递引用类型的时候,传递方式不一样。值类型我们称之为值传递,引用类型我们称之为引用传递。
  3. 引用类型的对象存储在堆中,并且会分配一个内存地址。该内存地址会存储到栈空间,栈空间名为变量名。
    即读取引用类型对象的顺序:变量—>内存地址—>实例对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//打印内存地址
func address(of object: UnsafeRawPointer) -> String {
let addr = Int(bitPattern: object)
return String(format: "%p", addr)
}

var number = 10
var number2 = number
print(address(of: &number), address(of: &number2))
//打印内存地址为 number=0x1199d2030; number2=0x1199d2038

------------
|number(oxa) | ----|
|------------| |
|number2(oxa)| ----|
|------------| |
| | /
|------------| /
| oxa(10) |<--
------------

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person {
var name: String
init(name: String) {
self.name = name
}
}
let p = Person(name: "11")
let p1 = p
p1.name = "22"
print(p.name, p1.name)// 22 22

栈 堆
---------- ----------
| p |---->| Person |
|----------| | |----------|
| p1 |__| | name = 11|
|----------| | |
| | | |
---------- ----------

其中,如果引用类型里面包含值类型,值类型会存在引用类型的存储区中。