Swift中类和结构体非常类似,都具有定义和使用属性、方法、下标和构造器等面向对象特性,但结构体不具有继承性,也不具备运行时强制类型转换、使用析构器和使用引用计数等能力。
Swift中struct是值类型,而class是引用类型。值类型的变量直接包含他们的数据,引用类型的变量存储对他们的数据引用,因此后者称为对象,因此对一个变量操作可能影响另一个变量所有引用的对象。对于值类型都有自己的数据副本,因此对一个变量操作不可能影响另一个变量。
1,类和结构体的定义和实例化对比
定义
// 定义类
class 类名 {
定义类的成员
}
// 建立一个 class 名称为 ClassCoder
class ClassCoder {
var name = "IAMCJ"
var age = 0
}
// 定义结构体
struct 结构体名 {
定义结构体的成员
}
// 建立一个 struct 名称为 StructCoder
struct StructCoder {
var name = "IAMCJ"
var age = 0
}
实例化
// 类实例化
let classCoder = ClassCoder()
// class 不能直接用 ClassCoder(name:"CJ",age:18) 必需要自己创建构造函数才可以
classCoder.name = "CJ"
classCoder.age = 18
// 结构体实例化
var structCoder = StructCoder(name:"CJ",age:18)
// 另外一种实例化方法
var structCoder = StructCoder()
structCoder.name = "CJ"
structCoder.age = 18
class在实例化的时候不能自动把property放在constructor的参数里,必须创建自己的构造函数才可以。
2,mutating关键字:
//在不修改原 class 和 struct 的情况下添加一个 method:modifyCoderName(newName:)
// 类
class ClassCoder {
var name = "IAMCJ"
var age = 0
}
extension ClassCoder {
func modifyCoderName(newName:String) {
self.name = newName
}
}
// 结构体
struct StructCoder {
var name = "IAMCJ"
var age = 0
}
extension StructCoder {
mutating func modifyCoderName(newName:String) {
self.name = newName
}
}
struct在func里面需要修改property的时候需要加上mutating关键字,而class就不用。
3,class可以继承,而struct不可以。
4,内存分配:struct内存是分配在栈上,class内存分配在堆上。
栈内存的存储结构比较简单,可以简单理解为push到栈底pop出来,而要做的就是通过移动栈针来分配和销毁内存。
堆内存相比栈有着更为负责的存储结构,它的分配方式可以理解为在堆中寻找合适大小的空闲内存块来分配内存,把内存块重新插入堆类销毁内存,当然这些仅仅是堆内存想必栈内存性能消耗大的一个方面,更重要的是堆内存支持多线程操作,响应的就要通过同步等方式保证线程的安全。
struct Point {
var x, y: Double
func draw() { ... }
}
let point1 = Point(x: 0, y: 0)
var point2 = point1
point2.x = 5
首先在代码执行之前编译器会在栈上分配一块4个word大小的内存,分配的过程就是我在上面提到的移动栈针
用(0,0)来初始化point1 并把point1赋值给point2,你可以理解为将value拷贝到分配好的栈内存上,因为struct是值类型所以point1和point2是两个独立的instance
修改point2.x不会影响point1
最后use point1 use point2离开作用域 移动栈针,销毁栈内存
接下来我们看一下class的情况
class Point {
var x, y: Double
func draw() { ... }
}
let point1 = Point(x: 0, y: 0)
let point2 = point1
point2.x = 5
首先在代码执行之前编译器会在栈上分配两个word大小的内存,不过这一次不是用来存储Point的property
代码执行初始化point1在堆上寻找合适大小的内存块分配
然后将value拷贝到堆内存存储,并在栈上存储一个指向这块堆内存的指针
可以看到堆上分配的是4个word大小的内存块,剩余的两个蓝色格子存放的是关于class生命周期相关函数表的指针let point2 = point1
执行的是引用语言,只是在point2的栈内存存储了一个指向point1堆内存的指针
修改point2.x也会影响到point1
最后use point1 use point2 离开作用域,堆内存销毁(查找并把内存块重新插入到栈内存中),栈内存销毁(移动栈针)
当然栈内存在分配的过程中还要保证线程安全(这也是一笔很大的开销)
5,引用计数:struct均为栈内存分配,不涉及任何引用计数;
6,方法派发:
- 静态派发:编译器讲函数地址直接编码在汇编中,调用的时候根据地址直接跳转到实现,编译器可以进行内联等优化,Struct都是静态派发。
- 动态派发:运行时查找函数表,找到后再跳转到实现,动态派发仅仅多一个查表环节并不是他慢的原因,真正的原因是它阻止了编译器可以进行的内联等优化手段
参考文章:
https://www.jianshu.com/p/596864f2c672
https://www.jianshu.com/p/394e4fe614a0