iOS Swift5:浅析结构体(struct)与类(class)

一、前言

关于 struct 与 class,相信大家或多或少有些了解,本篇的目的是让大家完全透彻的熟悉,不在是片面了解。

二、基础

2.1、起源

大家学过 C 语言,也应该学过 C++,先帮大家从底层先梳理一下基础知识:

  • struct 与 class 的出现
    • C 语言中是有 struct 结构体的,但是 C 语言是没有 class 的;
    • C++,扩展了 struct,并有了 class 的出现;
  • struct 与 class 的定义
    • C 语言中,struct 只是数据结构,没有函数;
    • C++ 中,struct 与 class 类似,可以有变量,可以有函数;

我们不谈 C 语言,因为它没有 class,struct 也功能单一。

2.2、C++ 中两者的区别

C++ 中 struct 与 class 的区别:

  • 成员:class 中,成员默认是 private 的,而 struct 中,成员默认是 public 的;
  • 继承:class 默认是 private 继承,而 struct 默认是 public 继承;
  • 模板:class 可以使用,而 struct 不能;

2.2.1、继承

关于继承,可能大家都忘记了『基类与子类在继承时,属性的限制』:

public、protected、private 指定继承方式
不同的继承方式会影响基类成员在派生类中的访问权限。

  1. public继承方式
    基类中所有 public 成员在派生类中为 public 属性;
    基类中所有 protected 成员在派生类中为 protected 属性;
    基类中所有 private 成员在派生类中不能使用。

  2. protected继承方式
    基类中的所有 public 成员在派生类中为 protected 属性;
    基类中的所有 protected 成员在派生类中为 protected 属性;
    基类中的所有 private 成员在派生类中不能使用。

  3. private继承方式
    基类中的所有 public 成员在派生类中均为 private 属性;
    基类中的所有 protected 成员在派生类中均为 private 属性;
    基类中的所有 private 成员在派生类中不能使用。

综上,我们可以得出结论:

  • 基类成员在派生类中的访问权限不得高于继承方式中指定的权限;
  • 不管继承方式如何,基类中的 private 成员在派生类中始终不能使用;
  • 如果希望基类的成员能够被派生类继承并且毫无障碍地使用,那么这些成员只能声明为 public 或 protected;
  • 如果希望基类的成员既不向外暴露(不能通过对象访问),还能在派生类中使用,那么只能声明为 protected;

我们用图来方便大家记忆:

inherience.png

2.2.2、模板(template)

说起模板可能大家有点生疏,但我换个词大家就能理解:函数重载!

何谓函数重载?
函数重载针对的是形参(即入参),可以有多个同名函数,但只要满足以下条件中的一个就行:

  1. 函数的参数个数不同;
  2. 函数的参数类型不同;
  3. 函数的不同参数类型顺序不同;

例如我有如下方法:

//交换 int 变量的值
void swap(int *a, int *b){
    int temp = *a;
    *a = *b;
    *b = temp;
}

//交换 float 变量的值
void swap(float *a, float *b){
    float temp = *a;
    *a = *b;
    *b = temp;
}

如果用模板来实现如下:

template void Swap(T *a, T *b){
    T temp = *a;
    *a = *b;
    *b = temp;
}

template是声明模板关键字,typename是声明类型关键字(Java中泛型);

2.3、类型区别

  • struct 是值类型(可以简单类比为基本类型),因此,是值拷贝传递,数据的改变不会相互影响;
  • class 是引用类型(可以理解为指针),也叫对象,因此,是引用传递(指针传递),数据的改为会相互影响;

最简单的理解:

  • 内存地址是一致还是发生变化(struct 是值传递,所以是新的变量;而 class 是引用传递,内存地址不变);
  • 内存地址的引用计数是否发生变化(struct 没有引用计数;而 class 会随着引用传递计数不断增加);

三、Swift 中的两者浅析

无论是 OC 还是 Swift,它们的底层都是 C++,所以,struct 与 class 在 C++ 层面的特性是不会发生变化的,我们主要讨论的是两者在 Swift 层面的相同点与不同点。

3.1、相同点

  • 定义属性用于存储值
  • 定义方法用于提供功能
  • 定义下标操作用于通过下标语法访问它们的值
  • 定义构造器(init)用于设置初始值
  • 支持扩展(extension)以增加默认实现之外的功能
  • 遵循协议(protocol)以提供某种标准功能

3.2、不同点

  • 继承允许一个类继承另一个类的特征
  • 类型转换允许在运行时检查和解释一个类实例的类型
  • 析构器允许一个类实例释放任何其所被分配的资源
  • 引用计数允许对一个类的多次引用

以上主要是针对 class 来说的,struct 是没有这些的。

四、总结

Swift 之所有增加了 struct,主要有以下几点考虑:

  • 安全性:值拷贝传递,没有引用计数;
  • 内存:没有引用也就不会因为循环引用导致内存泄漏;
  • 速度:值类型是直接在栈上分配,而不是在堆上,所以比 class 要快很多;
  • 拷贝:值类型是深拷贝,class 默认浅拷贝;
  • 线程安全:自动线程安全的(无论哪个线程去访问都非常简单);


关于速度话题的延伸:

  • “堆”和“栈”并不是数据结构上的Heap跟Stack,而是程序运行中的不同内存空间;
  • 栈是程序启动的时候,系统事先分配的,使用过程中,系统不干预;
  • 堆是用的时候才向系统申请的,用完了需要交还,这个申请和交还的过程开销相对就比较大了;
  • 栈是编译时分配空间,而堆是动态分配(运行时分配空间),所以栈的速度快;

从两方面来考虑:

  • 分配和释放:堆在分配和释放时都要调用函数(MALLOC,FREE),比如分配时会到堆空间去寻找足够大小的空间(因为多次分配释放后会造成空洞),这些都会花费一定的时间,而栈却不需要这些;
  • 访问时间:访问堆的一个具体单元,需要两次访问内存,第一次得取得指针,第二次才是真正得数据,而栈只需访问一次;

关于速度,这里有一个早期 stackoverflow 的帖子:
http://stackoverflow.com/a/24243626/596821

速度的测试用例(Github):
https://github.com/knguyen2708/StructVsClassPerformance


苹果官方也给出了两者如何选择使用:
https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes

说白了就是:

  • 不考虑应用的状态,也不存在对外的数据交互,就用 struct;
  • 需要在多个地方修改 / 更新数据(共享数据),就用 class;
  • 涉及到继承就用 class,没有就用 struct;
  • 官方建议用 struct + protocol 来建模继承与共享;

你可能感兴趣的:(iOS Swift5:浅析结构体(struct)与类(class))