iOS堆和栈的区别(三)

152 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

Swift中的使用

Swift 中的数据类型分为引用类型(类)和值类型(枚举、结构体)。引用类型存储在 “堆” 上,值类型存储在 “栈” 上。Swift 管理引用类型采用自动引用计数(ARC)的管理方法。值类型是由处理器来管理的,不需要程序员来管理。

image.png

在 Swift 中,典型的有 struct,enum,以及 tuple 都是值类型。而平时使用的Int,Double,Float,String,Array,Dictionary,Set 其实都是用结构体实现的,也是值类型。Swift 中,值类型的赋值为深拷贝(Deep Copy),值语义(Value Semantics)即新对象和源对象是独立的,当改变新对象的属性,源对象不会受到影响,反之同理。

在 Swift 中,class 和闭包是引用类型。引用类型的赋值是浅拷贝(Shallow Copy),引用语义(Reference Semantics)即新对象和源对象的变量名不同,但其引用(指向的内存空间)是一样的,因此当使用新对象操作其内部数据时,源对象的内部数据也会受到影响。

值类型作为参数传入时,函数体内部不能修改其值。引用类型作为参数传入时,函数体内部不能修改其指向的内存地址,但是可以修改其内部的变量值。

值类型的优点是:不变性,值类型的变量是严格的被一个所有者控制的;独立性,引用类型是相互依赖的,是一种隐式的依赖;还有可交换性。

对于面向对象编程,由于实例对象是可变的,导致对象的另一个享有者在合适的时候会去改变这个对象的属性。swift支持类的单继承,导致从多个class继承到更多地功能,增加了复杂度,并且会导致class紧耦合的问题。在多线程情况下,可以同时改变同一个引用。

选择值类型而不是引用类型的一个主要原因是能让你的代码变得更加简单。Swift的核心是面向协议,引用类型有许多的享有者。值类型被赋给一个变量或者常量,传给函数做参数时是它的值被拷贝的。这就让值类型在任何时候只有一个享有者,从而降低复杂度。你在任何情况下用一个值类型,都能够假设你的其他代码不会使它改变,这通常在多线程环境中很有用,如果一个线程中使用的数据被另一个线程给意外的修改了,这通常会产生非常严重的Bug,且相当难以调试。Class = 高复杂度,值 = 低复杂度。而且,swift对值类型的操作上进行了一些优化,因此才有了swift大量使用值类型代替引用类型的说法。

由于只有当你需要修改数据时两者的区别才会得到体现,所以当你的实例不会对数据进行修改的时候,值类型和引用类型看起来是完全相同的。你也许会想,写一个完全不可变的类,通过使用不可变的存储属性,以及避免暴露修改数据的接口,从而在Swift里实现一个不可变的类。事实上,大多数的Cocoa类,比如NSURL等,都被设计为不可变的类,然而,Swift当前并没有提供任何语言机制去强制申明一个类不可改变(比如子类化就能修改一个类的实现),只有结构体和枚举才是强制不可变的。

在Swift里,Array、String和Dictionary都是值类型,他们的行为和C语言中的int类似,每个实例都有自己的数据,你不需要额外做任何事情,比如做一个显式的copy,防止其他代码在你不知情的情况下修改等,更重要的是,你能安全地在线程间传递它,而不需要使用同步技术。在提高安全性的精神下,这个模型将帮助你在Swift中写出更多可预知的代码。

除此之外,Swift和OC还有其他的类型对应,对应关系如下:

image.png

但是,需要关注的是,对于原来OC中的数据的引用类型,swift中并没有真正完全的实现一套数据存储逻辑。只是内部保存了对oc对象的引用,使得swift api访问时行为逻辑和值类型一致。