Swift中值类型和引用类型之间的区别

介绍:

因此,对于每个新手迅速的程序员来说,了解值类型和引用类型之间的主要区别很重要。 在本文中,我们将讨论有关值类型和引用类型的主要区别。应选择的示例以及定义的时间。

在深入研究之前,我们需要了解一些基本定义,例如..

类型:类型是数据的分类,告诉编译器或解释器如何使用数据。

有一些Swift基本类型:

 Int, Float ,Double,Bool,String,Array 

定义:

值类型 :值类型是直接在内存上创建的类型。每个实例保留唯一的数据副本。在分配或复制时,它会创建一个完整的新数据。

引用类型 :共享单个数据的类型。 初始化一次并分配给变量,触点或函数后,其返回引用。

示例照片:

让我们用类做一些代码:

现在,如果我们将dadCar的值更改为将发生的事情,那么这就是主要问题!

为什么要改变妈妈的颜色:

当我们通过分配momCar实例使dadCor常量时,它仅返回对dadCar的引用,而未创建momCar的完整新副本。由于swift类是引用类型,因此仍引用单个实例。

像这样 :

还要注意的另一件事是dadCar是常量(let),但是我们可以为其分配值,因为引用类型实例是可变的。我们正在为dadCar分配值,并且正在更改其引用的值而不是本身。默认情况下,引用为nil。

与Struct相同的代码:

主要区别:

值类型和引用类型之间的主要区别在于,值类型复制数据,而引用类型共享其数据的单个副本。当我们创建具有值类型的实例时,值类型的均值不变,它创建数据的唯一副本,并且它可以t可以更改,但引用类型是可变的,其值可以更改..

使用值类型时:

  • 将实例数据与==进行比较很有意义
  • 您希望副本具有独立状态
  • 数据将在多个线程的代码中使用。因此,您不必担心数据会从另一个线程更改。

使用引用类型时:

  • 将实例身份与===进行比较比较有意义
  • 您要创建共享的可变状态

如何存储在内存中:

在普通类型系统中:

1.Value Type —获取存储在堆栈存储器中。
2引用类型 —获取存储在托管堆内存中

使用值类型的优点:

1.效率

引用类型实例是在堆上分配的,这比堆栈分配要贵。 为了确保在不再需要引用类型实例时释放已分配的内存,需要保留对每个引用类型实例的所有活动引用的计数,并在没有更多引用引用时对其进行释放。 值类型不受此开销的影响,从而可以有效地创建和复制实例。 值类型的副本被认为是便宜的,因为值类型实例可以在恒定时间内被复制。

Swift将内置的可扩展数据结构(例如String,Array,Dictionary等)实现为值类型。 但是,由于它们的大小在编译时未知,因此无法在堆栈上分配它们。 为了能够有效地使用堆分配并维护值语义,Swift使用了一种称为copy-on-write的优化技术。 这意味着,虽然每个复制的实例在逻辑上都是一个副本,但是仅当复制的实例发生突变时才在堆上进行实际复制。 在此之前,所有逻辑副本将继续指向同一基础实例。 这提供了更好的性能特征,因为制作的副本较少,并且在进行复制时,它涉及固定数量的引用计数操作。 如果需要,此性能优化还可用于自定义值类型。

2.可预测的代码

对于引用类型,保存对实例的引用的代码的任何部分都无法确定该实例包含的内容,因为可以使用任何其他引用对其进行修改。 由于值类型实例是在赋值时复制的,没有隐式数据共享,因此我们无需考虑在代码的一部分中采取的措施会影响其他行为的意外后果。 而且,当我们看到一个声明为包含值类型实例的let常量的变量时,我们可以确定,无论如何定义值类型,都永远不能修改该值。 这为代码的某些部分的行为方式提供了有力的保证和细粒度的控制,使代码更易于推理和更可预测。

有人可能会争辩说,可以编写代码来使得每次将引用类型实例交给新所有者时,都会创建一个副本。 但这将导致大量防御性复制,由于复制引用类型会涉及大量开销,因此效率很低。 如果要复制的引用类型实例具有同样是引用类型实例的属性,并且我们要避免任何隐式数据共享,则每次都必须创建一个深层副本 ,这会使性能特征更加糟糕。 我们还可以尝试通过使所有引用类型不可变来解决共享状态和可变性的问题。 但是,这仍然会涉及许多低效的复制,并且无法更改引用类型的状态将大大破坏使用引用类型的目的。

3.线程安全

值类型实例可以在多线程环境中使用,而不必担心一个线程会改变另一个实例正在使用的实例的状态。 由于没有竞争条件或死锁,因此无需实现同步机制。 因此,使用值类型编写多线程代码变得更加简单,安全和高效。

4.没有内存泄漏

Swift使用自动引用计数并在没有引用类型实例的情况下取消分配引用类型实例。 这解决了正常事件过程中的内存泄漏问题。 但是,在强引用循环中仍然可能存在内存泄漏,在这种情况下,两个类实例相互保持强引用,并防止彼此释放。 当类实例和闭包之间有很强的引用循环时(在Swift中也是引用类型),可能会发生同样的事情。 由于值类型没有引用,因此不会出现内存泄漏的问题。

5.更容易测试

因为引用类型在其生命周期内保持状态,所以单元测试引用类型通常涉及使用模拟框架来观察各种方法调用对被测试对象的状态和行为的影响。 而且,由于引用类型实例的行为可以随状态的改变而改变,因此通常需要设置代码才能使被测对象处于正确的状态。 对于值类型,重要的是属性的值。 因此,我们要做的就是创建一个具有与期望值相同属性的新值,并比较它们是否相等。

有关堆和堆栈的更多信息:

堆栈用于存储静态内存,堆用于动态内存分配,两者均存储在计算机的RAM中。

堆栈由CPU严格管理和优化。 当函数创建变量时,堆栈会存储该变量,并且在函数退出时会被破坏。 在堆栈上分配的变量直接存储到内存中,对该内存的访问非常快。 当一个函数或方法调用另一个函数,依次调用另一个函数等时,所有这些函数的执行将保持挂起状态,直到最后一个函数返回其值为止。 堆栈始终按LIFO顺序保留,最近保留的块始终是要释放的下一个块。 这使得跟踪堆栈真的非常简单,从堆栈中释放一个块只不过是调整一个指针而已。 由于堆栈组织得很好,因此非常高效且快速。

→系统使用堆存储其他对象引用的数据。 堆通常是一个很大的内存池,系统可以从中请求并动态分配内存块。 堆不会像堆栈那样自动销毁其对象。 为此,必须进行外部工作。 ARC在Apple设备中完成这项工作。 ARC跟踪引用计数,当引用计数变为零时,将释放对象。 因此,与堆栈相比,整个过程(分配,跟踪引用和释放)要慢一些。 因此,值类型比引用类型快。

结论:谢谢您和我在一起……别忘了给我鼓掌并与您的朋友分享。