在Swift上,请参阅Apple文档的ClassesAndStructures:类将称为“引用类型”,而Struct将是“值类型”: – 类是引用类型:与值类型不同,将引用类型分配给变量或常量或将它们传递给函数时, 不会复制引用类型 。 而不是副本,而是使用对相同现有实例的引用。 – 结构和枚举是值类型: 值类型是一种类型,其值在分配给变量或常量或传递给函数时将被复制 。 因此,让我们尝试下面的示例来了解什么是引用类型,值类型: class HumanClass { var name: String init(name: String) { self.name = name } } var classyHuman = HumanClass(name: “Bob”) classyHuman.name // “Bob” var newClassyHuman = classyHuman // Created a “copied” object new ClassyHuman.name = “Bobby” classyHuman.name // “Bobby” 当我将newClassHuman的name属性更改为“ Bobby”时,原始对象的name属性,classyHuman也更改为“ Bobby”。 那么Struct怎么样,让我们看下面的例子: struct HumanStruct […]
介绍: 因此,对于每个新手迅速的程序员来说,了解值类型和引用类型之间的主要区别很重要。 在本文中,我们将讨论有关值类型和引用类型的主要区别。应选择的示例以及定义的时间。 在深入研究之前,我们需要了解一些基本定义,例如.. 类型:类型是数据的分类,告诉编译器或解释器如何使用数据。 有一些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的优化技术。 这意味着,虽然每个复制的实例在逻辑上都是一个副本,但是仅当复制的实例发生突变时才在堆上进行实际复制。 在此之前,所有逻辑副本将继续指向同一基础实例。 这提供了更好的性能特征,因为制作的副本较少,并且在进行复制时,它涉及固定数量的引用计数操作。 如果需要,此性能优化还可用于自定义值类型。 […]
之前,我们讨论过变量,常量和类型。 开始Swift编程第1部分-变量,常量和类型 我知道那里有很多教程,Apple有很好的学习Swift的资源,包括他们的WWDC视频…… medium.com 在这一部分中,我们将讨论几个特殊类型,但是为了深入实现这一点,我们必须首先讨论值类型,引用类型和指针。 提醒一句,指针可能是最难掌握的概念之一,我会尽力而为。 初步的东西 是的,我们上次这样做,现在我们将再次进行。 上一次我们讨论了内存以及它如何以块,字节和位的形式排列。 我想在这里对此进行扩展,并为您提供一些有关内存工作原理的直观表示。 让我们创建一个包含值Hello的字符串。 我们之前没有做过什么特别的事情。 我在上一篇文章中告诉您,内存具有价值。 如果我们可以看一下该内存,它将像这样存储: 我敢打赌,您要问的是\0在字符串的末尾。 这称为空终止符。 斜线表示程序已准备好执行命令,零表示没有任何意义。 这是字符串值的存储方式,并在字符串允许使用空格的同时让程序知道字符串何时完成。 数字略有不同。 数字存储为二进制数字。 二进制很容易理解,从内存块最右边的1开始,向左移动,每次将当前值乘以2。 如果您对图形卡或记忆棒有所了解,这就是为什么我们会看到8位(Atari,NES),16位(Sega Genesis,SNES),32位(PlayStation),64位(Nintendo 64)和以此类推。 在二进制中,如果位置为0,则表示关闭,这是错误的。 如果位置为1,则为开或为true。 基于此逻辑,我们只需要计算位置为1的位置即可。 您能找出上图中的数字吗? 现在,这是一组8位内存,是的,它是一个完整的内存字节。 之前我说过字符串中的每个字符都是2个字节,这意味着我们使用16位来存储字符。 16位二进制文件的最大值为256。但是,当我们谈论位置时,我们总是从0开始。因此16位二进制数的范围是0-255 。 我敢打赌,您在问我们如何才能使字符超出此范围。 好吧,在这种情况下,我将带您到ASCII表,您可以在其中查看自己的值。 您甚至还会看到隐藏的字符,例如空终止符/0如上所示。 内存将值存储在静态内存中,该位置称为stack ,而该位置称为堆。 可以快速访问静态内存和堆栈内存,但是从堆访问内容的速度很慢(以计算机时间计)。 尽管对您来说似乎并不慢,但从堆栈加载可能要花费一毫秒或更短的时间,而从堆加载可能要花费10毫秒。 在介绍事物时,我将解释它们的实现位置,以便您以后知道将其放置在何处。 值类型 值类型很容易学习,这就是我们自然地思考事物的方式。 当您描述某些事物时,例如说一个高尔夫球,您可能会说一个高尔夫球很小,或者一个篮球是橙色。 值类型是包含您要查找的值的内存。 如果您将值8存储在内存中,然后再要求输入该值,则会收到数字8,非常简单,对吧? 字符串,整数,双精度型,浮点型和布尔型都是值类型,还有更多,但是我们还没有涉及它们,到目前为止,我所有的例子都是值类型。 值类型存储在堆栈中。 如果在它们前面添加静态元素,那么它们会添加到静态内存中,而不仅仅是在各处都这样做,这会带来另一个问题,我将在后面讨论。 参考类型和指针 引用类型在某种程度上类似于值类型,因为它们可以为您提供与值类型相同的值,但它们可以为您提供更多的价值。 引用类型使用指针为您提供所需的值。 之所以要准备这么长的篇幅,主要是因为本节有保证。 如果您看一下C和Objective-C程序,您会发现到处都使用了指针,看起来像这样 集合类型 继续本部分的最后一部分。 […]
让我们快速深入了解struct struct是一种值类型 ,当将其值分配给变量或常量或将其传递给函数时,其值将被复制 。 结构必须在创建结构实例时将所有存储的属性都设置为初始值。 您可以在初始化程序中或通过分配默认值来为存储的属性设置初始值。 让我们为ecommerse网站的产品创建一个结构。 当我们复制struct时,它复制所有值类型,但它共享引用类型的引用。 如果您在上面的对象图中看到,ProductA和productB之间共享ProductFactory 结构类型的成员初始化器 所有结构都有一个自动生成的成员初始化器,您可以使用该初始化器来初始化新结构实例的成员属性。 可以通过如下名称将新实例的属性的初始值传递给成员初始化程序: 产品(标识号:1234,名称:“ Remote car”,库存:true) 产品又有一个工厂。 由于我们已经在声明时初始化了此属性,因此无需传入initializer 。 可选属性类型 可选类型的属性会自动初始化为nil值。 关于写时复制的不错的文章 内存分配: 结构将始终在堆栈中为所有值类型分配内存。 堆栈是一个简单的数据结构,具有两个操作,即Push和Pop 。 您可以通过按住指向堆栈末尾的指针来推动堆栈末尾并弹出堆栈末尾。 所有引用类型都将存储在堆中。 堆使您可以分配具有动态生命周期的内存。 它必须搜索未使用的内存块进行分配,然后重新插入内存块以进行重新分配。 因此,如果结构具有引用类型,它将具有指向该类型的指针。 结论: 我试图在此处添加有关结构值类型的所有信息,这对于我们作为开发人员来说是必需的。
在Swift中,我们有引用类型(类)和值类型(结构,元组,枚举)。 值类型具有复制语义。 这意味着,如果您为变量分配值类型或将其作为参数传递给函数(除非它是inout参数),则将复制该值的基础数据。 您将拥有两个具有相同内容的值,但是分配在两个不同的内存地址中。 有关Apple博客上引用类型和值类型之间差异的更详细说明。 由于我们将要讨论写时复制,因此了解Swift值语义非常重要。 所以……开始吧 什么是写时复制? 在Swift中,当您具有较大的值类型并且必须将参数分配或作为参数传递给函数时,就性能而言,将其复制可能会非常昂贵,因为您必须将所有基础数据复制到内存中的另一个位置。 为了将问题最小化,Swift Standard库为某些值类型(例如Array)实现了这套机制,其中值仅在发生突变时才复制,即使在该值具有多个引用的情况下也才复制,因为如果该值是唯一引用的,不需要复制,可以在引用上进行更改。 因此,仅分配给变量或将Array传递给函数并不一定意味着它将被复制,并且确实可以提高性能。 真正重要的是要知道的是… 写时复制不是值类型的默认行为,它是在Swift标准库中针对某些类型(例如数组和集合)实施的。 因此,这意味着并非标准库中的每个值类型都具有此行为。 此外,除非您自己实现它,否则您创建的值类型没有它。 这是我将在下一节稍后讨论的内容。 让我们在实践中看一个例子: 这就是本文的全部,希望您喜欢🙂 如果您有任何意见或疑问,请告诉我。 我很高兴收到您的反馈feedback 您可以在Twitter上@ LucianoPassos11找到我。 感谢您阅读🙂
在我们的应用程序中,当我们需要共享状态时,我们可以使用引用类型。 例如,视图,视图控制器和模型控制器需要是引用类型。 但是,我们应用的模型必须是值类型,以便我们可以传递本地副本。 否则,您可能会遇到更改模型状态的问题,并在应用程序的其他位置产生副作用。 因为您的模型类型也是传递最多的模型类型,所以当它们是引用类型时,可能会对内存管理产生更大的影响。 Swift的问题在于,由于存在内存管理问题,它不允许使用递归值类型。 值类型分配在堆栈上,而不分配在堆上。 它们的大小必须固定,因为操作系统需要知道将它们放入堆栈的大小。 将它们放在堆栈上之后,它们的大小将无法增长,因为其他数据将被置于它们之上。 如果结构包含自身,则系统无法计算其大小。 这就是为什么Swift不允许递归值类型,并且您不能编写这样的代码的原因: 结构TypeA { 让其他:TypeA } 或这个: 结构TypeA { 让其他:B型 } 结构B型{ 让其他:TypeA } 有两种解决此问题的方法: 集合提供足够的间接性以允许递归引用 只有一个参考时使用协议 假设我们有两个结构。 一个用于用户 ,一个用于博客 。 User结构具有一个属性,该属性包含该用户的所有已发布博客,该属性包含该用户的所有关注者,以及一个属性lastBlog(用于最近发布的博客)。 Blog结构具有此Blog作者的属性,类型为User 。 我们可以通过为其中一个用户创建协议来解决User结构的LatestBlog属性和Blog结构的author属性的递归引用问题。 在我们的Blog结构中。 属性博客和User结构的关注者的收集类型提供了足够的间接方式来规避此问题: struct用户{ 命名:字符串 var博客:[Blog] var个关注者:[User] var LatestBlog:BlogType? } 协议 BlogType {} struct Blog:BlogType { 让日期:日期 让内容:字符串 让作者:用户 }