Swift 中Class和Struct异同
Swift 中类和结构体有很多共同点。共同处在于:
- 定义属性用于存储值
- 定义方法用于提供功能
- 定义下标操作使得可以通过下标语法来访问实例所包含的值
- 定义构造器用于生成初始化值
- 通过扩展以增加默认实现的功能
- 实现协议以提供某种标准功能
与结构体相比,类还有如下的附加功能:
- 继承允许一个类继承另一个类的特征
- 类型转换允许在运行时检查和解释一个类实例的类型
- 析构器允许一个类实例释放任何其所被分配的资源
- 引用计数允许对一个类的多次引用
Tip: 类的对象是引用类型,而结构体是值类型。所以类的赋值是传递引用,结构体则是Copy传值,不是使用引用计数。
类为支持的额外功能会增加其复杂性。一般,更倾向使用选择结构和枚举,因为他们更容易理解,而类,则当再在合适和必要的时候使用。实际上,这意味着大多数的自定义数据类型定义为结构和枚举就可以了。
值类型、引用类型 最基本的定义:
值类型每个实例都拥有其数据的一份副本。当被赋值给一个变量或常量,或传递给一个函数时候,它会建立一份新的副本。
引用类型所有实例共享一个数据副本。当被赋值给一个变量或常量,或传递给一个函数时候,一个引用类型一旦被初始化,会返回一个指向已存在实例的引用。
比较项 | struct | class |
---|---|---|
类型 | 值类型 | 引用类型 |
属性初始化 | 可用默认构造直接初始化 | 需要自己创建构造方法 |
变量赋值 | 深拷贝 | 浅拷贝,增加原对象引用 |
方法中修改属性 | 需要添加mutating | 不需要 |
继承关系 | 不能继承 | 可以继承 |
内存 | 栈上,自动内存管理 | 堆上,手动内存管理 |
速度 | 高效 | 相比效率低 |
线程安全 | 自动线程安全的 | 大多是非线程安全的 |
与oc混编 | 不支持,oc无法调struct | 支持混编 |
序列化 | 不支持,可用字节转NSData | 支持序列化 |
Class优势 Struct
- 混合开发中,OC无法调用swift的struct,因为oc调用swift代码,对象必须继承nsobject
- struct不能相互继承,
- struct不能被序列化成NSdata对象,所以不能存入NSUserDefaults,所以需要数据序列化,储存最好用class实现
struct的优点
- 安全性:Struct是值类型传递,没有引用计数
- 内存:由于他没有引用计数,不会因为循环引动导致内存泄露
- 速度:Struct 值类型通常是以栈分配,不是堆,所以Struct比class快的多
- 拷贝:当你拷贝一个对象时不需要知道是深拷贝,浅拷贝
- 线程安全:无论从那个线程访问Struct,都简单
总结:
各有优缺点,如果模型较小,无需继承和存储到NSUserDefaults,或无需oc使用时,可以用Struct。
struct可以保证代码更加安全可靠,以及struct+protocol更加切合swift面向协议编程的初衷
二、struct并不是swift中唯一的值类型,class也不是唯一的引用类型。下面是一些例子:
值类型 | 引用类型 |
---|---|
Int | Function |
Double | Closures |
String | NSString |
Array | NSArray |
Dictionary | NSDictionary |
Set | NSSet |
Struct | Class |
Enum | NS 继承NSObject相关数据类型 |
Tuple |
备注:Swift把一个引用类型看成一个类,这和Objective-C中很像。Objective-C中一切继承于NSObject都被按照引用类型存储
三、我们什么时候选择值类型而不用引用类型?
以下时候使用值类型:
想要用==比较实例数据。一个双等号(==)用于比较值。
你想复制来建立独立数据。
数据要在多线程的代码中使用,那么你就不用担心数据会被其他线程改变。
以下时候使用引用类型(比如一个类):
想要用===比较实例一致性。===会检查两个对象是否完全一致,包括存储数据的内存地址。
你想要创建用于共享,可改变的数据。
引用类型和值类型在内存中怎么存储?
- 值类型-在栈内存中存储
- 引用类型-在托管堆内存中存储
栈与堆的不同!
像前面说的,引用类型实例存在堆中,值类型实例比如结构存在于一个称为栈的内存区域中。如果值类型实例是一个类的一部分,值会和类一起存在堆中。
栈被用于静态存储分配,栈用于动态存储分配,它们都存在计算机的RAM中。
栈被CPU紧密管理并优化,当一个函数创建一个变量,栈会存储这个变量,并在函数退出时候被毁掉。被分配到栈的变量直接存储在内存上,访问这段内存非常快。当一个函数或者方法调用另一个函数,另一个函数再依次调用其他函数等等,直到最后一个函数返回它的值之前,其他所有函数都会保持暂停执行。
栈总是按照LIFO顺序保留,最新保留的区块总是会下一个释放。这使得跟踪记录栈非常简单,释放一个栈上的区块不过是调整一个指针。因为栈非常组织有序,所以它快捷高效。
系统使用堆存储被其他对象引用的数据,堆是一大片内存,系统可以从中请求并动态分配内存区块。堆并不会像栈一样自动毁掉它的对象,需要外部工作来处理这些。在苹果设备中ARC就做这个工作。引用数量会被ARC追踪,当它变为0时对象会被释放。因此整个过程(分配,追踪引用,释放)会比栈要慢。所以值类型要快于引用类型。