swift 值类型和引用类型1

232 阅读4分钟

「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」。

前言

  • 值类型和引用类型是Swift中的核心概念,了解它们是每位Swift开发人员的基础, 在以后的探索中多次提到值类型和引用类型,所以在这里做个笔记。供以后参考。

内容

    1. 值类型和引用类型的概念
    1. 值类型和引用类型的内存管理
    1. 值类型和引用类型的选择

一 、值类型和引用类型的定义

值类型(Value Type) :即每个实例保持一份数据拷贝。

引用类型(Reference Type) :即所有实例共享一份数据拷贝。

Swift有三种声明类型的方式:classstructenum。它们可以分为值类型(struct和enum)和引用类型(class)。它们在内存中的存储方式不同决定它们之间的区别:

  • 值类型是这样一种类型,当它被赋值给一个变量、常量或者被传递给一个函数的时候,其值会被拷⻉。
  • 实际上,Swift 中所有的基本类型:整数 (integer)、浮点数(floating-point number)、布尔值(boolean)、字符串串(string)、数组 (array)和字典(dictionary),都是值类型,其底层是使用结构体实现的。Swift 中所有的结构体和枚举类型都是值类型。这意味着它们的实例例,以及实例例中所包含的任何 值类型的属性,在代码中传递的时候都会被复制。
  • 与值类型不同,引⽤类型在被赋予到一个变量量、常量或者被传递到一个函数时,其值不会被拷贝。因此,使用的是已存在实例的引⽤,而不是其拷贝。

验证值类型:

import UIKit

struct Point {
    var x: Double
    var y: Double
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let point1 = Point(x: 3, y: 5)
        var point2 = point1
        
        print(point1)           // Point(x: 3.0, y: 5.0)
        print(point2)           // Point(x: 3.0, y: 5.0)
        
        point2.x = 5
        
        print(point1)           // Point(x: 3.0, y: 5.0)
        print(point2)           // Point(x: 3.0, y: 5.0)
        
    }
}

//打印point1 不随着 point2 而变化 。说明他们内存独立
Point(x: 3.0, y: 5.0)
Point(x: 3.0, y: 5.0)
Point(x: 5.0, y: 5.0)

验证引用类型:

import UIKit

class Point {
    var x: Double = 0.0
    var y: Double = 0.0
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let point1 = Point()
        point1.x = 3.0
        point1.y = 5.0
        let point2 = point1
        
        print(point1)
        print(point2)
        
        point2.x = 5
        
        print(point1.x , point1.y)
        print(point2.x , point2.y)
    }
}

//打印point1 随着 point2 而变化 。说明他们公用一块内存
5.0 5.0
5.0 5.0

二、 值类型和引用类型的内存管理

  • 值类型存储在栈区。每个值类型变量都有其自己的数据副本,并且对一个变量的操作不会影响另一个变量。
  • 引用类型存储在其他位置(堆区),我们在内存中有一个指向该位置的引用。引用类型的变量可以指向相同类型的数据。因此,对一个变量进行的操作会影响另一变量所指向的数据

栈区存储临时数据:方法的参数和局部变量。每次我们调用一个方法时,都会在栈上分配一块新的内存。该方法退出时,将释放该内存。除特殊情况(下面会讲),所有Swift值类型都在此处。

堆区存储具有生存期的对象。这些都是Swift引用类型,还有一些值类型的情况。堆和栈朝着彼此增长堆区的分配一般按照地址从小到大进行,而栈区的分配一般按照地址从大到小进行分配。

【堆与栈分配的成本】

栈区内存分配和销毁的工作原理与数据结构中的栈相同。你只能从栈顶压栈或出栈。指向栈顶的指针足以实现这两个操作。因此,栈指针可以腾出空间来分配其他更多的内存。当函数执行完退出时,我们将栈指针增加到调用此方法之前的位置。(为什么增加才能回到调用之前的地址,刚说了栈是从大到小进行分配的)

  • 栈分配和释放的成本相当于整数复制的成本

堆分配过程涉及的东西很多。我们必须搜索堆区以找到适合它大小的空内存块。我们还必须同步堆,因为多个线程可能同时在其中分配内存。为了从堆中释放内存,我们必须将该内存重新插入适当的位置。

  • 堆分配和释放的成本比栈要大得多